HAVING 中的条件比 WHERE 中的相同条件更快?

Condition in HAVING faster than same condition in WHERE?

我遇到了一个查询,该查询在查询的 HAVING 部分中有一个条件,我通常将其放在 WHERE 部分中。我先将条件移动到 WHERE 部分,令我惊讶的是,使用 WHERE 而不是 HAVING 的代码 运行s 大约长了 200%+。

这对我来说很奇怪,我还没有在网上找到任何描述这个的东西。

以下是查询布局的示例:

此查询持续在 50 到 55 秒内 运行s。

SELECT TB1.COL1
    ,TB1.COL2
    ,TB2.COL3
    ,TB3.COL4
    ,TB1.SOME_ID
FROM TABLE1 TB1
JOIN TABLE2 TB2
JOIN TABLE3 TB3
    ON TB1.SOME_ID = TB2.SOME_ID
    ON TB1.SOME_ID = TB3.SOME_ID
GROUP BY TB1.COL1, TB1.COL2, TB2.COL3, TB3.COL4, TB1.SOME_ID
HAVING TB1.SOME_ID = 9999999

但是此查询 运行s 在 120 到 130 秒内一致。

SELECT TB1.COL1
    ,TB1.COL2
    ,TB2.COL3
    ,TB3.COL4
    ,TB1.SOME_ID
FROM TABLE1 TB1
JOIN TABLE2 TB2
JOIN TABLE3 TB3
    ON TB1.SOME_ID = TB2.SOME_ID
    ON TB1.SOME_ID = TB3.SOME_ID
WHERE TB1.SOME_ID = 9999999
GROUP BY TB1.COL1, TB1.COL2, TB2.COL3, TB3.COL4, TB1.SOME_ID

对于这种情况,为什么 HAVING 比 WHERE 语句快 运行 我很困惑。我的意思是它不是聚合或任何东西,所以在这种情况下我通常会使用 WHERE 但测试表明它更慢......

有什么想法吗?

更新:

这是原始查询,请注意我没有写这个查询,它确实工作得很好。我只是想了解为什么 HAVING 比在 WHERE:

中使用相同条件更快
SELECT TB1.COL1
,TABLE302.COL2
,TABLE314.COL3
,TABLE314.COL4
,TABLE312.COL5
,TABLE314.COL6
,TABLE302.COL7
,TABLE320.COL8
,TABLE320.COL9
,TABLE302.COL10
,TABLE302.COL11
,TABLE230.COL12
,TABLE100.COL13
,TABLE100.COL14
,TABLE104.COL15
,TABLE110.COL16
,TABLE230.COL17
,TABLE230.COL18
,TB1.COL19
,IIf([TABLE230.CD]>'     '
    And format(cast(TABLE230.DT as date),'yyyy-MM-dd') = format(cast('12/31/9999' as date),'yyyy-MM-dd'), TABLE230.AMT,TB1.O) AS [FA]
,TABLE230.COL20
,TABLE230.COL21
,format(cast(DATEADD(day, DATEDIFF(day, 0, GETDATE()), 0) as date),'yyyy-MM-dd') AS [TODAY]
,TABLE104.COL22
,TABLE320.COL23
,TABLE180.COL24
,Count(TABLE100.COL14) AS [COUNT_COL14]
,IIf(format(cast(TABLE230.FDT as date),'yyyy-MM-dd') = format(cast('1/1/1901' as date),'yyyy-MM-dd')
    And format(cast(TABLE230.DDT as date),'yyyy-MM-dd') = format(cast('1/1/1901' as date),'yyyy-MM-dd'),'NOT ACTIVE','ACTIVE') AS [ST]
,TABLE230.COL25
FROM TABLE1 TB1
RIGHT JOIN TABLE302 
INNER JOIN TABLE114 
INNER JOIN TABLE130 
INNER JOIN TABLE110 
INNER JOIN TABLE126
INNER JOIN TABLE104
INNER JOIN TABLE124
INNER JOIN TABLE400
INNER JOIN TABLE120
ON TABLE400.CYP = TABLE120.CYP
INNER JOIN TABLE100
ON TABLE120.PID = TABLE100.COL13
ON TABLE124.PID = TABLE100.COL13
INNER JOIN TABLE230
ON TABLE120.PID = TABLE230.PID
AND TABLE120.CYP = TABLE230.CYP
ON TABLE104.PID = TABLE230.PID
AND TABLE104.PID = TABLE124.PID
ON TABLE126.PID = TABLE104.PID
ON TABLE110.EID = TABLE230.EID
AND TABLE110.EID = TABLE126.EID
ON TABLE130.GCD = TABLE230.GCD
AND TABLE130.EID = TABLE110.TID
ON TABLE114.GCD = TABLE130.GCD
ON TABLE302.COL7 = TABLE230.LD
AND TABLE302.COL10 = TABLE400.HP
AND TABLE302.COL11 = TABLE400.SP
INNER JOIN TABLE180
INNER JOIN TABLE320
ON TABLE180.RN = TABLE320.COL23
ON TABLE302.COL7 = TABLE320.CID
INNER JOIN TABLE314
ON TABLE302.ZID = TABLE314.COL4
ON TB1.COL1 = TABLE230.GCD
LEFT JOIN TABLE312
ON TABLE314.COL4 = TABLE312.TID
GROUP BY TB1.COL1
,TABLE302.COL2
,TABLE314.COL3
,TABLE314.COL4
,TABLE312.COL5
,TABLE314.COL6
,TABLE302.COL7
,TABLE320.COL8
,TABLE320.COL9
,TABLE302.COL10
,TABLE302.COL11
,TABLE230.COL12
,TABLE100.COL13
,TABLE100.COL14
,TABLE104.COL15
,TABLE110.COL16
,TABLE230.COL17
,TABLE230.COL18
,TB1.COL19
,IIf([TABLE230.CD]>'     '
    And format(cast(TABLE230.DT as date),'yyyy-MM-dd') = format(cast('12/31/9999' as date),'yyyy-MM-dd'), TABLE230.AMT,TB1.O)
