【问题标题】:Optimizing CASE WHEN statement in SQL Server WHERE clause condition优化 SQL Server WHERE 子句条件中的 CASE WHEN 语句
【发布时间】:2016-12-12 20:46:14
【问题描述】:

我正在重写我的 sql 以降低执行成本,并想知道是否有一种有效的方法来编写以下用于 WHERE 条件的 CASE WHEN 语句:

SELECT l.*,tg.*
FROM  RefTable tg, 
      InputTbl l
WHERE tg.areascheme = l.areascheme
  AND tg.countrycode = l.strareabriefnamel1  
  AND ( CASE WHEN l.strareabriefnamel2 IS NULL THEN '' ELSE tg.areacode END ) = COALESCE( l.strareabriefnamel2,'' )
  AND ( CASE WHEN l.strareabriefnamel3 IS NULL THEN '' ELSE tg.subareaname END ) = COALESCE( l.strareabriefnamel3,'' )
  AND ( CASE WHEN l.strareabriefnamel4 IS NULL THEN '' ELSE tg.postalname END ) = COALESCE( l.strareabriefnamel4,'' )
  option( MAXDOP 0 ); 

执行计划:-

更多细节:-

InputTable( 466K records ) 共有四个字段参与 JOIN 逻辑,总共有 16 种可能的 (NULL,NOT NULL) 组合。

L1,  L2,  L3,  L4
NULL,NULL,NULL,NULL
NULL,NULL,NULL,NOT NULL
NULL,NULL,NOT NULL, NULL
NULL,NULL,NOT NULL,NOT NULL
NULL,NOT NULL,NULL,NULL
NULL,NOT NULL,NULL, NOT NULL
NULL,NOT NULL, NOT NULL,NULL
NULL,NOT NULL,NOT NULL,NOT NULL
NOT NULL,NULL,NULL,NULL
NOT NULL,NULL,NULL,NOT NULL
NOT NULL,NULL,NOT NULL,NULL
NOT NULL,NULL,NOT NULL,NOT NULL
NOT NULL,NOT NULL,NULL,NULL
NOT NULL,NOT NULL,NULL,NOT NULL
NOT NULL,NOT NULL,NOT NULL,NULL
NOT NULL,NOT NULL,NOT NULL,NOT NULL

RefTable( 45k records ) 将使用 InputTable 参与 JOIN 逻辑,根据上述标准生成结果集,生成大约 3.51 亿行。

我的输入数据目前只满足两种情况。

输入表:-

NULL,NULL,NULL,NULL - 225776 rows
NOT NULL, NOT NULL, NULL, NULL - 240360 rows

任何意见将不胜感激。谢谢。

【问题讨论】:

  • 您在使用 case-when 时发现的性能问题是什么?如果您只想检查 NULL ,您可以很好地使用 coalesce ,就像在其他条件下使用一样...
  • strareabriefnamel2 字段值可以为 NULL 和 NOT NULL。需要考虑这两个条件并在 WHERE 子句中使用它们...
  • 但是根据您的示例,您可以只使用 iif...
  • @KannanKandasamy 。 . .虽然我不喜欢WHERE 子句中的CASE,但IIF() 更糟糕,因为它不是标准SQL。
  • 不,如果 strareabriefnamel2 为 NULL,我实际上是在两边加入空字符串,但是当 strareabriefnamel2 不是 NULL 时,两边会有两个不同的 NOT NULL 值....

标签: sql sql-server tsql case


【解决方案1】:

简单规则:从不FROM 子句中使用逗号。 始终使用明确、正确的JOIN 语法。

这可能不会改变查询的性能,但它是一种更典型的编写方式。我很确定意图是:

SELECT l.*, tg.*
FROM RefTable tg JOIN
     InputTbl l
     ON tg.areascheme = l.areascheme AND tg.countrycode = l.strareabriefnamel1  
WHERE (l.strareabriefnamel2 IS NULL OR tg.areacode = l.strareabriefnamel2) AND
      (l.strareabriefnamel3 IS NULL OR tg.subareaname  = l.strareabriefnamel3) AND
      (l.strareabriefnamel4 IS NULL OR tg.postalname = l.strareabriefnamel4)
  option( MAXDOP 0 ); 

优化此查询的起点是索引。我建议:RefTable(areascheme, countrycode)InputTbl(areascheme, strareabriefnamel1)

【讨论】:

  • @SqlZim 。 . .我不会从包含的列开始,但话又说回来,我并不完全理解数据结构。
  • @clifton_h 如果没有 WHERE 子句来连接列,那将是正确的。
  • 您的意思是:笛卡尔加入@clifton_h 吗?
  • @clifton_h 虽然从 ANSI-89 标准来看确实如此,但它已在 ANSI-92 标准中被取代(25 年前)。逗号分隔的列表仍然存在,但已经失宠了很长时间。它是交叉连接的简写。主要原因是很容易漏掉一个谓词。
  • 最好的理由是在使用 OUTER 连接时使用一致的语法 -- LEFT 和 RIGHT 连接。在糟糕的过去,有一些符号用于这种类型的语法,代码如WHERE table1.field1 *= table2.field1,但实现很粗略,可读性有限,并且使用这种语法与使用 LEFT/RIGHT 语法时返回的结果可能不同- 因为 JOIN 不在 WHERE 子句中。语法的一致性对我来说已经足够了。
猜你喜欢
  • 1970-01-01
  • 2021-08-09
  • 1970-01-01
  • 1970-01-01
  • 2023-01-21
  • 2016-07-08
  • 2013-06-30
  • 2013-12-06
  • 1970-01-01
相关资源
最近更新 更多