sql >> Base de Datos >  >> RDS >> Sqlserver

Eliminación de unión:cuando SQL Server elimina tablas innecesarias

Autor invitado:Bert Wagner (@bertwagner)

La eliminación de combinaciones es una de las muchas técnicas que utiliza el optimizador de consultas de SQL Server para crear planes de consulta eficientes. Específicamente, la eliminación de combinaciones ocurre cuando SQL Server puede establecer la igualdad mediante la lógica de consulta o las restricciones de bases de datos confiables para eliminar las combinaciones innecesarias. Vea una versión completa en video de esta publicación en mi canal de YouTube.

Únase a la eliminación en acción

La forma más sencilla de explicar la eliminación de combinaciones es a través de una serie de demostraciones. Para estos ejemplos, utilizaré la base de datos de demostración de WideWorldImporters.

Para empezar, veremos cómo funciona la eliminación de combinaciones cuando hay una clave externa presente:

SELECT
  	il.*
  FROM
  	Sales.InvoiceLines il
  	INNER JOIN Sales.Invoices i
  		ON il.InvoiceID = i.InvoiceID;

En este ejemplo, solo devolvemos datos de Sales.InvoiceLines donde se encuentra un InvoiceID coincidente en Sales.Invoices. Si bien puede esperar que el plan de ejecución muestre un operador de unión en las tablas Sales.InvoiceLines y Sales.Invoices, SQL Server nunca se molesta en mirar Sales.Invoices en absoluto:

SQL Server evita unirse a la tabla Sales.Invoices porque confía en la integridad referencial mantenida por la restricción de clave externa definida en InvoiceID entre Sales.InvoiceLines y Sales.Invoices; si existe una fila en Sales.InvoiceLines, una fila con el valor coincidente para InvoiceID debe existen en Ventas.Facturas. Y dado que solo devolvemos datos de la tabla Sales.InvoiceLines, SQL Server no necesita leer ninguna página de Sales.Invoices en absoluto.

Podemos verificar que SQL Server está utilizando la restricción de clave externa para eliminar la unión eliminando la restricción y ejecutando nuestra consulta nuevamente:

ALTER TABLE [Sales].[InvoiceLines]  
DROP CONSTRAINT [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices];

Sin información sobre la relación entre nuestras dos tablas, SQL Server se ve obligado a realizar una unión, escaneando un índice en nuestra tabla Sales.Invoices para encontrar los Id. de factura coincidentes.

Desde el punto de vista de E/S, SQL Server debe leer 124 páginas adicionales de un índice en la tabla Sales.Invoices, y eso es solo porque puede usar un índice estrecho (columna única) creado por una restricción de clave externa diferente. Este escenario podría ser mucho peor en tablas más grandes o tablas que no están indexadas adecuadamente.

Limitaciones

Si bien el ejemplo anterior muestra los conceptos básicos de cómo funciona la eliminación de combinaciones, debemos tener en cuenta algunas advertencias.

Primero, volvamos a agregar nuestra restricción de clave externa:

ALTER TABLE [Sales].[InvoiceLines]  
  WITH NOCHECK ADD  CONSTRAINT 
  [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices] FOREIGN KEY([InvoiceID])
REFERENCES [Sales].[Invoices] ([InvoiceID]);

Si ejecutamos nuestra consulta de muestra nuevamente, notaremos que no obtenemos un plan que muestre la eliminación de combinación; en su lugar, obtenemos un plan que escanea nuestras dos tablas unidas.

La razón por la que esto ocurre es porque, cuando volvimos a agregar nuestra restricción de clave externa, SQL Server no sabe si se ha modificado algún dato mientras tanto. Es posible que los datos nuevos o modificados no cumplan con esta restricción, por lo que SQL Server no puede confiar en la validez de nuestros datos:

SELECT
	f.name AS foreign_key_name
	,OBJECT_NAME(f.parent_object_id) AS table_name
	,COL_NAME(fc.parent_object_id, fc.parent_column_id) AS constraint_column_name
	,OBJECT_NAME (f.referenced_object_id) AS referenced_object
	,COL_NAME(fc.referenced_object_id, fc.referenced_column_id) AS referenced_column_name
	,f.is_not_trusted
