7

我有三张桌子:

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

SmallTable最多有几十条记录。BigTable有几百万,实际上是一个视图,它将这个数据库中的一个表与同一服务器上另一个数据库中的一个表联合起来。

这是连接逻辑:

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记录,flag1并且flag2设置为1,因此实际上甚至不需要访问 BigTable.text1 上的索引,但 SQL Server 无论如何都会这样做,从而导致代价高昂的索引扫描和嵌套循环。

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

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

4

6 回答 6

5

SQL 布尔评估不保证运算符短路。有关说明假设运算符短路如何导致正确性问题和运行时错误的清晰示例,请参阅On SQL Server boolean operator short-circuit 。

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

于 2010-01-25T21:24:39.963 回答
1

不幸的是,我不相信 SQL Server 会短路这种情况。

所以我建议做 2 个查询并将它们联合起来。第一个查询使用 s.flag1=1 和 s.flag2=1 WHERE 条件,第二个查询使用 s.flag1<>1 和 s.flag2<>1 条件连接到 BigTable。

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

...SQL Server 不会像在其他编程语言中那样进行短路,并且您无法强制它这样做。

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

于 2010-01-25T21:17:46.067 回答
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')
)
于 2010-01-25T21:15:56.957 回答
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')
    )
于 2010-01-25T21:16:09.677 回答
0

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

SELECT      * 
FROM        (
            SELECT * FROM SmallTable where flag1 <> 1 or flag2 <> 1
            ) s
INNER JOIN  JoinTable j ON j.SmallTableID = s.ID
...
于 2010-01-25T21:24:41.420 回答
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

于 2019-06-14T12:10:39.920 回答