确定查询中的操作顺序

use*_*923 -6 t-sql sql-server sql-server-2008

说我有这样的查询:

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

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

Mar*_*ith 5

是的,这在很大程度上是可能的(尽管这里的答案中讨论了一些警告和反例)

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 
Run Code Online (Sandbox Code Playgroud)

但为什么要这么麻烦?只有非常有限的情况我可以看到这将是有用的(例如,如果先前的谓词被评估,则防止除以零0).

像这样包装谓词会使查询完全无法查找,并且会阻止三个(否则为sargable)谓词中的任何一个使用索引.它保证读取所有行的完整扫描.

看一个这样的例子

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 
Run Code Online (Sandbox Code Playgroud)

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

在此输入图像描述

请注意,第二个查询的成本计算为批次成本的100%.

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

第二个查询只是遍历表中的所有行,并按所需顺序评估谓词.读取页数的差异非常大.

原始查询

Table 'Foo'. Scan count 3, logical reads 23,
Table 'Worktable'. Scan count 0, logical reads 0
Run Code Online (Sandbox Code Playgroud)

嵌套案例

Table 'Foo'. Scan count 1, logical reads 100373
Run Code Online (Sandbox Code Playgroud)