Qual è l'algoritmo dietro l'operatore EXCEPT?


10

Qual è l'algoritmo interno di come funziona l' operatore Except sotto le copertine di SQL Server? Prende internamente un hash di ogni riga e confronta?

David Lozinksi ha condotto uno studio, SQL: il modo più veloce per inserire nuovi record in cui uno non esiste già. Ha mostrato che l'istruzione Except è la più veloce per file di grandi dimensioni; strettamente legato ai nostri risultati di seguito.

Presupposto: penso che il join sinistro sarebbe più veloce, in quanto confronta solo 1 colonna, tranne che richiederebbe il tempo più lungo, poiché deve confrontare tutte le colonne.
Con questi risultati, ora il nostro pensiero è Tranne automaticamente e internamente prende un hash di ogni riga? Ho esaminato Tranne il piano di esecuzione e utilizza un po 'di hash.

Contesto: il nostro team stava confrontando due tabelle heap. Tabella A Le righe non nella tabella B sono state inserite nella tabella B.

Le tabelle heap (dal filesystem di testo legacy) non hanno chiavi / guide / identificatori primari. Alcune tabelle avevano righe duplicate, quindi abbiamo trovato l'hash di ogni riga, rimosso i duplicati e creato identificatori di chiave primaria.

1) Per prima cosa abbiamo eseguito un'istruzione di esclusione, escluso (colonna hash)

select * from TableA
Except
Select * from TableB,

2) Quindi abbiamo eseguito un confronto a sinistra tra le due tabelle su HashRowId

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

sorprendentemente l'inserto Except Statement era il più veloce.

I risultati in realtà corrispondono ai risultati dei test di David Lozinksi

inserisci qui la descrizione dell'immagine


Risposte:


10

Qual è l'algoritmo interno di come funziona l'operatore Except sotto le copertine di SQL Server?

Non direi che esiste un algoritmo interno speciale per EXCEPT. Per A EXCEPT B, il motore prende tuple distinte (se necessario) da A e sottrae le righe che corrispondono in B. Non ci sono operatori del piano di query speciali. Il distinto e la sottrazione sono implementati attraverso gli operatori tipici che vedresti con un ordinamento o con un join. Sono supportati l'unione loop nidificata, l'unione unione e hash join. Per dimostrarlo, lancerò 15 milioni di file in un paio di cumuli:

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);

L'ottimizzatore prende le solite decisioni basate sui costi su come implementare l'ordinamento e il join. Con due cumuli ottengo un hash join come previsto. Puoi vedere altri tipi di join naturalmente aggiungendo indici o modificando i dati in entrambe le tabelle. Di seguito forzo l'unione e l'unione di loop con suggerimenti solo a scopo illustrativo:

si unisce

Prende internamente un hash di ogni riga e confronta?

No. È implementato come qualsiasi altro join. Una differenza è che i NULL vengono considerati uguali. Questo è un particolare tipo di paragone che si può vedere nel piano di esecuzione: <Compare CompareOp="IS">. Tuttavia, è possibile ottenere lo stesso piano con T-SQL che non include la EXCEPTparola chiave. Ad esempio, quanto segue ha esattamente lo stesso piano di EXCEPTquery della query che utilizza un hash join:

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))
);

Diffondere l'XML dei piani di esecuzione rivela solo differenze superficiali tra alias e cose del genere. I residui del probe per i join hash eseguono il confronto delle righe. Sono uguali per entrambe le query:

inserisci qui la descrizione dell'immagine

Se hai ancora dubbi, ho eseguito PerfView con la frequenza di campionamento più alta disponibile per ottenere stack di chiamate con la query EXCEPTe senza la query. Ecco i risultati fianco a fianco:

inserisci qui la descrizione dell'immagine

Non c'è vera differenza. Le pile di chiamate lì sono presenti hash di riferimento a causa delle corrispondenze di hash nel piano. Se aggiungo indici per ottenere un join di unione naturale, non vedrai alcun riferimento all'hash nelle pile di chiamate:

inserisci qui la descrizione dell'immagine

Qualsiasi hashing che si verifica è dovuto all'implementazione degli operatori di corrispondenza hash. Non c'è niente di speciale in ciò EXCEPTche porta a un confronto di hashing interno speciale.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.