¿Cuáles son las tablas más efectivas, CTE o temporales?


Respuestas:


62

Diría que son conceptos diferentes pero no demasiado diferentes para decir "tiza y queso".

  • Una tabla temporal es buena para su reutilización o para realizar múltiples pases de procesamiento en un conjunto de datos.

  • Un CTE puede usarse para recurrir o simplemente para mejorar la legibilidad.
    Y, como una vista o una función con valores de tabla en línea, también puede tratarse como una macro para expandirse en la consulta principal

  • Una tabla temporal es otra tabla con algunas reglas sobre el alcance

He almacenado procs donde uso ambos (y las variables de tabla también)


12
Las tablas temporales también permiten índices e incluso estadísticas que a veces son necesarias, mientras que un CTE no.
CodeCowboyOrg

9
Creo que esta respuesta no destaca lo suficiente el hecho de que los CTE pueden conducir a un rendimiento terrible. Normalmente me refiero a esta respuesta en dba.stackexchange. Su pregunta aparece en segundo lugar en mi motor de búsqueda si estoy buscando, cte vs temporary tablesasí que en mi humilde opinión, esta respuesta debe resaltar los inconvenientes de CTE mejor. TL; DR de la respuesta vinculada: un CTE nunca debe usarse para el rendimiento. . Estoy de acuerdo con esa cita ya que he experimentado las desventajas de CTE.
TT.

2
@TT. Interesante. Encuentro que los CTE funcionan mucho mejor
Squ1rr3lz

198

Depende.

Ante todo

¿Qué es una expresión de tabla común?

Un CTE (no recursivo) se trata de manera muy similar a otras construcciones que también se pueden usar como expresiones de tabla en línea en SQL Server. Tablas derivadas, Vistas y funciones con valores de tabla en línea. Tenga en cuenta que si bien BOL dice que un CTE "puede considerarse como un conjunto de resultados temporal", esta es una descripción puramente lógica. La mayoría de las veces no está materializado por derecho propio.

¿Qué es una tabla temporal?

Esta es una colección de filas almacenadas en páginas de datos en tempdb. Las páginas de datos pueden residir parcial o totalmente en la memoria. Además, la tabla temporal puede indexarse ​​y tener estadísticas de columna.

Datos de prueba

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;

Ejemplo 1

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

Plan 1

Observe que en el plan anterior no se menciona CTE1. Simplemente accede a las tablas base directamente y se trata igual que

SELECT A,
       ABS(B) AS Abs_B,
       F
FROM   T
WHERE  A = 780 

Reescribir al materializar el CTE en una tabla temporal intermedia aquí sería enormemente contraproducente.

Materializando la definición CTE de

SELECT A,
       ABS(B) AS Abs_B,
       F
FROM T

Implicaría copiar aproximadamente 8 GB de datos en una tabla temporal, entonces todavía queda la sobrecarga de seleccionarlo.

Ejemplo 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 

El ejemplo anterior tarda unos 4 minutos en mi máquina.

Solo 15 filas de los 1,000,000 de valores generados aleatoriamente coinciden con el predicado, pero el costoso escaneo de la tabla ocurre 16 veces para ubicarlos.

ingrese la descripción de la imagen aquí

Este sería un buen candidato para materializar el resultado intermedio. La reescritura de la tabla temporal equivalente tomó 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 

Con plan

La materialización intermedia de parte de una consulta en una tabla temporal a veces puede ser útil incluso si solo se evalúa una vez, cuando permite que el resto de la consulta se vuelva a compilar aprovechando las estadísticas sobre el resultado materializado. Un ejemplo de este enfoque se encuentra en el artículo de SQL Cat Cuándo desglosar consultas complejas .

En algunas circunstancias, SQL Server usará un spool para almacenar en caché un resultado intermedio, por ejemplo, de un CTE, y evitará tener que volver a evaluar ese subárbol. Esto se discute en el elemento Connect (migrado). Proporciona una pista para forzar la materialización intermedia de CTE o tablas derivadas . Sin embargo, no se crean estadísticas sobre esto e incluso si el número de filas en cola fuera muy diferente del estimado, no es posible que el plan de ejecución en curso se adapte dinámicamente en respuesta (al menos en las versiones actuales. Los planes de consulta adaptativa pueden ser posibles en el futuro).


33
Esta es la única respuesta que responde a la pregunta real (que pregunta cuál tiene mejor rendimiento, no cuál es la diferencia o cuál es su favorito), y responde esa pregunta correctamente: "Depende" es la respuesta correcta. También es la única respuesta con datos de apoyo para explicar, varios otros (con un alto número de votos) hacen afirmaciones definitivas de que uno es mejor que el otro sin referencias o pruebas ... Para ser claros, todas esas respuestas también son incorrectas . Porque "depende"
Arkaine55

2
También es una respuesta bien escrita y bien referenciada. En serio de primera clase.
Dan Williams

50

