SQL CTE против таблицы Temp с точки зрения производительности - PullRequest
1 голос
/ 23 мая 2019

Задача: найти все тройки чисел (n1, n2, n3) от 1 до 512 так, чтобы 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) 

1 Ответ

1 голос
/ 23 мая 2019

Проблема связана с последним запросом и его вторым соединением.Он должен сделать дополнительные расчеты и счетчик строк просто перебраться через крышу.Поняв, что вы пытаетесь сделать, легко избежать вызова 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;
...