,TABLE230.COL20
,TABLE230.COL21
,format(cast(DATEADD(day, DATEDIFF(day, 0, GETDATE()), 0) as date),'yyyy-MM-dd')
,TABLE104.COL22
,TABLE320.COL23
,TABLE180.COL24
,IIf(format(cast(TABLE230.FDT as date),'yyyy-MM-dd') = format(cast('1/1/1901' as date),'yyyy-MM-dd')
    And format(cast(TABLE230.DDT as date),'yyyy-MM-dd') = format(cast('1/1/1901' as date),'yyyy-MM-dd'),'NOT ACTIVE','ACTIVE')
,TABLE230.COL25
,TABLE230.FDT
,TABLE230.MDT
,TABLE230.NCD
,TABLE114.GCD
,TABLE230.MDT
HAVING TABLE314.COL4 = 99999999  -- If I move this line to WHERE it runs 2x longer
and format(cast(TABLE230.MDT as date),'yyyy-MM-dd') > format(cast('12/31/2019' as date),'yyyy-MM-dd')

执行计划似乎也不同。

区别在于执行计划。您必须查看两个查询的执行计划才能发现差异。

根据我的经验,差异通常是由于 GROUP BY 使用索引的能力所致。 WHERE 中的过滤阻止使用索引。但是,您的查询并非如此,因为它是按多个表中的列进行聚合的。

还有一种可能是过滤器去掉的记录比较少,但是影响了JOIN的执行计划。我怀疑这是你所看到的原因。您需要查看执行计划以查看连接是否相同。

一般来说,where 子句上的过滤器应该更快,尤其是聚合(应用过滤器越快,聚合越快)。但是,您的查询不是典型的或更好的 "local" 查询。您正在加入 table 个链接服务器,在处理链接服务器时,"topological where"/应用过滤器的哪一侧很重要(它是应用在本地服务器上还是传递到链接服务器?) .

从执行计划的图片来看,有3个远程tables和1个本地table(假设执行计划是针对同一个查询,只是过滤器的位置发生了变化在查询中)。 第一个计划,因为 HAVING 没有 3 个远程操作员。这意味着其中一个远程操作员实际上是两个 table 的连接(在远程端),这很可能是第一个远程操作员 (98%)。请注意,没有过滤器,因此在远程端连接了两个 table 并且所有行都被 returned [最好通过将鼠标悬停在运算符上并检查执行的查询来验证]。 HAVING 计划的第二个远程运算符,再次从远程端拉出所有行,将它们与本地 IRIS table 哈希匹配,对哈希匹配输出进行排序,并将两个步骤(正在排序)的结果合并连接并聚合。 这样做的主要好处是远程端仅被访问两次,并且连接的工作量由远程端提供。

对于WHERE plan,filter是通过(?)到另一边的,但是只针对一个table(远程查询97%--> filter --> merge)。这里有一个问号,因为如果 filter/WHERE 值被传递到链接服务器,过滤器运算符不应该在那里,远程运算符将 return 只有符合条件的行(如果过滤器实际上是通过了)。也许这是最坏的情况,来自远程 table 的所有行都被拉入,然后在本地进行过滤。对第二个 table(远程查询 0%)进行相同的操作,合并连接(在本地服务器)并与本地 table IRIS 匹配哈希。在这一点上,HAVING 和 WHERE 之间的主要区别是谁在进行连接。

HAVING --> 在远端加入两个 tables

WHERE --> 拉取远程行并在本地加入它们

最多 "tricky" 运算符,可能是 WHERE 计划的第三个远程查询(成本 2%),在嵌套循环的内部。此运算符的 "course" 取决于嵌套循环的外部行数。如果有 1、2、3 行要迭代,那么 1-3 个远程查询不会受到伤害(除非他们在远程端执行各种完整扫描,不太可能)。但是,如果嵌套循环必须迭代数千行,那么每次迭代都是对链接服务器的查询,这可能会非常昂贵(您可以通过查看实际执行中的实际行数来验证计划)。

乍一看,这两个执行计划的性能差异可以用联合努力来解释。 HAVING 需要较少的本地工作量,因此速度更快。

这并不意味着 WHERE 上的过滤器不能更快。这也可能是您编写脚本的同事的想法,因为他使用的是嵌套连接

FROM TABLE1 TB1
JOIN 
( TABLE2 TB2
  JOIN TABLE3 TB3 ON TB1.SOME_ID = TB2.SOME_ID
)  ON TB1.SOME_ID = TB3.SOME_ID
WHERE TB1.SOME_ID = 9999999

这似乎是故意的,尤其是如果以下内容为真:本地 table TB1,远程 tables TB2&TB3。

理想的执行,将是两个执行计划的混搭(在远程端加入两个 tables 但带有谓词,即过滤值) 您可以尝试更改在嵌套查询中显式应用过滤器的查询

FROM TABLE1 TB1
JOIN 
( TABLE2 TB2
  JOIN TABLE3 TB3 ON TB1.SOME_ID = TB2.SOME_ID **AND TB2.SOME_ID = 9999999**
)  ON TB1.SOME_ID = TB3.SOME_ID
WHERE TB1.SOME_ID = 9999999

并且可能明确强制命令,看看它是否有任何不同。

我希望以上内容有些道理。