SQL CTE vs Temp表在性能方面

时间:2019-05-22 23:56:55

标签: sql-server common-table-expression temp-tables

挑战:找到(n1, n2, n3)到1到512的所有三元组。仅使用纯SQL,没有期望的预先存在的表空间,也没有创建新的永久表。

CTE解决方案:

n1=n2*n3

运行时间:不知道;将近2分钟后停止。

临时表解决方案:

;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

运行时间:1秒。

问题:为什么效果如此不同?为什么即使在上述解决方案中多次使用CTE表,也无法实现?

更重要的是,由于巨大的性能影响,如何避免使用干净整洁的方法来完成工作,但会带来性能危害?是否有避免这种情况的准则?

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 个答案:

答案 0 :(得分:1)

问题与最后一个查询及其第二个联接有关。它必须进行额外的计算,并且行数只是越过屋顶。通过了解您要执行的操作,可以轻松避免再次调用CTE。

WITH 
two AS (
    SELECT 0 AS ID UNION ALL
    SELECT 1 AS ID
),
eight AS (
    SELECT t1.ID * 4 + t2.ID * 2 + t3.ID AS ID
    FROM   two AS t1
    CROSS JOIN two AS t2 
    CROSS JOIN two AS t3 
),
halfk AS (
    SELECT t1.ID * 8 * 8 + t2.ID * 8 + t3.ID + 1 AS ID
    FROM   eight AS t1
    CROSS JOIN eight AS t2
    CROSS JOIN eight AS t3
)
SELECT t1.ID, 
    t2.ID, 
    t1.ID / t2.ID AS ID
FROM   halfk AS t1
INNER JOIN halfk AS t2 ON t1.ID >= t2.ID AND t1.ID % t2.ID = 0;

CTE不会实现,因为它们只是经过重构以简化读写代码的查询,因此每次调用它们时,都会重新计算它们。

您甚至可以采用一种更有效的方式来创建统计表。

WITH 
E(n) AS(
    SELECT n FROM (VALUES(0),(0),(0),(0),(0),(0),(0),(0),(0),(0))E(n)
),
E3(n) AS(
    SELECT a.n FROM E a, E b, E c
),
cteTally(ID) AS(
    SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) n
    FROM E3
)
SELECT t1.ID, 
    t2.ID, 
    t1.ID / t2.ID AS ID
FROM   cteTally AS t1
INNER JOIN cteTally AS t2 ON t1.ID >= t2.ID AND t1.ID % t2.ID = 0;