sql >> Base de Datos >  >> RDS >> Database

Todo lo que necesita saber sobre SQL CTE en un solo lugar

La primera vez que Karl oyó hablar de SQL Server CTE fue cuando buscaba algo para hacer que su código SQL fuera más fácil de ver. Es una especie de dolor de cabeza cuando lo miras. Anton, su colega preocupado, le preguntó sobre CTE. Karl pensó que Anton se refería a su dolor de cabeza. Tal vez lo escuchó todo mal, así que respondió:"Por supuesto que no". Lo gracioso es que se refería a la encefalopatía traumática crónica, también una CTE, una enfermedad neurodegenerativa causada por lesiones repetidas en la cabeza. Pero según la respuesta de Karl, Anton sabía con seguridad que su colega no tenía ni idea de lo que estaba diciendo.

¡Qué manera más loca de presentar CTE! Entonces, antes de subirse al mismo bote, aclaremos, ¿qué es SQL CTE o Common Table Expressions en el mundo SQL?

Puedes leer los conceptos básicos aquí. Mientras tanto, aprenderemos un poco más sobre lo que sucedió en esta historia inusual.

4 cosas básicas sobre CTE en SQL Server

“Un CTE de SQL tiene un nombre”

Anton comenzó con la idea de que los CTE de SQL se denominan temporalmente conjuntos de resultados. Al ser un medio temporal, el CTE tiene un alcance limitado.

"Entonces, ¿es como una subconsulta?" preguntó Karl.

“En cierto modo, sí. Pero no puedes nombrar una subconsulta”, dijo Anton. “Un CTE tiene un nombre muy parecido a una tabla con un nombre. Sin embargo, en lugar de CREAR, usa CON para crearlo”. Luego, escribió la sintaxis en papel:

WITH <cte_name>(<column list>)
AS
(
<inner query defining the CTE>
)
<outer query against CTE>

“CTE desaparece cuando se realiza SELECT”

Anton continuó explicando el alcance de SQL CTE.

“Una tabla temporal puede existir dentro del alcance del procedimiento o globalmente. Pero CTE se ha ido cuando se hace el SELECT”, dijo con una rima. “Lo mismo si lo usa para INSERTAR, ACTUALIZAR o ELIMINAR”, continuó.

“No se puede reutilizar”

“A diferencia de una vista o una tabla temporal, no puede reutilizar SQL CTE. El nombre está ahí, por lo que puede consultarlo en la consulta interna y externa. Pero eso es todo”, dijo Anton.

“Entonces, ¿cuál es el problema de los CTE de SQL?” preguntó Karl.

“Puede hacer que su código sea más legible”

"¿El gran problema?" Anton devolvió la pregunta. “Es que puedes hacer que tu código sea fácilmente legible. ¿No es eso lo que estás buscando?”

"Así es", admitió Karl.

Entonces, ¿cuál es el siguiente paso lógico que debe hacer Karl?

Cosas adicionales sobre CTE en SQL

Al día siguiente, Karl continuó su búsqueda de SQL CTE. Aparte de lo anterior, esto es lo que encontró:

  • SQL CTE puede ser recursivo o no recursivo.
  • No solo SQL Server, sino también MySQL y Oracle respaldan la idea. De hecho, es parte de las especificaciones de SQL-99.
  • Si bien se usa para simplificar el código SQL, no mejora el rendimiento.
  • Y tampoco reemplazará las subconsultas y las tablas temporales. Cada uno tiene su lugar y uso.

En resumen, es otra forma de expresar una consulta .

Pero Karl estaba ansioso por obtener más detalles, por lo que continuó buscando qué funcionaría, qué no y cómo funcionaría en comparación con las subconsultas y las tablas temporales.

¿Qué funcionará en SQL Server CTE?

Profundizando más para desentrañar más sobre CTE, Karl enumeró a continuación lo que aceptaría SQL Server. Echa un vistazo a sus estudios también.

Asignar alias de columna en línea o externa

Los CTE de SQL admiten dos formas de asignar alias de columna. El primero es el formulario en línea, como el siguiente ejemplo:

-- Use an Inline column alias

USE AdventureWorks
GO;

WITH Sales_CTE
AS  
(  
	SELECT SalesPersonID, COUNT(*) AS NumberOfOrders
	FROM Sales.SalesOrderHeader  
	WHERE SalesPersonID IS NOT NULL  
	GROUP BY SalesPersonID  
)
SELECT
 a.SalesPersonID
,a.NumberOfOrders
FROM Sales_CTE a

El código anterior usa un alias de columna dentro de la definición de CTE cuando se asigna dentro de la instrucción SELECT. ¿Notó el COUNT(*) AS NumberOfOrders ? Ese es el formulario en línea.

Ahora, otro ejemplo es la forma externa:

-- Use an external column alias

USE AdventureWorks
GO;

WITH Sales_CTE(SalesPersonID, NumberOfOrders) 
AS  
(  
	SELECT SalesPersonID, COUNT(*)
	FROM Sales.SalesOrderHeader  
	WHERE SalesPersonID IS NOT NULL  
	GROUP BY SalesPersonID  
)
SELECT
 a.SalesPersonID
,a.NumberOfOrders
FROM Sales_CTE a

Las columnas también se pueden definir entre paréntesis después de configurar el nombre de CTE. Observe el CON Ventas_CTE (SalesPersonID, NumberOfOrders) .

CTE en SQL precede a SELECCIONAR, INSERTAR, ACTUALIZAR o ELIMINAR

El siguiente elemento trata sobre el consumo del CTE. El primer ejemplo común es cuando precede a una instrucción SELECT.

-- List down all Salespersons with their all-time number of orders
USE AdventureWorks
GO;

WITH Sales_CTE (SalesPersonID, NumberOfOrders)  
AS  
(  
	SELECT SalesPersonID, COUNT(*)  
	FROM Sales.SalesOrderHeader  
	WHERE SalesPersonID IS NOT NULL  
	GROUP BY SalesPersonID  
)
SELECT
 a.SalesPersonID
,CONCAT(P.LastName,', ',P.FirstName,' ',P.MiddleName) AS SalesPerson
,a.NumberOfOrders
FROM Sales_CTE a
INNER JOIN Person.Person p ON a.SalesPersonID = p.BusinessEntityID

¿Qué muestra este ejemplo?

  • Ventas_CTE – el nombre del CTE.
  • (ID de vendedor, número de pedidos) – la definición de las columnas CTE.
  • SELECCIONE SalesPersonID, COUNT(*) FROM Sales.SalesOrderHeader DONDE SalesPersonID NO ES NULO GRUPO POR SalesPersonID – el SELECT interno que define el CTE.
  • SELECT a.SalesPersonID, CONCAT(P.LastName,’, ‘,P.FirstName,’ ‘,P.MiddleName) AS SalesPerson – la consulta externa que consume el CTE. Este ejemplo usa un SELECT para consumir el CTE.
  • DE Ventas_CTE a – la referencia de la consulta exterior al CTE.

Además de SELECCIONAR, también funciona con INSERTAR, ACTUALIZAR y ELIMINAR. Aquí hay un ejemplo del uso de INSERTAR:

-- add a 10% increase to Employee 16 after 1 year from the previous increase.
USE AdventureWorks
GO;

WITH LatestEmployeePay
AS
(
    SELECT TOP 1
     eph.BusinessEntityID
    ,eph.RateChangeDate
    ,eph.Rate
    ,eph.PayFrequency
    FROM HumanResources.EmployeePayHistory eph 
    WHERE eph.BusinessEntityID = 16
    ORDER BY eph.RateChangeDate DESC
)
INSERT INTO HumanResources.EmployeePayHistory
SELECT
 BusinessEntityID
,DATEADD(d,365,RateChangeDate)
,(Rate * 0.1) + Rate
,PayFrequency
,GETDATE()
FROM LatestEmployeePay

En la lista anterior, el CTE recupera el último pago del empleado 16. El conjunto de resultados del CTE se usa para insertar un nuevo registro en EmployeePayHistory . Karl registró sus hallazgos con elegancia. Además, usó ejemplos apropiados.

