Il s'agit d'une simple sélection dans une table temporaire, reliant à gauche une table existante sur sa clé primaire, avec deux sous-sélections utilisant le top 1 faisant référence à la table jointe.
Dans du code:
SELECT
TempTable.Col1,
TempTable.Col2,
TempTable.Col3,
JoinedTable.Col1,
JoinedTable.Col2,
(
SELECT TOP 1
ThirdTable.Col1 -- Which is ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeColumn = JoinedTable.SomeColumn
) as ThirdTableColumn1,
(
SELECT TOP 1
ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
) as ThirdTableColumn2,
FROM
#TempTable as TempTable
LEFT JOIN
JoinedTable
ON (TempTable.PKColumn1 = JoinedTable.PKColumn1 AND
TempTable.PKColumn2 = JoinedTable.PKColumn2)
WHERE
JoinedTable.WhereColumn IN (1, 3)
Ceci est une réplique exacte de ma requête.
Si je supprime les deux sous-sélections, cela fonctionne très bien et rapidement. Avec les deux sous-sélections, j'obtiens environ 100 enregistrements par seconde, ce qui est extrêmement lent pour cette requête car elle devrait renvoyer près d'un million d'enregistrements.
J'ai vérifié si chaque table a une clé primaire, elles le font toutes. Ils ont tous des index et des statistiques pour leurs colonnes importantes, comme ceux de ces clauses WHERE et ceux de la clause JOIN. La seule table sans clé primaire définie ni index est la table temporaire, mais ce n'est pas le problème non plus car ce n'est pas celle liée aux sous-sélections lentes, et comme je l'ai mentionné, sans sous-sélection, elle fonctionne très bien.
Sans ces TOP 1
il renvoie plus d'un résultat et déclenche une erreur.
De l'aide, quelqu'un?
MODIFIER :
Le plan d'exécution m'a donc dit qu'il me manquait un index. Je l'ai créé et recréé certains des autres index. Après un certain temps, le plan d'exécution les utilisait et la requête s'exécute désormais rapidement. Le seul problème est que je ne réussis pas à refaire cela sur un autre serveur, pour la même requête. Donc, ma solution sera d'indiquer quel index SQL Server utilisera.
Je pense que dans une requête d'un million d'enregistrements, vous devez éviter des choses comme OUTER JOINS
. Je vous suggère d'utiliser UNION ALL
Au lieu de LEFT JOIN
. Tant que je pense que CROSS APPLY
Est plus efficace que la sous-requête dans la clause select, je modifierai la requête écrite par Conard Frix, qui je pense est correcte.
maintenant: quand j'ai commencé à modifier votre requête, j'ai remarqué que vous aviez une clause WHERE disant: JoinedTable.WhereColumn IN (1, 3)
. dans ce cas, si le champ est nul, la condition devient fausse. alors pourquoi utilisez-vous LEFT JOIN pendant que vous filtrez les lignes de valeur nulle? remplacez simplement LEFT JOIN
par INNER JOIN
, je vous garantis que cela deviendra plus rapide.
sur INDEX:
veuillez noter que lorsque vous avez un index sur une table, dites
table1(a int, b nvarchar)
et votre index est:
nonclustered index ix1 on table1(a)
et vous voulez faire quelque chose comme ça:
select a,b from table1
where a < 10
dans votre index, vous n'avez pas inclus la colonne b
alors que se passe-t-il?
si sql-server utilise votre index, il devra chercher dans l'index, appelé "Index Seek" puis se référer à la table principale pour obtenir la colonne b
, appelée "Rechercher". Cette procédure peut prendre beaucoup plus de temps que l'analyse de la table elle-même: "Table Scan".
mais sur la base des statistiques dont dispose sql-server, dans de telles situations, il se peut qu'il n'utilise pas du tout votre index.
vérifiez donc tout d'abord le Execution Plan
pour voir si l'index est utilisé.
si oui ou non les deux, modifiez votre index pour inclure toutes les colonnes que vous sélectionnez. dire comme:
nonclustered index ix1 on table1(a) include(b)
dans ce cas, la recherche ne sera pas nécessaire et votre requête s'exécutera beaucoup plus rapidement.
C'est le sous-sélection dans votre sélection de colonne qui provoque le retour lent. Vous devriez essayer d'utiliser vos sous-sélections dans les jointures de gauche, ou utiliser une table dérivée comme je l'ai défini ci-dessous.
tilisation des jointures gauches pour deux instances de la troisième table
SELECT
TempTable.Col1,
TempTable.Col2,
TempTable.Col3,
JoinedTable.Col1,
JoinedTable.Col2,
ThirdTable.Col1 AS ThirdTableColumn1,
ThirdTable2.Col1 AS ThirdTableColumn2
FROM #TempTable as TempTable
LEFT JOIN JoinedTable ON (TempTable.PKColumn1 = JoinedTable.PKColumn2 AND
TempTable.PKColumn 2 = JoinedTable.PKColumn2)
LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
WHERE
JoinedTable.WhereColumn IN (1, 3)
en utilisant une table dérivée
SELECT
TempTable.Col1,
TempTable.Col2,
TempTable.Col3,
DerivedTable.Col1,
DerivedTable.Col2,
DerivedTable.ThirdTableColumn1,
DerivedTable.ThirdTableColumn2
FROM #TempTable as TempTable
LEFT JOIN (SELECT
JoinedTable.PKColumn2,
JoinedTable.Col1,
JoinedTable.Col2,
JoinedTable.WhereColumn,
ThirdTable.Col1 AS ThirdTableColumn1,
ThirdTable2.Col1 AS ThirdTableColumn2
FROM JoinedTable
LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn)
DerivedTable ON (TempTable.PKColumn1 = DerivedTable .PKColumn2 AND
TempTable.PKColumn2 = DerivedTable.PKColumn2)
WHERE
DerivedTable.WhereColumn IN (1, 3)
Essayez plutôt une application croisée
SELECT
TempTable.Col1,
TempTable.Col2,
TempTable.Col3,
JoinedTable.Col1,
JoinedTable.Col2,
ThirdTableColumn1.col1,
ThirdTableColumn2.col1
FROM
#TempTable as TempTable
LEFT JOIN
JoinedTable
ON (TempTable.PKColumn1 = JoinedTable.PKColumn2 AND
TempTable.PKColumn 2 = JoinedTablePKColumn2)
CROSS APPLY
(
SELECT TOP 1
ThirdTable.Col1 -- Which is ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeColumn = JoinedTable.SomeColumn
) as ThirdTableColumn1
CROSS APPLY (
SELECT TOP 1
ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
) as ThirdTableColumn2,
WHERE
JoinedTable.WhereColumn IN (1, 3)
Vous pouvez également utiliser les CTE et row_number ou une requête en ligne à l'aide de MIN
Déplacez les bits JOIN hors de la partie principale de la clause et placez-les en tant que sous-sélection. Le déplacer vers la section WHERE et JOIN vous garantit que vous n'avez pas à sélectionner TOP 1 encore et encore, ce qui, je crois, est la raison de sa lenteur. Si vous souhaitez vérifier cela, examinez le plan d'exécution.
Les références ThirdTable
(sous-sélections dans votre exemple) nécessitent la même attention d'index que toute autre partie d'une requête.
Peu importe si vous utilisez des sous-sélections:
(
SELECT TOP 1
ThirdTable.Col1 -- Which is ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeColumn = JoinedTable.SomeColumn
) as ThirdTableColumn1,
(
SELECT TOP 1
ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
) as ThirdTableColumn2,
JOINT GAUCHE (tel que proposé par John Hartsock):
LEFT JOIN ThirdTable ON ThirdTable.SomeColumn = JoinedTable.SomeColumn
LEFT JOIN ThirdTable ThirdTable2 ON ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
CROSS APPLY (comme proposé par Conrad Frix):
CROSS APPLY
(
SELECT TOP 1
ThirdTable.Col1 -- Which is ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeColumn = JoinedTable.SomeColumn
) as ThirdTableColumn1
CROSS APPLY (
SELECT TOP 1
ThirdTable.Col1 -- Which is also ThirdTable's Primary Key
FROM
ThirdTable
WHERE
ThirdTable.SomeOtherColumn = JoinedTable.SomeColumn
) as ThirdTableColumn2
Vous devez vous assurer que covering indexes
sont définis pour ThirdTable.SomeColumn
et ThirdTable.SomeOtherColumn
et les index sont uniques. Cela signifie que vous devrez qualifier davantage les références ThirdTable
pour éliminer la sélection de plusieurs lignes et améliorer les performances. Le choix de sub selects
, LEFT JOIN
, ou CROSS APPLY
n'aura pas vraiment d'importance tant que vous n'aurez pas amélioré la sélectivité pour ThirdTable.SomeColumn
et ThirdTable.SomeOtherColumn
en incluant plus de colonnes pour garantir une sélectivité unique. D'ici là, je m'attends à ce que vos performances continuent de souffrir.
Le covering index
le sujet est bien présenté par Maziar Taheri; sans répéter son travail, j'insiste sur la nécessité de prendre à cœur l'utilisation des index de recouvrement.
En bref: améliorer la sélectivité pour le ThirdTable.SomeColumn
et ThirdTable.SomeOtherColumn
requêtes (ou jointures) en ajoutant des colonnes dans la table associées pour assurer une correspondance de ligne unique. Si cela n'est pas possible, vous continuerez à souffrir de problèmes de performances car le moteur est occupé à tirer des rangées qui sont ensuite jetées. Cela a un impact sur vos E/S, votre processeur et, en fin de compte, sur le plan d'exécution.