FROM 
	sys.foreign_keys AS f
	INNER JOIN sys.foreign_key_columns AS fc
		ON f.object_id = fc.constraint_object_id
WHERE 
	f.parent_object_id = OBJECT_ID('Sales.InvoiceLines');

Para restablecer la confianza de SQL Server en esta restricción, debemos verificar su validez:

ALTER TABLE [Sales].[InvoiceLines] 
WITH CHECK CHECK CONSTRAINT [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices];

En tablas grandes, esta operación puede llevar algún tiempo, sin mencionar la sobrecarga de SQL Server al validar estos datos durante cada modificación de inserción/actualización/eliminación en el futuro.

Otra limitación es que SQL Server no puede eliminar tablas unidas cuando la consulta necesita devolver datos de esos posibles candidatos a eliminación:

SELECT
	il.*,
	i.InvoiceDate
FROM
	Sales.InvoiceLines il
	INNER JOIN Sales.Invoices i
		ON il.InvoiceID = i.InvoiceID;

La eliminación de unión no ocurre en la consulta anterior porque estamos solicitando que se devuelvan los datos de Sales.Invoices, lo que obliga a SQL Server a leer los datos de esa tabla.

Finalmente, es importante tener en cuenta que la eliminación de unión no ocurrirá cuando la clave externa tenga varias columnas o si las tablas están en tempdb. Esta última es una de varias razones por las que no debería intentar resolver problemas de optimización copiando sus tablas en tempdb.

Escenarios adicionales

Múltiples tablas

La eliminación de combinaciones no solo se limita a combinaciones internas de dos tablas y tablas con restricciones de clave externa.

Por ejemplo, podemos crear una tabla adicional que haga referencia a nuestra columna Sales.Invoices.InvoiceID:

CREATE TABLE Sales.InvoiceClickTracking
  (
  	InvoiceClickTrackingID bigint IDENTITY PRIMARY KEY,
  	InvoiceID int
  	-- other fields would go here 
  );  
GO
 
ALTER TABLE [Sales].[InvoiceClickTracking]  WITH CHECK 
    ADD  CONSTRAINT [FK_Sales_InvoiceClickTracking_InvoiceID_Sales_Invoices] 
    FOREIGN KEY([InvoiceID])
  REFERENCES [Sales].[Invoices] ([InvoiceID]);

Unir esta tabla a nuestra consulta de muestra original también permitirá que SQL Server elimine nuestra tabla Sales.Invoices:

SELECT 
  	il.InvoiceID,
  	ict.InvoiceID
  FROM
  	Sales.InvoiceLines il
  	INNER JOIN Sales.Invoices i
  		ON il.InvoiceID = i.InvoiceID
  	INNER JOIN Sales.InvoiceClickTracking ict
  		ON i.InvoiceID = ict.InvoiceID;

SQL Server puede eliminar la tabla Sales.Invoices debido a la asociación transitiva entre las relaciones de estas tablas.

Restricciones únicas

En lugar de una restricción de clave externa, SQL Server también realizará la eliminación conjunta si puede confiar en la relación de datos con una restricción única:

ALTER TABLE [Sales].[InvoiceClickTracking] 
  DROP CONSTRAINT [FK_Sales_InvoiceClickTracking_InvoiceID_Sales_Invoices];
  GO
 
ALTER TABLE Sales.InvoiceClickTracking
  ADD CONSTRAINT UQ_InvoiceID UNIQUE (InvoiceID);   
GO 
 
  SELECT 
  	i.InvoiceID
  FROM
  	Sales.InvoiceClickTracking ict
  	RIGHT JOIN Sales.Invoices i
  		ON ict.InvoiceID = i.InvoiceID;

Uniones externas

Siempre que SQL Server pueda inferir restricciones de relación, otros tipos de uniones también pueden experimentar la eliminación de tablas. Por ejemplo:

SELECT
	il.InvoiceID