El CTE tiene sus usos: cuando los datos en el CTE son pequeños y hay una fuerte mejora en la legibilidad como en el caso de las tablas recursivas. Sin embargo, su rendimiento ciertamente no es mejor que las variables de tabla y cuando se trata de tablas muy grandes, las tablas temporales superan significativamente al CTE. Esto se debe a que no puede definir índices en un CTE y cuando tiene una gran cantidad de datos que requiere unirse a otra tabla (CTE es simplemente como una macro). Si está uniendo varias tablas con millones de filas de registros en cada una, CTE tendrá un rendimiento significativamente peor que las tablas temporales.


9
He visto esto desde mi propia experiencia. Los CTE funcionan significativamente más lento.
goku_da_master

77
Los CTE también funcionan más lentamente porque los resultados no se almacenan en caché. Entonces, cada vez que usa el CTE, vuelve a ejecutar la consulta, el plan y todo.
goku_da_master

1
Y el motor de db puede elegir volver a ejecutar la consulta no solo en cada referencia, sino en cada fila de la consulta del consumidor, como una subconsulta correlacionada ... siempre debe tener cuidado si no se desea.
Mike M

La tabla temporal se almacena en tempdb en SQL Server, que es un disco pero tiene la ventaja de estar indexada y el optimizador de SQL funciona bien en consultas seleccionadas en ese caso. No estoy seguro de en qué área de base de datos o disco está almacenado el CTE (cuando excede el tamaño de la memoria y está en cola para paginación de E / S), pero nunca se optimiza con el gran volumen de datos. He usado la opción del compilador (con recompilación) a veces para hacerlo más rápido
rmehra76

33

Las tablas temporales siempre están en el disco, por lo tanto, siempre que su CTE pueda mantenerse en la memoria, lo más probable es que sea más rápido (como una variable de tabla también).

Pero, de nuevo, si la carga de datos de su CTE (o variable de tabla temporal) es demasiado grande, también se almacenará en el disco, por lo que no hay grandes beneficios.

En general, prefiero un CTE sobre una tabla temporal, ya que desapareció después de usarlo. No necesito pensar en dejarlo explícitamente ni nada.

Entonces, no hay una respuesta clara al final, pero personalmente, preferiría CTE sobre tablas temporales.


2
En el caso de SQLite y PostgreSQL, las tablas temporales se eliminan automáticamente (generalmente al final de una sesión). Sin embargo, no sé sobre otros DBMS.
Serrano

1
CTE es como una vista temporal. Los datos de AFAIK no se almacenan, por lo que nada puede guardarse en la memoria o almacenarse en el disco. Nota importante: cada vez que utiliza el CTE, la consulta se ejecuta nuevamente.
Rob

1
Personalmente, nunca he visto un CTE que funcione mejor que una tabla Temp para la velocidad. Y la depuración de pozos es mucho más fácil con la tabla temporal
Mark Monforti

7

Entonces, la consulta que me asignaron para optimizar se escribió con dos CTE en el servidor SQL. Tardaba 28 segundos.

Pasé dos minutos convirtiéndolos en tablas temporales y la consulta tomó 3 segundos

Agregué un índice a la tabla temporal en el campo en el que se estaba uniendo y lo bajé a 2 segundos

Tres minutos de trabajo y ahora funciona 12 veces más rápido al eliminar CTE. Personalmente, no usaré CTE siempre que sean más difíciles de depurar también.

Lo loco es que los CTE solo se usaron una vez y aún así ponerles un índice resultó ser un 50% más rápido.


6

CTE no tomará ningún espacio físico. Es solo un conjunto de resultados que podemos usar join.

Las tablas temporales son temporales. Podemos crear índices, restricciones como tablas normales, para eso necesitamos definir todas las variables.

Alcance de la tabla temporal solo dentro de la sesión. EJ: Abra dos ventanas de consulta SQL

create table #temp(empid int,empname varchar)
insert into #temp 
select 101,'xxx'

select * from #temp

Ejecute esta consulta en la primera ventana, luego ejecute la consulta a continuación en la segunda ventana, puede encontrar la diferencia.

select * from #temp

44
>> "es solo un conjunto de resultados que podemos usar join". -> Esto no es exacto. CTE no es un "conjunto de resultados" sino un código en línea. El motor de consulta de SQL Server analiza el código CTE como parte del texto de la consulta y crea un plan de ejecución según corresponda. La idea de que CTE está en línea es la gran ventaja de usar CTE, ya que le permite al servidor crear un "plan de ejecución combinado"
Ronen Ariely

4

He usado ambos, pero en procedimientos complejos masivos siempre he encontrado que las tablas temporales son mejores para trabajar y más metódicas. Los CTE tienen sus usos, pero generalmente con datos pequeños.

Por ejemplo, he creado sprocs que vuelven con resultados de grandes cálculos en 15 segundos, pero convierten este código para que se ejecute en un CTE y lo he visto ejecutar más de 8 minutos para lograr los mismos resultados.


3

Tarde a la fiesta, pero ...

El entorno en el que trabajo es muy limitado, es compatible con algunos productos de proveedores y proporciona servicios de "valor agregado" como informes. Debido a las limitaciones de la política y del contrato, generalmente no se me permite el lujo de un espacio separado de tablas / datos y / o la capacidad de crear código permanente [se pone un poco mejor, dependiendo de la aplicación].

