【问题标题】:Conditional filtering with join使用连接进行条件过滤
【发布时间】:2016-07-14 11:25:53
【问题描述】:

在具有单个参数的存储过程中,我有一个类似于以下性质的查询:

SELECT
    ID,
    DepartmentID,
    FileName
FROM
    Document
-- conditional join from here
JOIN
    AllowedDepartmentList ON DepartmentID = AllowedDepartmentList.ID 
                          AND @IsAdmin = 'false'

参数为@IsAdmin,数据类型位。

我使用的两个表是 Document 表(请参阅上面查询中的结构)和包含单个 int 列的 AllowedDepartmentList

我使用这个查询来过滤带有连接的Document 表的返回结果。我不使用 WHERE DepartmentID IN() 子句,因为 AllowedDepartmentList 可以长达 600-700 个项目(对于 IN() 来说太多了,无法在潜在的 1M 记录表中以良好的性能处理) 所以我使用连接进行过滤,但只有在@IsAdmin 参数为false 时才应执行过滤。就像 --conditional join from here 注释之后的行一样。

我尝试了上面的查询,但它没有产生任何记录。我怀疑我使用了错误的联接类型,但我被卡住了。

【问题讨论】:

    标签: sql-server tsql join


    【解决方案1】:

    您可以将左连接与需要管理员权限的地方结合使用,或者在连接上进行匹配

    SELECT
        ID,
        DepartmentID,
        FileName
    FROM
        Document
    -- conditional join from here
    LEFT JOIN
        AllowedDepartmentList ON DepartmentID = AllowedDepartmentList.ID 
    WHERE
        @IsAdmin = 'true' OR AllowedDepartmentList.ID IS NOT NULL
    

    【讨论】:

    • 请注意,jhilden 的建议更有效,因为您可以避免在不需要的情况下加入。不过,我确实理解您只保留一个查询的论点。
    • 非常感谢,这似乎没问题,我得测试一下。是的,你是对的,我们还必须测试额外的连接是否会导致如此多的开销,以至于值得复制代码。
    • 我接受了您的回答,因为我们已实施此解决方案以进行测试。它根据需要工作并完成工作,但应该衡量性能。还有许多其他适合我的答案,所以感谢所有花时间摆弄我的问题的人。
    【解决方案2】:

    作为一般规则,将参数放入 WHERE 子句 VS 连接中。一个稍微简单的解决方案是让 TVF 或 sproc 运行两个完全不同的查询。像这样:

    IF (@isAdmin = 0) --notice I used a SQL bool vs a string of 'false'
    BEGIN
        SELECT
            ID,
            DepartmentID,
            FileName
        FROM
            Document
        JOIN
            AllowedDepartmentList ON DepartmentID = AllowedDepartmentList.ID;
    END;
    ELSE
       --@IsAdmin is not false, so don't join
        SELECT
            ID,
            DepartmentID,
            FileName
        FROM
            Document;
    END;
    

    【讨论】:

    • 这是我试图避免的:重复选择。我们的 SP 中的精确选择要复杂得多(比如 150 行),如果整个 shebang 被复制,就会变得非常难看……更不用说未来的修改了。
    • @Daniel 如果整个 shebang 是 150 行,则更有理由进行有效的查询。复制粘贴和删除连接有多难?
    • 你在这件事上是绝对正确的。我们将不得不对真实数据进行一些压力测试,以找出最佳答案。我只是在寻找代码重复的替代方法,但我从没想过会得到这么多好的建议(包括保持当前轨道,就像 jhilden - 和你 - 建议的那样)。
    【解决方案3】:

    你可以使用存在条件,像这样:

    SELECT
     ID,
     DepartmentID,
     FileName
    FROM Document
    WHERE exists(
     SELECT 1 from AllowedDepartmentList 
     WHERE DepartmentID = AllowedDepartmentList.ID) 
    OR @IsAdmin = 'true'
    

    【讨论】:

    • 我认为它更快,特别是如果您使用附加条件,但您必须尝试。 join 是获取数据,exist 非常适合你:)
    • 加入会导致记录相乘,不存在。
    • 嗯。有趣的厘米。一定会检查这个建议。
    【解决方案4】:

    使用Dynamic query 的另一种方法。与JOIN/Exists options 相比,这将具有良好的性能

    DECLARE @sql     NVARCHAR(max)='',
            @IsAdmin BIT = 1
    
    SET @sql = '
    SELECT
        ID,
        DepartmentID,
        FileName
    FROM
        Document D
        ' + CASE WHEN @IsAdmin = 'false' THEN ' where exists (select 1 from  AllowedDepartmentList AD where D.DepartmentID = AD.ID ) ' ELSE '' END
    
    --PRINT @sql 
    
    exec sp_executesql @sql 
    

    @IsAdmin = 0 时的动态框架查询

    SELECT ID,
           DepartmentID,
           FileName
    FROM   Document D
    WHERE  EXISTS (SELECT 1
                   FROM   AllowedDepartmentList AD
                   WHERE  D.DepartmentID = AD.ID) 
    

    @IsAdmin = 0 时的动态框架查询

    SELECT ID,
           DepartmentID,
           FileName
    FROM   Document D
    

    【讨论】:

      【解决方案5】:

      以下查询会对您有所帮助

      SELECT  ID
              ,DepartmentID
              ,FileName
      FROM    Document
              LEFT JOIN   AllowedDepartmentList ON DepartmentID = AllowedDepartmentList.ID
      WHERE   @isAdmin = 1
          OR  ( @isAdmin = 0 AND AllowedDepartmentList.ID IS NOT NULL)
      

      【讨论】:

        【解决方案6】:

        为管理员添加一个类似于 -1 的 AllowedDepartmentList.ID
        为 @adminID 传递 null 或 -1

        ON isnull(@adminID, DepartmentID) = AllowedDepartmentList.ID
        

        一个 OR(或 @IsAdmin = 'true' )效率不高,这也只返回 1

        【讨论】:

        • 这会杀死DepartmentID上的所有索引
        • @Prdp 所以,我假设 AllowedDepartmentList.ID 是一个 PK。它并不比代码更少的 EXISTS 差。
        猜你喜欢
        • 2018-02-21
        • 1970-01-01
        • 2013-01-19
        • 1970-01-01
        • 1970-01-01
        • 2012-03-14
        • 1970-01-01
        • 1970-01-01
        • 2020-05-25
        相关资源
        最近更新 更多