Definir múltiples CTE en 1 consulta

Así es. Karl encontró que múltiples CTE son posibles en 1 consulta. He aquí un ejemplo:

-- Get the present and previous rate of employee 16
USE AdventureWorks
GO;

WITH LatestEmployeePay
AS
(
    SELECT TOP 1
     eph.BusinessEntityID
    ,eph.RateChangeDate
    ,eph.Rate
    FROM HumanResources.EmployeePayHistory eph 
    WHERE eph.BusinessEntityID = 16
    ORDER BY eph.RateChangeDate DESC
),
PreviousEmployeePay AS
(
    SELECT TOP 1
     eph.BusinessEntityID
    ,eph.RateChangeDate
    ,eph.Rate
    FROM HumanResources.EmployeePayHistory eph
    INNER JOIN LatestEmployeePay lep 
      ON eph.BusinessEntityID = lep.BusinessEntityID
    WHERE eph.BusinessEntityID = 16
      AND eph.RateChangeDate < lep.RateChangeDate
    ORDER BY eph.RateChangeDate DESC
)
SELECT
 a.BusinessEntityID
,a.Rate
,a.RateChangeDate
,b.Rate AS PreviousRate
FROM LatestEmployeePay a
INNER JOIN PreviousEmployeePay b 
    ON a.BusinessEntityID = b.BusinessEntityID

El código anterior usa 2 CTE en una consulta, a saber, LatestEmployeePay y PreviousEmployeePay .

Hacer referencia a un CTE varias veces

Hay más en el ejemplo anterior. Observe también que puede INNER JOIN del primer CTE al segundo CTE. Finalmente, la consulta externa puede unir los 2 CTE. El LatestEmployeePay se ha mencionado dos veces.

Pasar Argumentos a un CTE de SQL

Los argumentos, como las variables, se pueden pasar a lo largo de un CTE:

DECLARE @SalesPersonID INT = 275;

WITH Sales_CTE
AS  
(  
	SELECT SalesPersonID, COUNT(*) AS NumberOfOrders
	FROM Sales.SalesOrderHeader 
	WHERE SalesPersonID = @SalesPersonID  
	GROUP BY SalesPersonID  
)  
SELECT SalesPersonID, NumberOfOrders
FROM Sales_CTE

El código anterior comienza declarando y configurando una variable @SalesPersonID . Luego, el valor se pasa al CTE para filtrar el resultado.

Usar en un CURSOR

Un cursor SQL puede usar una instrucción SELECT y recorrer los resultados. Además, se puede usar un SQL CTE con él:

DECLARE @SalesPersonID INT
DECLARE @NumberofOrders INT

DECLARE sales_cursor CURSOR FOR
    WITH Sales_CTE (SalesPersonID, NumberOfOrders)  
	AS  
	(  
		SELECT SalesPersonID, COUNT(*)  
		FROM Sales.SalesOrderHeader  
		WHERE SalesPersonID IS NOT NULL  
		GROUP BY SalesPersonID  
	)  
	SELECT salespersonid, numberoforders
	FROM Sales_CTE; 
OPEN sales_cursor
FETCH NEXT FROM sales_cursor INTO @SalesPersonID, @NumberofOrders
WHILE @@FETCH_STATUS = 0  
BEGIN
	PRINT 'SalesPersonID: ' + CAST(@SalesPersonID AS VARCHAR)
	PRINT '# of Orders: ' + CAST(@NumberofOrders AS VARCHAR)
	FETCH NEXT FROM sales_cursor  INTO @SalesPersonID, @NumberofOrders
END
CLOSE sales_cursor
DEALLOCATE sales_cursor;

Usar una tabla temporal en un CTE recursivo

CTE recursivo usa un miembro de anclaje y un miembro recursivo dentro de la definición de CTE. Ayuda a obtener jerarquías dentro de una tabla. SQL CTE también puede usar una tabla temporal para este propósito. Vea un ejemplo a continuación:

-- Create a Crew table.  
CREATE TABLE #EnterpriseDSeniorOfficers  
(  
CrewID SMALLINT NOT NULL,  
FirstName NVARCHAR(30)  NOT NULL,  
LastName  NVARCHAR(40) NOT NULL,  
CrewRank NVARCHAR(50) NOT NULL,  
HigherRankID INT NULL,  
 CONSTRAINT PK_CrewID PRIMARY KEY CLUSTERED (CrewID ASC)   
);  
-- Populate the table with values.  
INSERT INTO #EnterpriseDSeniorOfficers VALUES   
 (1, N'Jean-Luc', N'Picard', N'Captain',NULL)  
,(2, N'William', N'Riker', N'First Officer',1)  
,(3, N'Data', N'', N'Second Officer',1)  
,(4, N'Worf', N'', N'Chief of Security',1)  
,(5, N'Deanna', N'Troi', N'Ship Counselor',1)  
,(6, N'Beveryly', N'Crusher', N'Chief Medical Officer',1)  
,(7, N'Geordi', N'LaForge', N'Chief Engineer',1);  

WITH DirectReports(HigherRankID, CrewID, Title, CrewLevel) AS   
(  
    SELECT HigherRankID, CrewID, CrewRank, 0 as CrewLevel
    FROM #EnterpriseDSeniorOfficers
    WHERE HigherRankID IS NULL  
    UNION ALL  
    SELECT e.HigherRankID, e.CrewID, e.CrewRank, CrewLevel + 1  
    FROM #EnterpriseDSeniorOfficers AS e  
        INNER JOIN DirectReports AS d  
        ON e.HigherRankID = d.CrewID   
)  
SELECT HigherRankID, CrewID, Title, CrewLevel   
FROM DirectReports  
OPTION (MAXRECURSION 2)
ORDER BY HigherRankID;  

DROP TABLE #EnterpriseDSeniorOfficers

Karl explicó diseccionando este CTE. Así es como funciona.

El miembro ancla es la primera instrucción SELECT con el nivel de tripulación cero (0):

SELECT HigherRankID, CrewID, CrewRank, 0 as CrewLevel
 FROM #EnterpriseDSeniorOfficers
 WHERE HigherRankID IS NULL

Este miembro ancla obtiene el nodo raíz de la jerarquía. La cláusula WHERE especifica que el nivel raíz (HigherRankID IS NULL ).

El miembro recursivo que obtendrá los nodos secundarios se extrae a continuación:

SELECT e.HigherRankID, e.CrewID, e.CrewRank, CrewLevel + 1  
FROM #EnterpriseDSeniorOfficers AS e  
INNER JOIN DirectReports AS d  
        ON e.HigherRankID = d.CrewID

También hay una OPCIÓN (MAXRECURSION 2) utilizado en la consulta externa. Los CTE recursivos pueden volverse problemáticos cuando la consulta recursiva genera un bucle infinito. MAXRECURSION 2 evita este lío:limita el ciclo a solo 2 recursiones.

Esto termina la lista de Karl de lo que funcionará. Sin embargo, no todo lo que pensamos puede funcionar. La siguiente sección discutirá los hallazgos de Karl sobre estos.

¿Qué no funcionará en SQL CTE?

Aquí tenemos una lista de cosas que generarán un error al usar SQL CTE.

Sin punto y coma antes del CTE de SQL

Si hay una declaración antes del CTE, esa declaración debe terminar con un punto y coma. La cláusula WITH puede funcionar para otros propósitos, como en una sugerencia de tabla, por lo tanto, el punto y coma eliminará la ambigüedad. La declaración proporcionada a continuación provocará un error:

DECLARE @SalesPersonID INT

SET @SalesPersonID = 275

WITH Sales_CTE
AS  
(  
	SELECT SalesPersonID, COUNT(*) AS NumberOfOrders
	FROM Sales.SalesOrderHeader 
	WHERE SalesPersonID = @SalesPersonID  
	GROUP BY SalesPersonID  
)  
SELECT SalesPersonID, NumberOfOrders
FROM Sales_CTE

