Cela dépend des données de vos tables, de vos index, .... Difficile à dire sans pouvoir comparer les plans d'exécution / les statistiques io + temps.
La différence que j'attendrais est le filtrage supplémentaire qui se produit avant le JOIN entre les deux tables. Dans mon exemple, j'ai changé les mises à jour en sélectionne pour réutiliser mes tables.
Le plan d'exécution avec "l'optimisation"
Plan d'exécution
Vous voyez clairement une opération de filtrage se produire, dans mes données de test, aucun enregistrement n'a été filtré et, par conséquent, aucune amélioration n'a été apportée.
Le plan d'exécution, sans "l'optimisation"
Plan d'exécution
Le filtre a disparu, ce qui signifie que nous devrons compter sur la jointure pour filtrer les enregistrements inutiles.
Autre (s) raison (s)
Une autre raison / conséquence du changement de la requête pourrait être qu'un nouveau plan d'exécution a été créé lors du changement de la requête, ce qui s'avère plus rapide. Un exemple de ceci est le moteur qui choisit un opérateur Join différent, mais c'est juste une supposition à ce stade.
ÉDITER:
Clarification après avoir obtenu les deux plans de requête:
La requête lit les 550 millions de lignes de la grande table et les filtre.
Cela signifie que le prédicat est celui qui effectue la plupart du filtrage, pas le prédicat de recherche. Résultat: les données sont lues, mais beaucoup moins renvoyées.
Faire en sorte que le serveur SQL utilise un index différent (plan de requête) / ajouter un index pourrait résoudre ce problème.
Alors, pourquoi la requête d'optimisation n'a-t-elle pas le même problème?
Parce qu'un plan de requête différent est utilisé, avec une analyse au lieu d'une recherche.
Sans faire aucune recherche, mais en ne retournant que 4 millions de lignes avec lesquelles travailler.
Différence suivante
Sans tenir compte de la différence de mise à jour (rien n'est mis à jour sur la requête optimisée), une correspondance de hachage est utilisée sur la requête optimisée:
Au lieu d'une jointure en boucle imbriquée sur le non optimisé:
Une boucle imbriquée est préférable lorsqu'une table est petite et l'autre grande. Puisqu'ils sont tous deux proches de la même taille, je dirais que la correspondance de hachage est le meilleur choix dans ce cas.
Aperçu
La requête optimisée
Le plan de la requête optimisée présente un parallélisme, utilise une jointure par correspondance de hachage et doit effectuer moins de filtrage d'E / S résiduel. Il utilise également un bitmap pour éliminer les valeurs de clé qui ne peuvent produire aucune ligne de jointure. (De plus, rien n'est mis à jour)
La requête
non optimisée Le plan de la requête non optimisée n'a aucun parallélisme, utilise une jointure en boucle imbriquée et doit effectuer un filtrage d'E / S résiduel sur 550 millions d'enregistrements. (La mise à jour est également en cours)
Que pourriez-vous faire pour améliorer la requête non optimisée?
Modification de l'index pour que prénom et nom de famille figurent dans la liste des colonnes clés:
CRÉER L'INDEX IX_largeTableOfPeople_birth_date_first_name_last_name sur dbo.largeTableOfPeople (date_naissance, prénom, nom_famille) include (id)
Mais en raison de l'utilisation des fonctions et de la taille de ce tableau, ce n'est peut-être pas la solution optimale.
- Mise à jour des statistiques, recompilation pour essayer d'obtenir le meilleur plan.
- Ajout d'OPTION
(HASH JOIN, MERGE JOIN)
à la requête
- ...
Données de test + requêtes utilisées
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
devrait faire ce que vous voulez là sans vous obliger à lister tous les caractères et à avoir un code difficile à lire