7

我有三个表:SQL查询执行快捷方式或逻辑?

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

SmallTable有,顶多几十记录。 BigTable已经有几百万了,实际上是UNION在这个数据库中的一张表,在同一台服务器上的另一个数据库中有一张表。

这里的加入逻辑:

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被管理,所以这不会产生问题),这将使这个关键查询更快。

+0

+1有趣的问题。希望能够从这里学到更多! – AdaTheDev 2010-01-25 21:50:11

+0

你提到了一个关于'BigTable'的索引扫描,这是一个视图。它是索引视图还是索引扫描在基础表上执行?你可以在这里发布查询计划吗? – Quassnoi 2010-01-27 17:03:41

回答

5

SQL布尔评估确实不是保证运营商短路。请参阅On SQL Server boolean operator short-circuit以获取一个清晰的示例,说明假设操作员短路可能导致正确性问题和运行时错误。

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

+0

我同意 - 这本身并不是一个快捷方式问题,但是查询引擎即使将两个标志设置为1也会将工作检查为'text1'的值的问题设置为1.这是一个索引操作,但是不必要*如果* SmallTable中的所有选定记录都设置了这些标志。正如我在其他地方所评论的,我认为这是一个问题,查询优化器不够“足够聪明”以避免BigTable.text1上的所有记录不需要文本比较的工作分支。 – richardtallent 2010-01-25 21:34:14

+1

不幸的是,没有程序/条件查询树操作符。换句话说,没有运营商说'如果条件是真的,沿着这条路走下去,否则这条路'。查询优化必须创建一个满足所有可能条件的计划,即使那些可能性很低的计划也是如此。查询计划可以在许多*确定性*条件下做很多技巧,例如。与可信的外国关系*联合的两个表可能*完全消除该计划中的一个表。这就是所有T-SQL技巧的出现,比如使用UNION而不是OR。 – 2010-01-25 22:24:57

+0

这是一个非常好的评论。我努力说出我的理解,但这个恕我直言是一个很好的解释。 – AdaTheDev 2010-01-25 22:39:12

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') 

请让我知道发生了什么

此外,您也许能够加快这只需返回此查询的唯一标识,然后使用该标识的结果获取所有其他数据。

编辑

这样的事情?

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') 
) 
+0

试过这个,当两个标志都设置为1时,它实际上要慢得多。 – richardtallent 2010-01-25 21:25:51

+0

我想我们可以通过额外的连接和强制顺序来做到这一点,但是我的大脑现在正在模糊地想到它。 – Hogan 2010-01-25 21:58:24

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') 
    ) 
+0

谢谢...试过这个,但执行结果与我所知道的完全相同。 – richardtallent 2010-01-25 21:24:59

1

我不相信SQL Server将像短路条件很遗憾。

所以我建议做2个查询和联合他们在一起。首先用s.flag1 = 1和s.flag2 = 1查询条件,第二个查询用s.flag1加入到BigTable> 1 a s.flag2 <> 1条件。

This文章对此事是值得一读,包括底线:

... SQL Server不会做 短路就像是在 其他编程语言完成, 有没有什么可以做到的,以强制它 到。

更新:
This文章也是一个有趣的阅读,并包含有关这个主题的一些很好的联系,包括对SQL Server查询处理器团队,简要地提到,优化器开发经理的TechNet聊天允许短路评估。我从各种文章中得到的总体印象是“是的,优化者可以发现短路的机会,但不应该依靠它,而且你不能强迫它”。因此,我认为联盟的做法可能是你最好的选择。如果它没有提出一个利用捷径的机会的计划,这将归结为基于成本的优化器,认为它是一个合理的计划,而不是这样做(这将归结为索引,统计数据等) 。

+1

良好的评论,但虽然SQL Server不快捷表达评估,它*做*查询优化。因此,如果我的查询仅限于两个标志为“1”的SmallTable结果集,它应该足够聪明以跳过对“BigTable.text1”的检查,但这需要根据数据更改执行计划。我认为这是核心问题。 – richardtallent 2010-01-25 21:29:03

+0

是的,我看到你在说什么,但我仍然认为它是捷径 - 优化器需要能够看到短路的机会。我在过去对这类事情做过一些测试,这就是我所看到的(没有以这种方式进行优化)。 – AdaTheDev 2010-01-25 21:48:11

+0

问题是,对于一个'或'它可以按任何顺序进行评估,并且它首先执行右侧。 – Hogan 2010-01-25 21:56:00

0

SQL服务器通常抓起子查询暗示(虽然它是免费丢弃它):

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