Dipende dai dati nelle tue tabelle, dai tuoi indici, .... Difficile dirlo senza poter confrontare i piani di esecuzione / le statistiche io + time.
La differenza che mi aspetterei è il filtraggio aggiuntivo che si verifica prima del JOIN tra le due tabelle. Nel mio esempio, ho modificato gli aggiornamenti in modo da riutilizzare le mie tabelle.
Il piano di esecuzione con "l'ottimizzazione"
Progetto esecutivo
Si vede chiaramente un'operazione di filtro in corso, nei miei dati di test nessun record è stato filtrato e di conseguenza non sono stati apportati miglioramenti.
Il piano di esecuzione, senza "l'ottimizzazione"
Progetto esecutivo
Il filtro è sparito, il che significa che dovremo fare affidamento sul join per filtrare i record non necessari.
Altri motivi
Un altro motivo / conseguenza della modifica della query potrebbe essere la creazione di un nuovo piano di esecuzione durante la modifica della query, che risulta essere più rapido. Un esempio di ciò è il motore che sceglie un altro operatore Join, ma a questo punto è solo un'ipotesi.
MODIFICARE:
Chiarire dopo aver ottenuto i due piani di query:
La query sta leggendo 550M righe dalla tabella grande e le sta filtrando.
Ciò significa che il predicato è quello che esegue la maggior parte del filtro, non il predicato di ricerca. Il risultato è la lettura dei dati, ma molto meno la restituzione.
Far sì che il server sql utilizzi un indice diverso (piano di query) / l'aggiunta di un indice potrebbe risolverlo.
Quindi perché la query di ottimizzazione non presenta questo stesso problema?
Perché viene utilizzato un piano di query diverso, con una scansione anziché una ricerca.
Senza fare alcuna ricerca, ma restituendo solo 4M righe con cui lavorare.
La prossima differenza
Ignorando la differenza di aggiornamento (nulla viene aggiornato sulla query ottimizzata) viene utilizzata una corrispondenza hash sulla query ottimizzata:
Invece di un loop nidificato join sul non ottimizzato:
Un ciclo nidificato è preferibile quando una tabella è piccola e l'altra grande. Dato che entrambi sono vicini alla stessa dimensione, direi che la corrispondenza hash è la scelta migliore in questo caso.
Panoramica
La query ottimizzata
Il piano della query ottimizzata ha parallelismo, utilizza un join di corrispondenza hash e deve eseguire un filtro I / O residuo meno. Utilizza inoltre una bitmap per eliminare i valori chiave che non possono produrre righe di join. (Inoltre non viene aggiornato nulla)
La query
non ottimizzata Il piano della query non ottimizzata non ha parallelismo, utilizza un join loop nidificato e deve eseguire il filtro IO residuo sui record 550M. (Anche l'aggiornamento sta avvenendo)
Cosa potresti fare per migliorare la query non ottimizzata?
Modifica dell'indice per avere first_name e last_name nell'elenco delle colonne chiave:
CREATE INDEX IX_largeTableOfPeople_birth_date_first_name_last_name su dbo.largeTableOfPeople (birth_date, first_name, last_name) include (id)
Ma a causa dell'uso di funzioni e della grande tabella, questa potrebbe non essere la soluzione ottimale.
- Aggiornamento delle statistiche, utilizzando la ricompilazione per provare a ottenere il piano migliore.
- Aggiunta di OPZIONE
(HASH JOIN, MERGE JOIN)
alla query
- ...
Dati di prova + query utilizzate
CREATE TABLE #smallTableOfPeople(importantValue int, birthDate datetime2, first_name varchar(50),last_name varchar(50));
CREATE TABLE #largeTableOfPeople(importantValue int, birth_date datetime2, first_name varchar(50),last_name varchar(50));
set nocount on;
DECLARE @i int = 1
WHILE @i <= 1000
BEGIN
insert into #smallTableOfPeople (importantValue,birthDate,first_name,last_name)
VALUES(NULL, dateadd(mi,@i,'2018-01-18 11:05:29.067'),'Frodo','Baggins');
set @i += 1;
END
set nocount on;
DECLARE @j int = 1
WHILE @j <= 20000
BEGIN
insert into #largeTableOfPeople (importantValue,birth_Date,first_name,last_name)
VALUES(@j, dateadd(mi,@j,'2018-01-18 11:05:29.067'),'Frodo','Baggins');
set @j += 1;
END
SET STATISTICS IO, TIME ON;
SELECT smallTbl.importantValue , largeTbl.importantValue
FROM #smallTableOfPeople smallTbl
JOIN #largeTableOfPeople largeTbl
ON largeTbl.birth_date = smallTbl.birthDate
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.last_name)),RTRIM(LTRIM(largeTbl.last_name))) = 4
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.first_name)),RTRIM(LTRIM(largeTbl.first_name))) = 4
WHERE smallTbl.importantValue IS NULL
-- The following line is "the optimization"
AND LEFT(RTRIM(LTRIM(largeTbl.last_name)), 1) IN ('a','à','á','b','c','d','e','è','é','f','g','h','i','j','k','l','m','n','o','ô','ö','p','q','r','s','t','u','ü','v','w','x','y','z','æ','ä','ø','å');
SELECT smallTbl.importantValue , largeTbl.importantValue
FROM #smallTableOfPeople smallTbl
JOIN #largeTableOfPeople largeTbl
ON largeTbl.birth_date = smallTbl.birthDate
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.last_name)),RTRIM(LTRIM(largeTbl.last_name))) = 4
AND DIFFERENCE(RTRIM(LTRIM(smallTbl.first_name)),RTRIM(LTRIM(largeTbl.first_name))) = 4
WHERE smallTbl.importantValue IS NULL
-- The following line is "the optimization"
--AND LEFT(RTRIM(LTRIM(largeTbl.last_name)), 1) IN ('a','à','á','b','c','d','e','è','é','f','g','h','i','j','k','l','m','n','o','ô','ö','p','q','r','s','t','u','ü','v','w','x','y','z','æ','ä','ø','å')
drop table #largeTableOfPeople;
drop table #smallTableOfPeople;
AND LEFT(TRIM(largeTbl.last_name), 1) BETWEEN 'a' AND 'z' COLLATE LATIN1_GENERAL_CI_AI
dovresti fare quello che vuoi lì senza richiedere di elencare tutti i caratteri e avere un codice che è difficile da leggere