Los novatos que solían no terminar las declaraciones con un punto y coma encuentran este error:

Columnas sin nombre

“¿Olvidó poner un alias de columna? Entonces, te espera otro error”. Karl dijo esto en su artículo y también proporcionó un código de muestra que comparto a continuación:

DECLARE @SalesPersonID INT

SET @SalesPersonID = 275;

WITH Sales_CTE
AS  
(  
	SELECT SalesPersonID, COUNT(*)
	FROM Sales.SalesOrderHeader 
	WHERE SalesPersonID = @SalesPersonID  
	GROUP BY SalesPersonID  
)  
SELECT SalesPersonID, NumberOfOrders
FROM Sales_CTE

Luego, eche un vistazo al mensaje de error:

Nombres de columna duplicados

Otro error relacionado con el n. ° 2 anterior es usar el mismo nombre de columna dentro del CTE. Puede salirse con la suya en una declaración SELECT normal, pero no con un CTE. Karl tenía otro ejemplo:

WITH Sales_CTE
AS  
(  
	SELECT SalesPersonID AS col1, COUNT(*) AS col1
	FROM Sales.SalesOrderHeader 
	GROUP BY SalesPersonID  
)  
SELECT *
FROM Sales_CTE

Cláusula ORDER BY sin TOP ni OFFSET-FETCH

SQL estándar no permite ORDER BY en expresiones de tabla cuando lo usamos para ordenar los conjuntos de resultados. Sin embargo, si se usa TOP o OFFSET-FETCH, ORDER BY se convierte en una ayuda de filtrado.

Aquí está el ejemplo de Karl usando un ORDEN POR:

WITH LatestEmployeePay
AS
(
    SELECT
     eph.BusinessEntityID
    ,eph.RateChangeDate
    ,eph.Rate
    ,eph.PayFrequency
    FROM HumanResources.EmployeePayHistory eph 
    WHERE eph.BusinessEntityID = 16
    ORDER BY eph.RateChangeDate DESC
)
INSERT INTO HumanResources.EmployeePayHistory
SELECT
 BusinessEntityID
,DATEADD(d,365,RateChangeDate)
,(Rate * 0.1) + Rate
,PayFrequency
,GETDATE()
FROM LatestEmployeePay

Tenga en cuenta que es el mismo ejemplo que teníamos antes, pero esta vez no se especifica TOP. Mira el error:

El número de columnas no es el mismo que el de la definición de la lista de columnas

Karl usó el mismo ejemplo CTE recursivo, pero eliminó una columna en el miembro ancla:

WITH DirectReports(HigherRankID, CrewID, Title, CrewLevel) AS   
(  
    SELECT HigherRankID, CrewID
    FROM #EnterpriseDSeniorOfficers
    WHERE HigherRankID IS NULL  
    UNION ALL  
    SELECT e.HigherRankID, e.CrewID, e.CrewRank, CrewLevel + 1  
    FROM #EnterpriseDSeniorOfficers AS e  
        INNER JOIN DirectReports AS d  
        ON e.HigherRankID = d.CrewID   
)  
SELECT HigherRankID, CrewID, Title, CrewLevel   
FROM DirectReports  
ORDER BY HigherRankID;

El código anterior usa una lista de tres columnas con un formulario externo, pero el miembro ancla solo tenía 2 columnas. No esta permitido. Cometer un error como este provocará un error:

Otras cosas no permitidas en un CTE

Además de la lista anterior, aquí hay algunos hallazgos más de Karl que desencadenan errores si lo usa por error en un CTE de SQL.

  • Uso de la cláusula SELECT INTO, OPTION con sugerencias de consulta y uso de FOR BROWSE.
  • Diferentes datos y tipos en las columnas de miembros ancla en comparación con las columnas de miembros recursivos.
  • Tener las siguientes palabras clave en un miembro recursivo de un CTE recursivo:
    • ARRIBA
    • UNIÓN EXTERNA (pero la UNIÓN INTERNA está permitida)
    • AGRUPAR POR y TENER
    • Subconsultas
    • SELECCIONE DISTINTO
  • Uso de la agregación escalar.
  • Uso de subconsultas en un miembro recursivo.
  • Tener CTE de SQL anidados.

