¿Cuáles son más eficaces CTE
o Temporary Tables
?
¿Cuáles son más eficaces CTE
o Temporary Tables
?
Respuestas:
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)
cte vs temporary tables
así 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.
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
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.
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
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).
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.
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.
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.
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
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.
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).
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.
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.
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.
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