IOW, no puedo generalmente desarrollar un procedimiento almacenado o UDF o tablas temporales, etc. Tengo que hacer todo a través de MI interfaz de aplicación (Crystal Reports - tablas de agregar / vincular, establecer cláusulas de w / en CR, etc. ) Una pequeña gracia salvadora es que Crystal me permite usar COMANDOS (así como Expresiones SQL). Algunas cosas que no son eficientes a través de la capacidad regular de agregar / vincular tablas se pueden hacer definiendo un comando SQL. Utilizo CTE a través de eso y he obtenido muy buenos resultados "remotamente". Los CTE también ayudan con el mantenimiento de informes, ya que no requieren que se desarrolle el código, se entregue a un DBA para compilar, cifrar, transferir, instalar y luego requerir pruebas de múltiples niveles. Puedo hacer CTE a través de la interfaz local.

La desventaja de usar CTEs con CR es que cada informe es separado. Cada CTE debe mantenerse para cada informe. Donde puedo hacer SP y UDF, puedo desarrollar algo que pueda ser usado por múltiples informes, requiriendo solo vincular al SP y pasar parámetros como si estuvieras trabajando en una tabla normal. CR no es realmente bueno para manejar parámetros en comandos SQL, por lo que puede faltar ese aspecto del aspecto CR / CTE. En esos casos, generalmente trato de definir el CTE para que devuelva suficientes datos (pero no TODOS los datos), y luego uso las capacidades de selección de registros en CR para cortar y cortar eso.

Entonces ... mi voto es por los CTE (hasta que obtenga mi espacio de datos).


3

Un uso en el que encontré el rendimiento sobresaliente de CTE fue donde necesitaba unir una consulta relativamente compleja a unas pocas tablas que tenían unos pocos millones de filas cada una.

Usé el CTE para seleccionar primero el subconjunto basado en las columnas indexadas para reducir primero estas tablas a unos pocos miles de filas relevantes cada una y luego unir el CTE a mi consulta principal. Esto redujo exponencialmente el tiempo de ejecución de mi consulta.

Si bien los resultados para el CTE no se almacenan en caché y las variables de tabla podrían haber sido una mejor opción, realmente solo quería probarlos y encontré que se ajustaban al escenario anterior.


Además, creo que dado que solo uso el CTE en la unión, realmente solo ejecuto el CTE una vez en mi consulta, por lo que el almacenamiento en caché de los resultados no fue un problema tan grande a este respecto
compra el

1

Esta es una pregunta realmente abierta, y todo depende de cómo se use y del tipo de tabla temporal (tabla variable o tabla tradicional).

Una tabla temporal tradicional almacena los datos en la base de datos temporal, que ralentiza las tablas temporales; sin embargo, las variables de la tabla no.


1

Acabo de probar esto: tanto CTE como no CTE (donde se escribió la consulta para cada instancia de unión) ambos tomaron ~ 31 segundos. Sin embargo, CTE hizo que el código fuera mucho más legible: lo redujo de 241 a 130 líneas, lo cual es muy agradable. La tabla temporal, por otro lado, la redujo a 132 líneas y tardó CINCO SEGUNDOS en ejecutarse. No es broma. todas estas pruebas se almacenaron en caché; las consultas se ejecutaron varias veces antes.


1

Desde mi experiencia en SQL Server, encontré uno de los escenarios en los que CTE superó la tabla temporal

Necesitaba usar un DataSet (~ 100000) de una consulta compleja solo UNA VEZ en mi Procedimiento almacenado.

  • La tabla temporal estaba causando una sobrecarga en SQL donde mi procedimiento funcionaba lentamente (ya que las tablas temporales son tablas materializadas reales que existen en tempdb y Persist durante la vida de mi procedimiento actual)

  • Por otro lado, con CTE, CTE persiste solo hasta que se ejecuta la siguiente consulta. Entonces, CTE es una práctica estructura en memoria con un alcance limitado. Los CTE no usan tempdb por defecto.

Este es un escenario en el que los CTE realmente pueden ayudar a simplificar su código y superar la tabla temporal. Había usado 2 CTE, algo así como

WITH CTE1(ID, Name, Display) 
AS (SELECT ID,Name,Display from Table1 where <Some Condition>),
CTE2(ID,Name,<col3>) AS (SELECT ID, Name,<> FROM CTE1 INNER JOIN Table2 <Some Condition>)
SELECT CTE2.ID,CTE2.<col3>
FROM CTE2
GO

1
Su respuesta parece ser muy genérica ... ¿Cómo mide esa "tabla temporal con mejor rendimiento CTE"? ¿Tienes algunas medidas de tiempo? En mi opinión, debe editar su respuesta y agregar más detalles.
Il Vic

Sí, tengo medidas de tiempo y un plan de ejecución para respaldar mi declaración.
Amardeep Kohli

No se puede agregar el img para el plan de ejecución debido a los privilegios limitados. Actualizaré los detalles una vez que se resuelva
Amardeep Kohli
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.