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和最后的日期,完全是该顺序.
是的,这在很大程度上是可能的(尽管这里的答案中讨论了一些警告和反例)
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)