【问题标题】:determine order of operations in query确定查询中的操作顺序
【发布时间】:2014-03-14 00:44:46
【问题描述】:

假设我有这样的查询:

SELECT * 
FROM Foo
WHERE Name IN ('name1', 'name2') 
  AND (Date<'2013-01-01' AND Date>'2010-01-01') 
  AND Type = 1

有没有办法强制 SQL 服务器按照我确定的顺序而不是查询优化器所说的顺序来评估表达式?例如,我希望首先评估 IN 子句,然后由 Type = 1 评估该子句的输出,最后是日期,完全按照这个顺序。

【问题讨论】:

  • 为什么谓词的评估顺序很重要?
  • true AND true AND true 始终计算为 true。为什么你认为强制 SQL Server 以特定顺序评估真相会给你带来任何好处?
  • 优化器很聪明。你不能骗他做傻事。
  • 试图超越优化器通常不是一个好主意。
  • 如果你的问题是效率,这不是提问的方式。尝试添加表定义、所有索引和慢查询的实际执行计划。 SQL-Server 的优化器不过是迟钝的。

标签: sql-server sql-server-2008 tsql


【解决方案1】:

是的,这在很大程度上是可能的(尽管有一些警告和反例discussed in the answers here

SELECT *
FROM   Foo
WHERE  1 = CASE
             WHEN Name IN ( 'name1', 'name2' ) THEN
               CASE
                 WHEN Type = 1 THEN
                   CASE
                     WHEN ( Date < '2013-01-01'
                            AND Date > '2010-01-01' ) THEN 1
                   END
               END
           END 

但是为什么要打扰呢?只有在非常有限的情况下,我认为这会很有用(例如,如果早期的谓词评估为 0,则防止除以零)。

像这样包装谓词会使查询完全不可分割,并防止三个(否则可分割的)谓词中的任何一个使用索引。它保证读取所有行的全扫描。

看一个例子

CREATE TABLE Foo
  (
     Id     INT IDENTITY PRIMARY KEY,
     Name   VARCHAR(10),
     [Date] DATE,
     [Type] TINYINT,
     Filler CHAR(8000) NULL
  )

CREATE NONCLUSTERED INDEX IX_Name
  ON Foo(Name)

CREATE NONCLUSTERED INDEX IX_Date
  ON Foo(Date)

CREATE NONCLUSTERED INDEX IX_Type
  ON Foo(Type)

INSERT INTO Foo
            (Name,
             [Date],
             [Type])
SELECT TOP (100000) 'name' + CAST(0 + CRYPT_GEN_RANDOM(1) AS VARCHAR),
                    DATEADD(DAY, 7 * CRYPT_GEN_RANDOM(1), '2012-01-01'),
                    0 + CRYPT_GEN_RANDOM(1)
FROM   master..spt_values v1,
       master..spt_values v2 

然后在问题中运行原始查询与此查询给出计划

请注意,第二个查询的成本是批处理成本的 100%。

留给自己设备的查询优化器首先查找与 type 谓词匹配的 414 行,并将其用作哈希表的构建输入。然后它查找与name 匹配的728 行,查看它是否匹配哈希表中的任何内容,并且对于匹配的4 行,它对其他列执行键查找并针对这些列评估Date 谓词。最后它返回单个匹配行。

第二个查询只是遍历表中的所有行并以所需的顺序评估谓词。阅读页数的差异非常显着。

原始查询

Table 'Foo'. Scan count 3, logical reads 23,
Table 'Worktable'. Scan count 0, logical reads 0

嵌套案例

Table 'Foo'. Scan count 1, logical reads 100373

【讨论】:

  • +1 为答案。这确保了(正如您所说的一些警告)每一行的执行顺序。我们还可以使用临时表,在表级别强制执行此操作(首先检查IN 条件,保存在 temp1,然后检查...保存在 temp2,...)
  • @ypercube - 无论如何,“评估顺序”在所有情况下的含义都不是很清楚。例如在第一个计划中,它首先评估匹配type 的行,然后评估那些匹配name 的行。所以从某种意义上说,它首先评估了这一点。但它会从搜索中返回所有匹配的namerows,而不仅仅是那些已经被证明与type匹配的。
  • 感谢您的好回答,但我通过将谓词移动到连接条件来解决了我的具体情况,然后我能够通过使用连接提示来强制排序。查询运行时间从 2 分钟缩短到 2 秒。
【解决方案2】:

简短回答:不!

可以尝试使用括号、提示、学习查询计划等。

但是这样搞砸引擎/优化器是否明智? 你需要大量的学习和经验才能超越优化器,也就是说,请让引擎为你处理这些细节。

【讨论】:

  • @Blam - Intersect 对此不做任何保证。
  • @MartinSmith 同意。我只按照查询的顺序看到了 Intersect,但我玩了一下,得到了一些查询计划,而不是查询的顺序。
  • 正如我所说,你可以试试。即使更改查询计划也很棘手,并且需要了解引擎如何解析和执行的知识。您还需要了解提示等。马丁发布了一种“有效”的方式。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-01-08
  • 2017-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多