web-dev-qa-db-fra.com

Pourquoi y a-t-il une énorme différence de performances entre la table temporaire et la sous-sélection

Il s'agit d'une question sur SQL Server 2008 R2

Je ne suis pas un DBA, de loin. Je suis un Java, qui doit écrire SQL de temps en temps. (Principalement intégré dans le code). Je veux savoir si j'ai fait quelque chose de mal ici, et si oui, ce que je peut faire pour éviter que cela ne se reproduise.

Q1:

SELECT something FROM (SELECT * FROM T1 WHERE condition1) JOIN ...

Le premier trimestre comprend 14 jointures

Q2 est le même que Q1, à une exception près. (SELECT * FROM T1 WHERE condition1) est exécuté avant et stocké dans une table temporaire.

Ce n'est pas une sous-requête corrélée.

Q2:

SELECT * INTO #tempTable FROM T1 WHERE condition1
SELECT something FROM #tempTable  JOIN ...

encore une fois, 14 jointures.

Ce qui m'intrigue maintenant, c'est que Q1 a pris> 2 minutes, (essayé plusieurs fois, pour éviter la mise en cache pour jouer un rôle) tandis que Q2 (les deux requêtes combinées) a pris 2 secondes !!! Ce qui donne?

36
Ward

Pourquoi il n'est pas recommandé d'utiliser des sous-requêtes?

L'optimiseur de base de données (quelle que soit la base de données que vous utilisez) ne peut pas toujours optimiser correctement une telle requête (avec des sous-requêtes). Dans ce cas, le problème de l'optimiseur est de choisir la bonne façon de joindre les jeux de résultats. Il existe plusieurs algorithmes pour joindre deux ensembles de résultats. Le choix de l'algorithme dépend du nombre d'enregistrements contenus dans l'un et dans l'autre jeu de résultats. Si vous joignez deux tables physiques (la sous-requête n'est pas une table physique), la base de données peut facilement déterminer la quantité de données dans deux ensembles de résultats par les statistiques disponibles. Si l'un des ensembles de résultats est une sous-requête, il est très difficile de comprendre le nombre d'enregistrements qu'il renvoie. Dans ce cas, la base de données peut choisir un mauvais plan de jointure de requête, ce qui entraînera une réduction spectaculaire des performances de la requête.

La réécriture de la requête à l'aide de tables temporaires est destinée à simplifier l'optimiseur de base de données. Dans la requête réécrite, tous les jeux de résultats participant aux jointures seront des tables physiques et la base de données déterminera facilement la longueur de chaque jeu de résultats. Cela permettra à la base de données de choisir le plus rapide garanti de tous les plans de requête possibles. De plus, la base de données fera le bon choix quelles que soient les conditions. La requête réécrite avec des tables temporaires fonctionnerait bien sur n'importe quelle base de données, ce qui est particulièrement important dans le développement de solutions portables. De plus, la requête réécrite est plus facile à lire, à comprendre et à déboguer.

Il est entendu que la réécriture de la requête avec des tables temporaires peut entraîner un certain ralentissement en raison de dépenses supplémentaires: création de tables temporaires. Si la base de données ne se trompe pas avec le choix du plan de requête, elle exécutera l'ancienne requête plus rapidement qu'une nouvelle. Cependant, ce ralentissement sera toujours négligeable. En règle générale, la création d'une table temporaire prend quelques millisecondes. Autrement dit, le retard ne peut pas avoir un impact significatif sur les performances du système et peut généralement être ignoré.

Important! N'oubliez pas de créer des index pour les tables temporaires. Les champs d'index doivent inclure tous les champs utilisés dans les conditions de jointure.

48
Karthik AMR

Il y a beaucoup de choses à aborder ici, les index, les plans d'exécution, etc. Tester et comparer les résultats est la voie à suivre.

Vous pouvez jeter un œil aux suspects habituels, aux index. Jetez un œil au plan d'exécution et comparez-les. Assurez-vous que la clause WHERE utilise les bonnes. Assurez-vous que vous utilisez les index sur votre JOINs.

Ces réponses vous aideront certainement beaucoup.

9
Yaroslav