SQL CTE frente a tablas temporales frente a subconsultas

A veces, puede reescribir un CTE de SQL mediante una subconsulta. Además, en ocasiones, puede desglosar un CTE de SQL mediante tablas temporales por motivos de rendimiento. Como cualquier otra consulta, debe verificar el Plan de ejecución real y ESTADÍSTICAS IO para saber qué opción tomar. SQL CTE puede ser agradable a la vista, pero si se topa con un muro de rendimiento, use otra opción . Ninguna opción es más rápida que la otra.

Examinemos tres consultas de los artículos de Karl que arrojan los mismos resultados. Uno usa un CTE de SQL, el otro usa una subconsulta y el tercero usa una tabla temporal. Para simplificar, Karl utilizó un conjunto de resultados pequeño.

El código y el conjunto de resultados

Comenzó usando múltiples CTE de SQL.

WITH LatestEmployeePay
AS
(
    SELECT TOP 1
     eph.BusinessEntityID
    ,eph.RateChangeDate
    ,eph.Rate
    FROM HumanResources.EmployeePayHistory eph 
    WHERE eph.BusinessEntityID = 16
    ORDER BY eph.RateChangeDate DESC
),
PreviousEmployeePay AS
(
    SELECT TOP 1
     eph.BusinessEntityID
    ,eph.RateChangeDate
    ,eph.Rate
    FROM HumanResources.EmployeePayHistory eph
    INNER JOIN LatestEmployeePay lep 
        ON eph.BusinessEntityID = lep.BusinessEntityID
    WHERE eph.BusinessEntityID = 16
        AND eph.RateChangeDate < lep.RateChangeDate
    ORDER BY eph.RateChangeDate DESC
)
SELECT
 a.BusinessEntityID
,a.Rate
,a.RateChangeDate
,b.Rate AS PreviousRate
FROM LatestEmployeePay a
INNER JOIN PreviousEmployeePay b 
    ON a.BusinessEntityID = b.BusinessEntityID

La siguiente es una subconsulta. Como notará, el CTE parece modular y legible, pero la siguiente subconsulta es más corta:

SELECT TOP 1
 eph.BusinessEntityID
,eph.Rate
,eph.RateChangeDate
,(SELECT TOP 1 eph1.Rate FROM HumanResources.EmployeePayHistory eph1
  WHERE eph1.BusinessEntityID=16
    AND eph1.RateChangeDate < eph.RateChangeDate
  ORDER BY eph1.RateChangeDate DESC) AS PreviousRate
FROM HumanResources.EmployeePayHistory eph
WHERE eph.BusinessEntityID = 16
ORDER BY eph.RateChangeDate DESC;

Karl también intentó dividirlo en pequeños fragmentos de código y luego combinar los resultados usando tablas temporales.

SELECT TOP 1
 eph.BusinessEntityID
,eph.RateChangeDate
,eph.Rate
INTO #LatestPay
FROM HumanResources.EmployeePayHistory eph 
WHERE eph.BusinessEntityID = 16
ORDER BY eph.RateChangeDate DESC

SELECT TOP 1
 eph.BusinessEntityID
,eph.RateChangeDate
,eph.Rate
INTO #PreviousPay
FROM HumanResources.EmployeePayHistory eph
INNER JOIN #LatestPay lep 
    ON eph.BusinessEntityID = lep.BusinessEntityID
WHERE eph.BusinessEntityID = 16
    AND eph.RateChangeDate < lep.RateChangeDate
ORDER BY eph.RateChangeDate DESC

SELECT
 a.BusinessEntityID
,a.Rate
,a.RateChangeDate
,b.Rate AS PreviousRate
FROM #LatestPay a
INNER JOIN #PreviousPay b 
    ON a.BusinessEntityID = b.BusinessEntityID

DROP TABLE #LatestPay
DROP TABLE #PreviousPay

