Beneficios de la expresión de tabla común (CTE)?

21

De msdn :

A diferencia de una tabla derivada, un CTE puede ser autorreferenciado y puede ser referenciado varias veces en la misma consulta.

Estoy usando mucho los CTE, pero nunca he pensado profundamente en los beneficios de usarlos.

Si hago referencia a un CTE varias veces en la misma consulta:

  • ¿Hay algún beneficio de rendimiento?
  • Si estoy haciendo una autounión, ¿SQL Server escaneará las tablas de destino dos veces?
Royi Namir
fuente
2
Profiler debería decirte si escanea dos veces. En mi humilde opinión, los CTE son impresionantes para la recursividad.
Dan Andrews
3
No hay respuestas difíciles cuando el optimizador de consultas está en juego. Algunas consultas verán beneficios de rendimiento, otras no. A veces, usar una tabla temporal en lugar de un CTE será más rápido, a veces no.

Respuestas:

25

Como regla general, un CTE NUNCA mejorará el rendimiento .

Un CTE es esencialmente una vista desechable. No hay estadísticas adicionales almacenadas, ni índices, etc. Funciona como una abreviatura para una subconsulta.

En mi opinión, pueden ser FÁCILMENTE sobreutilizados (veo mucho uso excesivo en el código en mi trabajo). Aquí hay algunas buenas respuestas, pero si necesita referirse a algo más de una vez, o son más de unos cientos de miles de filas, póngalo en una #temptabla e indícelo.

JNK
fuente
3
De acuerdo. A excepción de los CTE recursivos, simplemente ayudan a la legibilidad
gbn
¿Qué sucede si el CTE devuelve solo unas pocas filas (para que puedan mantenerse en la memoria) que son costosas de calcular (agregación en una tabla grande) y ese resultado se usa más de una vez? Eso debería mejorar el rendimiento, ¿no? (al menos esa es mi experiencia con PostgreSQL y Oracle, donde la tabla temporal se usa muy raramente)
a_horse_with_no_name
2
@a_horse_with_no_name: sería equivalente a convertirlo en una subconsulta. Si el resultado se usa más de una vez en una sola consulta, se reutilizará y no se volverá a calcular. Si se usa en más de una consulta, a CTEes una mala elección ya que los resultados se descartan después de la primera consulta.
JNK
@JNK: gracias. Parece que SQL Server se comporta de manera diferente aquí.
a_horse_with_no_name
Algunas personas consideran que CTE es más legible en ciertas circunstancias FWIW stackoverflow.com/a/11170918/32453
rogerdpack
14

Un lugar además de la recursión donde encuentro que los CTE son increíblemente útiles es al crear consultas de informes complejas. Utilizo una serie de CTE para obtener fragmentos de los datos que necesito y luego los combino en la selección final. Encuentro que son más fáciles de mantener que hacer lo mismo con muchas tablas derivadas o 20 uniones y encuentro que puedo estar más seguro de que devuelve los datos correctos sin efecto de registros múltiples debido a las relaciones de uno a muchos en Todas las diferentes combinaciones. Déjame darte un ejemplo rápido:

;WITH Conferences (Conference_id)
AS 
(select  m.Conference_id
FROM mydb.dbo.Conference m 
WHERE client_id = 10
    and Conference_id in 
            (select Conference_id from mydb.dbo.Expense 
            where amount <>0
            and amount is not null)
     )
--select * from Conferences
,MealEaters(NumberMealEaters, Conference_id, AttendeeType)
AS
(Select count(*) as NumberMealEaters, m.Conference_id,  AttendeeType 
from mydb.dbo.attendance ma 
join Conferences m on m.Conference_id = ma.Conference_id
where (ma.meals_consumed>0 or meals_consumed is null)and attended = 1
group by m.Conference_id)
--select * from MealEaters

,Expenses (Conference_id,expense_date, expenseDescription,  RecordIdentifier,amount)
AS
(select Conference_id,max(expense_date) as Expense_date, expenseDescription,  RecordIdentifier,sum(amount) as amount
    FROM
        (SELECT Conference_id,expense_date,  amount, RecordIdentifier
        FROM mydb.dbo.Expense
        WHERE  amount <> 0 
            and Conference_id IN 
            (SELECT  Conference_id
            FROM mydb.dbo.Conferences ) 
        group by Conference_id, RecordIdentifier) a
)
--select * from Expenses
Select m.Conference_id,me.NumberMealEaters, me.AttendeeType, e.expense_date,         e.RecordIdentifier,amount
from Conferences m
join mealeaters me on m.Conference_id = me.Conference_id
join expenses e on e.Conference_id = m.Conference_id

Entonces, al separar los diferentes fragmentos de información que desea, puede verificar cada parte individualmente (usando las selecciones comentadas, descomentando cada una individualmente y solo corriendo hasta esa selección) y si necesita hacer un cambio en el gasto cálculo (en este ejemplo), es más fácil de encontrar que cuando se mezclan en una consulta masiva. Por supuesto, las consultas de informes reales para las que uso esto son generalmente mucho más complicadas que el ejemplo.

HLGEM
fuente
1
¿Solo por informar consultas? Los sistemas en los que trabajo todos los días tienen consultas de transacciones que son tan complicadas. Curiosamente, nuestras consultas de informes son a menudo algunas de las más simples. (Excluyendo consultas CRUD triviales sin sentido, por supuesto).
Kevin Cathcart
Lo usé como ejemplo porque esos son típicamente los más complicados aquí
HLGEM
+1 a veces una consulta más lógica (legible por humanos) es preferible a una que tenga un mejor rendimiento potencial.
cuando el
Sí. Dado que un CTE generalmente producirá el mismo plan resultante, no veo ninguna razón para crear monstruosidades de múltiples subconsultas horriblemente anidadas, cuando en su lugar podríamos diseñar cada componente visualmente en el orden en que se necesitan. Importo archivos XML y hago varias acrobacias para obtener los datos en la forma correcta, lo que sería insoportable para escribir / leer sin CTE. (¡Algunos de mis códigos antiguos probablemente tienen subconsultas horribles en todas partes!)
underscore_d
0

Como siempre, depende, pero hay casos en los que el rendimiento mejora considerablemente. Lo veo con las instrucciones INSERT INTO SELECT donde usa un CTE para la selección y luego lo usa en INSERT INTO. Es posible que tenga que ver con la configuración de RCSI para la base de datos, pero para aquellos momentos en que se selecciona muy poco, puede ayudar bastante.

Ron S
fuente