我遇到了一个查询,该查询在查询的HAVING部分中具有一个条件,而该条件通常会放在WHERE部分中。我先将条件移到“ WHERE”部分,但令我惊讶的是,使用“ WHERE”而不是“ HAVING”,代码运行的时间延长了200%以上。
这对我来说很奇怪,我还无法在网上找到任何描述这一点的东西。
以下是查询布局的示例:
此查询持续运行50到55秒。
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
但是此查询始终在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')
执行计划似乎也不同。
答案 0 :(得分:1)
不同之处在于执行计划。您必须查看两个查询的执行计划才能发现差异。
根据我的经验,差异通常是由于能够为GROUP BY
使用索引。 WHERE
中的过滤会阻止使用索引。但是,查询不是这种情况,因为它是按多个表中的列进行聚合的。
另一种可能性是过滤器删除了相对较少的记录,但影响了JOIN
的执行计划。我怀疑这是您所看到的原因。您需要查看执行计划,以查看联接是否相同。
答案 1 :(得分:1)
通常,where子句上的过滤器应该更快,尤其是对于聚合(应用过滤器越早,聚合速度越快)。但是,您的查询不是典型的查询,也不是“本地”查询。您正在连接链接服务器的表,并且在与链接服务器打交道时,应用过滤器的“拓扑位置” /哪一侧很重要(它是应用在本地服务器上还是传递到链接服务器上?)。
从执行计划的图片来看,有3个远程表和1个本地表(假设执行计划用于同一查询,并且仅更改查询中的过滤器位置)。 对于HAVING,第一个计划没有3个远程操作员。这意味着一个远程操作员实际上是两个表的连接(在远程端),这很可能是第一个远程操作员(98%)。请注意,这里没有过滤器,因此将返回远程端的两个表和ALL行的联接[更好地通过将鼠标悬停在运算符上并检查执行的查询来验证。 HAVING计划的第二个远程运算符,再次从远程端拉出所有行,将它们与本地IRIS表进行哈希匹配,对哈希匹配输出进行排序,然后合并(合并)两个步骤的结果(进行排序)。 这样做的主要好处是,远程方只能访问两次,而连接工作是由远程方提供的。
对于WHERE计划,过滤器通过(?)传递到另一侧,但仅用于一个表(远程查询97%->过滤器->合并)。这里有一个问号,因为如果将filter / WHERE值传递给链接服务器,则不应在其中使用过滤器运算符,因此远程运算符将仅返回合格的行(如果过滤器实际通过了)。也许这是最坏的情况,从远程表中提取所有行,然后在本地过滤。对第二张表(远程查询0%)执行相同的操作,合并联接(在本地服务器上),并且哈希与本地表IRIS匹配。此时,HAVING和WHERE之间的主要区别在于谁在执行联接。
HAVING->在远程连接两个表
位置->拉远行并在本地将其加入
最“棘手”的运算符可以是WHERE计划中嵌套循环内部的第三个远程查询(成本2%)。此运算符的“过程”取决于嵌套循环的外部行数。如果要迭代1、2、3行,则1-3个远程查询不会受到影响(除非它们不太可能在远程端执行所有类型的全面扫描)。但是,如果嵌套循环必须遍历几千行,则每次迭代都是对链接服务器的查询,这可能会非常昂贵(您可以通过查看实际执行中的实际行数来验证这一点计划)。
乍看之下,在这两个执行计划中,性能差异可以用联合努力来解释。进行本地操作所需的精力更少,因此速度更快。
这并不意味着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
这似乎是故意的,尤其是在满足以下条件的情况下:本地表TB1,远程表TB2&TB3。
理想的执行将是两个执行计划的混搭(在远程端连接两个表,但带有谓词,即过滤后的值) 您可以尝试在嵌套查询中显式应用过滤器来更改查询
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
,也许明确地强制执行命令,看看是否有任何区别。
我希望以上内容有道理。