Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Quais são as tabelas de melhor desempenho, CTE ou temporárias?


Depende.

Em primeiro lugar

O que é uma expressão de tabela comum?

Uma CTE (não recursiva) é tratada de maneira muito semelhante a outras construções que também podem ser usadas como expressões de tabela embutidas no SQL Server. Tabelas derivadas, exibições e funções com valor de tabela embutida. Observe que, embora BOL diga que um CTE "pode ​​ser considerado um conjunto de resultados temporário", esta é uma descrição puramente lógica. Na maioria das vezes, não é materializado por direito próprio.

O que é uma tabela temporária?

Esta é uma coleção de linhas armazenadas em páginas de dados em tempdb. As páginas de dados podem residir parcial ou totalmente na memória. Além disso, a tabela temporária pode ser indexada e ter estatísticas de coluna.

Dados de teste
CREATE TABLE T(A INT IDENTITY PRIMARY KEY, B INT , F CHAR(8000) NULL);

INSERT INTO T(B)
SELECT TOP (1000000)  0 + CAST(NEWID() AS BINARY(4))
FROM master..spt_values v1,
     master..spt_values v2;

Exemplo 1
WITH CTE1 AS
(
SELECT A,
       ABS(B) AS Abs_B,
       F
FROM T
)
SELECT *
FROM CTE1
WHERE A = 780



Observe que no plano acima não há menção ao CTE1. Ele apenas acessa as tabelas base diretamente e é tratado da mesma forma que
SELECT A,
       ABS(B) AS Abs_B,
       F
FROM   T
WHERE  A = 780 

Reescrever materializando o CTE em uma tabela temporária intermediária aqui seria extremamente contraproducente.

Materializando a definição do CTE de
SELECT A,
       ABS(B) AS Abs_B,
       F
FROM T

Envolveria copiar cerca de 8 GB de dados em uma tabela temporária, então ainda haveria a sobrecarga de selecionar também.

Exemplo 2
WITH CTE2
     AS (SELECT *,
                ROW_NUMBER() OVER (ORDER BY A) AS RN
         FROM   T
         WHERE  B % 100000 = 0)
SELECT *
FROM   CTE2 T1
       CROSS APPLY (SELECT TOP (1) *
                    FROM   CTE2 T2
                    WHERE  T2.A > T1.A
                    ORDER  BY T2.A) CA 

O exemplo acima leva cerca de 4 minutos na minha máquina.

Apenas 15 linhas dos 1.000.000 de valores gerados aleatoriamente correspondem ao predicado, mas a varredura de tabela cara acontece 16 vezes para localizá-los.



Este seria um bom candidato para materializar o resultado intermediário. A reescrita da tabela temporária equivalente levou 25 segundos.
INSERT INTO #T
SELECT *,
       ROW_NUMBER() OVER (ORDER BY A) AS RN
FROM   T
WHERE  B % 100000 = 0

SELECT *
FROM   #T T1
       CROSS APPLY (SELECT TOP (1) *
                    FROM   #T T2
                    WHERE  T2.A > T1.A
                    ORDER  BY T2.A) CA 



A materialização intermediária de parte de uma consulta em uma tabela temporária às vezes pode ser útil mesmo que seja avaliada apenas uma vez - quando permite que o restante da consulta seja recompilado aproveitando as estatísticas sobre o resultado materializado. Um exemplo dessa abordagem está no artigo SQL Cat When To Break Down Complex Queries.

Em algumas circunstâncias, o SQL Server usará um spool para armazenar em cache um resultado intermediário, por exemplo, de um CTE, e evite ter que reavaliar aquela subárvore. Isso é discutido no item Connect (migrado) Fornecer uma dica para forçar a materialização intermediária de CTEs ou tabelas derivadas. No entanto, nenhuma estatística é criada sobre isso e mesmo que o número de linhas em spool fosse muito diferente do estimado, não é possível que o plano de execução em andamento se adapte dinamicamente em resposta (pelo menos nas versões atuais. Planos de consulta adaptáveis ​​podem se tornar possíveis em o futuro).