SQL Query执行快捷方式OR逻辑?

时间:2010-01-25 21:05:59

标签: sql-server performance tsql sql-execution-plan short-circuiting

我有三张桌子:

SmallTable
   (id int, flag1 bit, flag2 bit)
JoinTable
   (SmallTableID int, BigTableID int)
BigTable
   (id int, text1 nvarchar(100), otherstuff...)

SmallTable最多只有几十条记录。 BigTable有几百万,实际上是UNIONS在这个数据库中的一个表,在同一台服务器上的另一个数据库中有一个表。

这是连接逻辑:

SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE
    (s.flag1=1 OR b.text1 NOT LIKE 'pattern1%')
    AND (s.flag2=1 OR b.text1 <> 'value1')

平均连接大小是几千个结果。显示的所有内容都已编入索引。

对于大多数SmallTable条记录,flag1flag2都设置为1,因此实际上甚至无需访问BigTable.text1上的索引,而是SQL Server无论如何,导致代价高昂的索引扫描和嵌套循环。

有没有更好的方法来提示SQL Server,如果flag1flag2都设置为1,它甚至不应该费心去查看text1

实际上,如果我可以在这些情况下完全避免加入BigTable(管理JoinTable,这不会产生问题),这将使这个关键查询更快。

6 个答案:

答案 0 :(得分:5)

SQL布尔评估 NOT 保证操作员短路。有关如何假设操作员短路可能导致正确性问题和运行时错误的明确示例,请参阅On SQL Server boolean operator short-circuit

另一方面,我的链接中的示例显示了 为SQL Server工作的内容:提供SQL可以使用的访问路径。因此,与所有 SQL性能问题和问题一样,真正的问题不在于表达SQL文本的方式,而在于存储的设计。 IE浏览器。查询优化器可以使用哪些索引来满足您的查询?

答案 1 :(得分:1)

我不相信SQL Server会不幸发生这样的短路情况。

所以我建议你做两个查询并将它们联合起来。第一个查询使用s.flag1 = 1和s.flag2 = 1 WHERE条件,第二个查询使用s.flag1&lt;&gt; 1 a s.flag2&lt;&gt; 1条件连接到BigTable。

This关于此事的文章值得一读,并包括底线:

  

... SQL Server没有   短路就像它完成的那样   其他编程语言和   你无能为力迫使它   到。

<强>更新
This文章也是一本有趣的读物,其中包含一些关于此主题的良好链接,包括与SQL Server查询处理器团队的开发经理进行的技术聊天,该团队简要提到优化器允许进行短路评估。我从各篇文章中得到的总体印象是“是的,优化器可以发现短路的机会,但你不应该依赖它,你不能强迫它”。因此,我认为UNION方法可能是您最好的选择。如果它没有提出一个利用捷径机会的计划,那将归结为基于成本的优化器,认为它找到了一个合理的计划,而不是这样做(这将取决于索引,统计等)

答案 2 :(得分:0)

不知道如果没有测试数据,这会更快......但听起来可能

SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE
    (s.flag1=1) AND (s.flag2=1)
 UNION ALL
 SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE
    (s.flag1=0 AND b.text1 NOT LIKE 'pattern1%')
    AND (s.flag2=0 AND b.text1 <> 'value1')

请让我知道会发生什么

此外,您可以通过仅返回此查询的唯一ID,然后使用其结果获取所有其余数据来加快速度。

修改

这样的事情?

SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE
    (s.flag1=1) AND (s.flag2=1)
 UNION ALL
 SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE EXISTS
    (SELECT 1 from BigTable b
     WHERE   
    (s.flag1=0 AND b.text1 NOT LIKE 'pattern1%')
    AND (s.flag2=0 AND b.text1 <> 'value1')
)

答案 3 :(得分:0)

它不优雅,但应该有用......

SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE
    (s.flag1 = 1 and s.flag2 = 1) OR 
    (
       (s.flag1=1 OR b.text1 NOT LIKE 'pattern1%')
       AND (s.flag2=1 OR b.text1 <> 'value1')
    )

答案 4 :(得分:0)

SQL Server通常会抓取子查询提示(尽管它可以自由丢弃):

SELECT      * 
FROM        (
            SELECT * FROM SmallTable where flag1 <> 1 or flag2 <> 1
            ) s
INNER JOIN  JoinTable j ON j.SmallTableID = s.ID
...

答案 5 :(得分:0)

希望这可行-小心case周围aggregates语句中的快捷方式逻辑,但是...

SELECT * FROM
    SmallTable s
    INNER JOIN JoinTable j ON j.SmallTableID = s.ID
    INNER JOIN BigTable b ON b.ID = j.BigTableID
WHERE 1=case when (s.flag1 = 1 and s.flag2 = 1) then 1
when (
       (s.flag1=1 OR b.text1 NOT LIKE 'pattern1%')
       AND (s.flag2=1 OR b.text1 <> 'value1')
    ) then 1
else 0 end