SQL:如果存在,则限制用户。如果不存在,则显示所有内容

Posted

技术标签:

【中文标题】SQL:如果存在,则限制用户。如果不存在,则显示所有内容【英文标题】:SQL: If exists, limit user. If not exists show everything 【发布时间】:2017-07-07 14:57:30 【问题描述】:

我正在尝试找到满足此要求的最佳方法:

@fkStaffID INT = 当前用户。

如果@fkStaffID 获得资源 BLABLA 仅显示表 X 的行,其中 StaffID 在这里。如果他没有资源 BLABLA,请显示所有内容。

抱歉,出于雇主的安全政策,我无法粘贴完整的 SQL。 (我希望我能提供足够的帮助,而不是太多的安全......)

我做什么:

SELECT * FROM X
WHERE ((EXISTS
(SELECT 1 FROM STAFF WHERE pkStaff=@fkStaffID
AND STAFF.PkStaff IN (SELECT fkStaff FROM SECURITYSUBQUERY WHERE ResourceName='BLABLA')) AND X.fkStaff=@fkStaffID)
OR ((NOT EXISTS (SELECT 1 FROM STAFF WHERE pkStaff=@fkStaffID
AND STAFF.PkStaff IN (SELECT fkStaff FROM SECURITYSUBQUERY WHERE ResourceName='BLABLA')) )

问题:真的很慢。我可以做一个更有效的方法吗?我可以用另一种方式吗?感谢您的帮助!

【问题讨论】:

为什么不用IF-ELSE 来分解它? 我不知道该怎么做,你能举个例子吗? 什么版本的 SQL?如果 2016+,因为这听起来像是一个相当注重安全的冒险,你最好的选择可能是使用行级安全。 docs.microsoft.com/en-us/sql/relational-databases/security/… 【参考方案1】:

我认为您应该能够这样写查询:

SELECT * FROM x
WHERE @fkStaffID NOT IN (SELECT fkStaff FROM SecuritySubquery WHERE ResourceName= 'BLABLA')
   OR @fkStaffID = fkStaff;

所以@fkStaffID 不是'BLABLA' 或者它与记录的员工 ID 匹配。

这个NOT IN / OR 仍然不会很快。无论如何,你应该有以下索引:

CREATE INDEX idx1 ON SecuritySubquery (ResourceName, fkStaff);
CREATE INDEX idx2 ON x (fkStaff);

【讨论】:

【参考方案2】:

我会试试这个:

if exists(select 1 from staff where pkstaff=@fkstaffid)
    begin
        select * from X where ResourceName = 'Blabla' and fkStaff = @fkStaffId
    end
else
    begin
        select * from X where ResourceName = 'Blabla'
    end

如果我们有匹配的记录,那么我们会通过 @fkStaffId 过滤,否则我们会选择所有内容。

【讨论】:

是的,它可以工作......但我唯一不喜欢的是我必须在两个地方复制代码......在某些情况下,这段代码(来自我的代码中的 x)是一个巨大的选择:老实说,我无法复制该代码。如果我可以在所有现有查询中添加 WHERE 条件,我会更开心。仍然感谢您提供有效的解决方案!【参考方案3】:

以下查询将只为您提供 X 中人员的数据,这些人员在 STAFF 表中以及 SECURITYSUBQUERY 表中的相应记录('BlaBla' 记录)。

首先,构建测试数据。

IF OBJECT_ID(N'tempdb..#x') IS NOT NULL
     DROP TABLE #x

CREATE TABLE #X ( fkStaff int, myStuff varchar(20) )
INSERT INTO #X ( fkStaff, myStuff )
VALUES 
      (1,'not me')
    , (2,'not me')
    , (3,'show me')
    , (4,'not me')
    , (5,'show me too')

IF OBJECT_ID(N'tempdb..#STAFF') IS NOT NULL
     DROP TABLE #STAFF

CREATE TABLE #STAFF ( pkStaff int, name varchar(20) )
INSERT INTO #STAFF ( pkStaff, name )
VALUES 
      (1, 'Joe')
    , (2, 'Jim')
    , (3, 'Bill')
    , (4, 'Ted')
    , (5, 'Rufus')

IF OBJECT_ID(N'tempdb..#SECURITYSUBQUERY') IS NOT NULL
     DROP TABLE #SECURITYSUBQUERY

CREATE TABLE #SECURITYSUBQUERY ( fkStaff int, ResourceName varchar(20) )
INSERT INTO #SECURITYSUBQUERY ( fkStaff, ResourceName )
VALUES 
      ( 1, 'NotAuth' )
    , ( 2, 'NotAuth' )
    , ( 3, 'BlaBla' )
    , ( 3, 'Extra Perm' )
    , ( 4, 'NotAuth' )
    , ( 5, 'BlaBla' )

现在进行查询。

DECLARE @fkStaffID int ; /* Only 3 or 5 will return records. */

SELECT x.* 
FROM #x x 
LEFT OUTER JOIN (
    SELECT s.pkStaff
    FROM #STAFF s
    INNER JOIN #SECURITYSUBQUERY ss ON s.pkStaff = ss.fkStaff
        AND ss.ResourceName = 'BlaBla'
    WHERE s.pkStaff = @fkStaffID
) t ON t.pkStaff = x.fkStaff
WHERE t.pkStaff IS NOT NULL
    AND x.fkStaff = @fkStaffID

这只会在用户 Bill 或 Rufus 登录(并作为 @fkStaffID 传递)时给出结果。

我不知道这将如何扩展,但优化器应该比 EXISTS 或 NOT IN 子查询运行得更快。用你的数据试试吧。

【讨论】:

刚刚在#x 中使用 250 万行进行了快速测试。查询运行时间不到 1 秒,所以这个操作应该很快。使用良好的索引甚至更快。 修改了测试数据,在#x 中包含用户 3 的大约 640K 阳性结果,并且查询在大约 5 秒内运行。

以上是关于SQL:如果存在,则限制用户。如果不存在,则显示所有内容的主要内容,如果未能解决你的问题,请参考以下文章

如果不存在,则查找值显示为零 SQL

sql - 如果不存在则插入

如果存在,则 T-SQL 删除类型 [重复]

如果所有记录都存在于 sql server 的另一个表中,则返回行列表

t-sql 如果记录存在则选择一列,如果不存在则选择不同的列

如果列不存在,则无法添加?