Eche un vistazo a los resultados de estas tres formas de obtener los pagos actuales y anteriores del Empleado 16. Son lo mismo:

Las lecturas lógicas

¿Qué consume la mayoría de los recursos de SQL Server? Veamos las ESTADÍSTICAS IO. Karl usó estadísticasparser.com para dar un formato agradable a los resultados, bien por nosotros.

La Figura 7 muestra las lecturas lógicas del uso de un CTE frente al uso de una subconsulta:

A continuación, vea el total de lecturas lógicas al dividir el código en pequeños fragmentos utilizando tablas temporales:

Entonces, ¿qué consume más recursos? Karl los clasificó para mayor claridad.

  1. Subconsulta:4 lecturas lógicas (¡GANADOR!).
  2. SQL CTE:6 lecturas lógicas.
  3. Tablas temporales:8 lecturas lógicas.

En este ejemplo, la más rápida será la subconsulta.

El Plan de Ejecución Real

Para dar sentido a las lecturas lógicas que obtuvimos de STATISTICS IO, Karl también verificó el Plan de ejecución real. Partió del CTE:

Podemos observar algunas cosas de este plan:

  • LatestEmployeePay CTE se evaluó dos veces cuando se usó en la consulta externa y cuando se unió a PreviousEmployeePay . Entonces, vemos 2 nodos TOP para esto.
  • Vemos PreviousEmployeePay evaluado una vez.

Luego, observe el plan de ejecución real de la consulta con una subconsulta:

Hay algunas cosas obvias aquí:

  • El plan es más simple.
  • Es más simple porque la subconsulta para obtener el último pago se evalúa solo una vez.
  • No es de extrañar que las lecturas lógicas sean menores en comparación con las lecturas lógicas de la consulta con un CTE.

Finalmente, aquí está el Plan de Ejecución Real cuando Karl usó tablas temporales:

Dado que es un lote de tres declaraciones, también vemos tres diagramas en el plan. Los tres son simples, pero el plan colectivo no es tan simple como el plan de la consulta con la subconsulta.

Las estadísticas de tiempo

Con la solución dbForge Studio para SQL Server, puede comparar las estadísticas de tiempo en Query Profiler. Para eso, mantenga presionada la tecla CTRL y haga clic en los nombres de los resultados de cada consulta en el historial de ejecución:

Las estadísticas de tiempo son coherentes con las lecturas lógicas y el plan de ejecución real. La subconsulta se ejecutó más rápido (88 ms). Le sigue el CTE (199ms). El último es el uso de tablas temporales (536ms).

Entonces, ¿qué hemos aprendido de Karl?

En este ejemplo en particular, vimos que usar una subconsulta es mucho mejor cuando queremos la opción más rápida. Sin embargo, puede ser una historia diferente si el conjunto de requisitos no es tal.

Revise siempre las ESTADÍSTICAS IO y los Planes de Ejecución Real para saber qué técnica utilizar.

Puntos para llevar

Espero que no te hayas golpeado la cabeza contra la pared para entender qué es un CTE (Common Table Expressions). De lo contrario, es posible que tenga una CTE (encefalopatía traumática crónica). Bromas aparte, ¿qué revelamos?

  • Las expresiones de tabla comunes son conjuntos de resultados con nombres temporales. Está más cerca de una subconsulta en comportamiento y alcance, pero es más clara y modular. También tiene un nombre.
  • SQL CTE es para simplificar el código, no para acelerar la consulta.
  • Siete cosas que hemos aprendido funcionarán en SQL CTE.
  • Cinco cosas desencadenarán un error.
  • También hemos confirmado una vez más que STATISTICS IO y Actual Execution Plan siempre le darán un mejor juicio. Es cierto si compara un CTE con una subconsulta y un lote usando tablas temporales.

¿Lo disfrute? Luego, los botones de las redes sociales están esperando ser presionados. ¡Elige tu favorito y comparte el amor!

Leer también

Cómo CTE puede ayudar a escribir consultas complejas y potentes:una perspectiva de rendimiento