FROM
	Sales.InvoiceLines il
	LEFT JOIN Sales.Invoices i
		ON il.InvoiceID = i.InvoiceID

Dado que todavía tenemos nuestra restricción de clave externa que impone que cada Id. de factura en Sales.InvoiceLines debe tener un Id. de factura correspondiente en Sales.Invoices, SQL Server no tiene problemas para devolver todo desde Sales.InvoiceLINEs sin la necesidad de unirse a Sales.Invoices:

No se requieren restricciones

Si SQL Server puede garantizar que no necesitará datos de una determinada tabla, potencialmente puede eliminar una unión.

No se produce ninguna eliminación de unión en esta consulta porque SQL Server no puede identificar si la relación entre Sales.Invoices y Sales.InvoiceLines es 1 a 1, 1 a 0 o 1 a muchos. Se ve obligado a leer Sales.InvoiceLines para determinar si se encuentran filas coincidentes:

SELECT
	i.InvoiceID
FROM
	Sales.InvoiceLines il
	RIGHT JOIN Sales.Invoices i
		ON il.InvoiceID = i.InvoiceID;

Sin embargo, si especificamos que queremos un conjunto DISTINTO de i.InvoiceIDs, cada valor único de Sales.Invoices regresa de SQL Server, independientemente de la relación que tengan esas filas con Sales.InvoiceLines.

-- Just to prove no foreign key is at play here
 
ALTER TABLE [Sales].[InvoiceLines] 
DROP CONSTRAINT [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices];
GO
 
-- Our distinct result set
SELECT DISTINCT
	i.InvoiceID
FROM
	Sales.InvoiceLines il
	RIGHT JOIN Sales.Invoices i
		ON il.InvoiceID = i.InvoiceID;

Visualizaciones

Una ventaja de la eliminación de combinaciones es que puede funcionar con vistas, incluso si la consulta de vista subyacente no puede usar la eliminación de combinaciones:

-- Add back our FK
 
ALTER TABLE [Sales].[InvoiceLines]    
WITH CHECK ADD  CONSTRAINT [FK_Sales_InvoiceLines_InvoiceID_Sales_Invoices] 
FOREIGN KEY([InvoiceID])
REFERENCES [Sales].[Invoices] ([InvoiceID]);
GO
 
-- Create our view using a query that cannot use join elimination
CREATE VIEW Sales.vInvoicesAndInvoiceLines
AS
	SELECT
		i.InvoiceID,
		i.InvoiceDate,
		il.Quantity,
		il.TaxRate
	FROM
		Sales.InvoiceLines il
		INNER JOIN Sales.Invoices i
			ON il.InvoiceID = i.InvoiceID;
GO
 
-- Join elimination works because we do not select any 
-- columns from the underlying Sales.Invoices table
 
SELECT Quantity, TaxRate FROM Sales.vInvoicesAndInvoiceLines;

Conclusión

La eliminación de combinaciones es una optimización que realiza SQL Server cuando determina que puede proporcionar un conjunto de resultados preciso sin necesidad de leer los datos de todas las tablas especificadas en la consulta enviada. Esta optimización puede proporcionar mejoras significativas en el rendimiento al reducir la cantidad de páginas que debe leer SQL Server; sin embargo, a menudo se produce a expensas de la necesidad de mantener ciertas restricciones de la base de datos. Podemos refactorizar las consultas para lograr los planes de ejecución más simples que proporciona la eliminación de combinaciones; sin embargo, tener el optimizador de consultas simplifica automáticamente nuestros planes eliminando las combinaciones innecesarias es un buen beneficio.

Nuevamente, lo invito a ver la versión completa en video de esta publicación.

Sobre el autor

Bert es un desarrollador de inteligencia empresarial de Cleveland, Ohio. Le encanta escribir consultas de ejecución rápida y disfruta ayudar a otros a aprender a ser autosuficientes en la resolución de problemas de SQL. Bert bloguea sobre SQL Server en bertwagner.com y crea videos de SQL Server en YouTube en youtube.com/c/bertwagner.