我有简单的SQL查询:
DECLARE @flag bit = 0;
SELECT TOP (100000)
[Id]
CASE WHEN @flag=1 THEN [Description] END
FROM [dbo].[ReallyBigTable]
描述是一个非常大的字段,这里的可变标志只是字段的过滤器,在大多数情况下几乎是恒定的,但是比较类似查询的执行计划:
SELECT TOP (100000)
[Id]
FROM [dbo].[ReallyBigTable]
为我提供了实际上已读取第一个查询中的描述的信息!它不会返回,但是磁盘上的I / O在那里。在第二种情况下,磁盘上没有太多I / O,并且查询执行非常快。两者之比约为25,因此第二个查询的I / O比第一个低25。
为什么在第一种情况下虽然未使用,但仍会读取该字段?
答案 0 :(得分:3)
SQL Server必须编译对参数@flag
的任何值均有效的执行计划(在这种情况下,它是一个变量而不是参数,但与在该变量甚至是偶数之前编译该语句同样适用分配,并且不了解其价值)。
最简单的方法是在运行时撤回[Description]
并评估CASE
。这就是它的作用。
如果对此不满意,可以添加OPTION(RECOMPILE)
以在分配变量后重新编译该语句。该计划不会被缓存,只需要对编译时已知的特定值有效。如果Description
不正确,则此“参数嵌入优化”可以简化对@flag=1
的引用。但这确实意味着每次执行都会重新编译。
或者,您也可以将它们分离为分别进行了优化的不同语句-或将它们与UNION ALL
和WHERE
DECLARE @flag bit = 0;
SELECT [Id],
[Description]
FROM [dbo].[ReallyBigTable]
WHERE @flag=1
UNION ALL
SELECT [Id],
NULL
FROM [dbo].[ReallyBigTable]
WHERE @flag = 0 OR @flag IS NULL
要获得一个包含两个分支的计划,该计划带有带有启动谓词的过滤器,以便在运行时仅执行相关的一个。