【发布时间】:2019-10-09 12:11:29
【问题描述】:
挑战:找出从 1 到 512 的所有三元组 (n1, n2, n3),使得 n1=n2*n3。仅使用纯 SQL,没有预期的预先存在的表空间,也没有创建新的永久表。
CTE 解决方案:
;with two as
(
select 0 as ID union select 1 as ID
), eight as
(
select t1.ID*4+t2.ID*2+t3.ID as ID
from two t1 inner join two t2 on 1=1 inner join two t3 on 1=1
), halfk as
(
select t1.ID*8*8 + t2.ID*8 + t3.ID + 1 as ID
from eight t1 inner join eight t2 on 1=1 inner join eight t3 on 1=1
)
select t1.ID, t2.ID, t3.ID
from halfk t1
inner join halfk t2 on t1.ID % t2.ID = 0
inner join halfk t3 on t3.ID * t2.ID = t1.ID
运行时间:不知道;大约 2 分钟后停止。
临时表解决方案:
if (object_id('tempdb..#tmp_two', 'U') is not null) drop table #tmp_two
select 0 as ID into #tmp_two union select 1 as ID
if (object_id('tempdb..#tmp_eight', 'U') is not null) drop table #tmp_eight
select t1.ID*4+t2.ID*2+t3.ID as ID into #tmp_eight
from #tmp_two t1 inner join #tmp_two t2 on 1=1 inner join #tmp_two t3 on 1=1
if (object_id('tempdb..#tmp_halfk', 'U') is not null) drop table #tmp_halfk
select t1.ID*8*8 + t2.ID*8 + t3.ID + 1 as ID into #tmp_halfk
from #tmp_eight t1 inner join #tmp_eight t2 on 1=1 inner join #tmp_eight t3 on 1=1
select t1.ID, t2.ID, t3.ID as ID
from #tmp_halfk t1 inner join #tmp_halfk t2 on t1.ID % t2.ID = 0
inner join #tmp_halfk t3 on t3.ID * t2.ID = t1.ID
运行时间:1 秒。
问题:为什么性能如此不同?为什么在上述解决方案中多次使用 CTE 表也没有实现?
更重要的是,作为巨大的性能影响,我如何避免使用干净整洁的方式来完成工作但引入性能危害?是否有任何指南可以避免此类情况?
select @@VERSION
Microsoft SQL Server 2016 (SP1-CU10-GDR) (KB4293808) - 13.0.4522.0 (X64) Jul 17 2018 22:41:29 Copyright (c) Microsoft Corporation Enterprise Edition (64-bit) on Windows Server 2012 R2 Datacenter 6.3 <X64> (Build 9600: ) (Hypervisor)
【问题讨论】:
-
因为 CTE 很可能没有被具体化。您可以更进一步,还可以考虑为临时表编制索引,这是 CTE 无法做到的。
-
第一步:检查查询计划(CTRL-L)
-
查询计划并不容易阅读。它很大,主要成本是“Table Spool”和“Filter”,这没有多大意义——“Table Spool”是指临时表,对吧?
-
@TimBiegeleisen 我不需要索引上面示例中的临时表,因为它们足够小。然而临时表解决方案运行良好。我担心的是:什么时候我可以确定使用 CTE 没有更差的性能?随着可读性的提高,我更喜欢它,但我不想导致查询缓慢。
-
SQL Server CTE 中的 AFAIK 未实现,您只需直接运行底层查询。
标签: sql-server common-table-expression temp-tables