即使未使用字段仍在读取字段的SQL CASE

时间:2019-12-25 11:04:28

标签: sql sql-server

我有简单的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。

为什么在第一种情况下虽然未使用,但仍会读取该字段?

1 个答案:

答案 0 :(得分:3)

SQL Server必须编译对参数@flag的任何值均有效的执行计划(在这种情况下,它是一个变量而不是参数,但与在该变量甚至是偶数之前编译该语句同样适用分配,并且不了解其价值)。

最简单的方法是在运行时撤回[Description]并评估CASE。这就是它的作用。

如果对此不满意,可以添加OPTION(RECOMPILE)以在分配变量后重新编译该语句。该计划不会被缓存,只需要对编译时已知的特定值有效。如果Description不正确,则此“参数嵌入优化”可以简化对@flag=1的引用。但这确实意味着每次执行都会重新编译。

或者,您也可以将它们分离为分别进行了优化的不同语句-或将它们与UNION ALLWHERE

组合在一起
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

要获得一个包含两个分支的计划,该计划带有带有启动谓词的过滤器,以便在运行时仅执行相关的一个。