¿Cuál es el algoritmo detrás del operador EXCEPTO?

10

¿Cuál es el algoritmo interno de cómo funciona el operador Excepto bajo las cubiertas en SQL Server? ¿Toma internamente un hash de cada fila y compara?

David Lozinksi realizó un estudio, SQL: la forma más rápida de insertar nuevos registros donde todavía no existe . Mostró que la declaración Except es la más rápida para filas de números grandes; estrechamente vinculado a nuestros resultados a continuación.

Supuesto: creo que la unión izquierda sería la más rápida, ya que solo compara 1 columna, excepto que tomaría más tiempo, ya que tiene que comparar todas las columnas.
Con estos resultados, ahora nuestro pensamiento es ¿Excepto automáticamente e internamente toma un hash de cada fila? Miré el plan de ejecución Excepto y utiliza algo de hash.

Antecedentes: nuestro equipo estaba comparando dos tablas de montón. Tabla A Las filas que no están en la Tabla B, se insertaron en la Tabla B.

Las tablas de montón (del sistema de archivos de texto heredado) no tienen claves / guías / identificadores primarios. Algunas de las tablas tenían filas duplicadas, por lo que encontramos el hash de cada fila, eliminamos duplicados y creamos identificadores de clave principal.

1) Primero ejecutamos una declaración except, excluyendo (columna hash)

select * from TableA
Except
Select * from TableB,

2) Luego ejecutamos una combinación de combinación izquierda entre las dos tablas en el HashRowId

select * 
FROM dbo.TableA A
left join dbo.TableB B
    on A.RowHash =  B.RowHash
where B.Hash is null

Sorprendentemente, el Except Statement Insert fue el más rápido.

Los resultados en realidad se corresponden con los resultados de las pruebas de David Lozinksi

ingrese la descripción de la imagen aquí

Comunidad
fuente
1
Ese no siempre será el caso. Encontré resultados ligeramente diferentes para las lecturas, por ejemplo .
Aaron Bertrand

Respuestas:

10

¿Cuál es el algoritmo interno de cómo funciona el operador Excepto bajo las cubiertas en SQL Server?

No diría que hay un algoritmo interno especial para EXCEPT. Para A EXCEPT B, el motor toma tuplas distintas (si es necesario) de A y resta filas que coinciden en B. No hay operadores de planes de consulta especiales. Lo distinto y la resta se implementan a través de los operadores típicos que vería con una ordenación o una unión. La unión de bucle anidado, la combinación de combinación y la combinación hash son compatibles. Para mostrar esto, arrojaré 15 millones de filas en un par de montones:

DROP TABLE IF EXISTS dbo.TABLE_1;

CREATE TABLE dbo.TABLE_1 (
    COL1 BIGINT NULL,
    COL2 BIGINT NULL
);

INSERT INTO dbo.TABLE_1 WITH (TABLOCK)
SELECT TOP (15000000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), NULL
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
OPTION (MAXDOP 1);


DROP TABLE IF EXISTS dbo.TABLE_2;

CREATE TABLE dbo.TABLE_2 (
    COL1 BIGINT NULL,
    COL2 BIGINT NULL
);

INSERT INTO dbo.TABLE_2 WITH (TABLOCK)
SELECT TOP (15000000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), NULL
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
OPTION (MAXDOP 1);

El optimizador toma las decisiones habituales basadas en los costos sobre cómo implementar la ordenación y la unión. Con dos montones obtengo un hash join como se esperaba. Puede ver otros tipos de unión de forma natural agregando índices o cambiando los datos en cualquiera de las tablas. A continuación, forzo las combinaciones de fusión y bucle con sugerencias solo con fines ilustrativos:

Uniones

¿Toma internamente un hash de cada fila y compara?

No. Se implementa como cualquier otra combinación. Una diferencia es que los NULL se tratan como iguales. Este es un tipo especial de comparación que se puede ver en el plan de ejecución: <Compare CompareOp="IS">. Sin embargo, puede obtener el mismo plan con T-SQL que no incluye la EXCEPTpalabra clave. Por ejemplo, lo siguiente tiene exactamente el mismo plan de EXCEPTconsulta que la consulta que utiliza una combinación hash:

SELECT t1.*
FROM
(
    SELECT DISTINCT COL1, COL2
    FROM dbo.TABLE_1
) t1
WHERE NOT EXISTS (
    SELECT 1
    FROM dbo.TABLE_2 t2
    WHERE (t1.COL1 = t2.COL1 OR (t1.COL1 IS NULL AND t2.COL1 IS NULL))
    AND (t1.COL2 = t2.COL2 OR (t1.COL2 IS NULL AND t2.COL2 IS NULL))
);

Diferenciar el XML de los planes de ejecución solo revela diferencias superficiales en torno a los alias y cosas así. Los residuos de la sonda para las uniones hash hacen la comparación de filas. Son lo mismo para ambas consultas:

ingrese la descripción de la imagen aquí

Si aún tiene dudas, ejecuté PerfView con la frecuencia de muestreo más alta disponible para obtener pilas de llamadas para la consulta con EXCEPTy la consulta sin ella. Aquí están los resultados uno al lado del otro:

ingrese la descripción de la imagen aquí

No hay diferencia real. Las pilas de llamadas allí están presentes hash de referencia debido a las coincidencias de hash en el plan. Si agrego índices para obtener una combinación de fusión natural, no verá ninguna referencia al hash en las pilas de llamadas:

ingrese la descripción de la imagen aquí

Cualquier hash que ocurra se debe a la implementación de operadores de coincidencia hash. No hay nada especial sobre lo EXCEPTque conduce a una comparación de hashing interna especial.

Joe Obbish
fuente