8 votes

Quelle est la différence entre ces requêtes T-SQL utilisant OR ?

J'utilise Microsoft SQL Server 2008 (SP1, x64). J'ai deux requêtes qui font la même chose, du moins je le pense, mais elles ont des plans de requête et des performances complètement différents.

Question 1 :

SELECT c_pk
FROM table_c
WHERE c_b_id IN (SELECT b_id FROM table_b WHERE b_z = 1)
  OR  c_a_id IN (SELECT a_id FROM table_a WHERE a_z = 1)

Question 2 :

SELECT c_pk
FROM table_c
LEFT JOIN (SELECT b_id FROM table_b WHERE b_z = 1) AS b ON c_b_id = b_id
LEFT JOIN (SELECT a_id FROM table_a WHERE a_z = 1) AS a ON c_a_id = a_id
WHERE b_id IS NOT NULL
  OR  a_id IS NOT NULL

La requête 1 est rapide comme je m'y attendais, tandis que la requête 2 est très lente. Le site plans de requêtes sont très différents.

Je voudrais que la requête 2 soit aussi rapide que la requête 1. J'ai un logiciel qui utilise la requête 2, et je ne peux pas le changer en requête 1. Je peux modifier la base de données.

Quelques questions :

  • pourquoi les plans de requêtes sont-ils différents ?
  • Puis-je "apprendre" au serveur SQL que la requête 2 est égale à la requête 1 ?

Toutes les tables ont des clés primaires (en grappe) et des index appropriés sur toutes les colonnes :

CREATE TABLE table_a (
  a_pk   int NOT NULL PRIMARY KEY,
  a_id   int NOT NULL UNIQUE,
  a_z    int
)
GO
CREATE INDEX IX_table_a_z ON table_a (a_z)
GO

CREATE TABLE table_b (
  b_pk   int NOT NULL PRIMARY KEY,
  b_id   int NOT NULL UNIQUE,
  b_z    int
)
GO
CREATE INDEX IX_table_b_z ON table_b (b_z)
GO

CREATE TABLE table_c (
  c_pk   int NOT NULL PRIMARY KEY,
  c_a_id int,
  c_b_id int
)
GO
CREATE INDEX IX_table_c_a_id ON table_c (c_a_id)
GO
CREATE INDEX IX_table_c_b_id ON table_c (c_b_id)
GO

Les tables ne sont pas modifiées après le remplissage initial. Je suis le seul à les interroger. Elles contiennent des millions d'enregistrements (table_a : 5M, table_b : 4M, table_c : 12M), mais utiliser seulement 1% donne des résultats similaires.

Edit : J'ai essayé d'ajouter des FOREIGN KEYs pour c_a_id y c_b_id mais cela n'a rendu la requête 1 plus lente...

J'espère que quelqu'un pourra jeter un coup d'œil à la plans de requêtes et expliquer la différence.

1voto

Adriano Repetti Points 22087

Les joints sont plus lents, laissez-moi vous dire par le design . La première requête utilise une sous-requête (pouvant être mise en cache) pour filtrer les enregistrements afin de produire moins de données (et moins d'accès à chaque table).

Tu as lu ça :

Ce que je veux dire, c'est qu'avec IN, le DB peut faire mieux. optimisations comme la suppression des doublons, l'arrêt à la première correspondance, etc. école souvenirs alors je suis sûr qu'il fera beaucoup mieux). Donc je devinez La question n'est pas de savoir pourquoi QP est différent mais de savoir à quel point les optimisations peuvent être intelligentes et profondes.

0voto

Vitaliy Kalinin Points 1106

Vous comparez des requêtes non équivalentes et vous utilisez la jointure gauche d'une manière assez inhabituelle. En général, si votre intention est de sélectionner toutes les entrées de la table_c qui ont des enregistrements liés dans la table_a ou la table_b, vous devez utiliser l'instruction exists :

SELECT c_pk 
FROM table_c 
WHERE  Exists( 
 SELECT 1
 FROM table_b 
 WHERE b_z = 1 and c_b_id = b_id 
) OR  Exists( 
 SELECT 1 
 FROM table_a 
 WHERE a_z = 1 and c_a_id = a_id
)

0voto

JeffO Points 5393

Puisque vous ne pouvez pas changer la requête, vous pouvez au moins améliorer l'environnement de la requête.

  1. Mettez votre requête en surbrillance, cliquez dessus avec le bouton droit de la souris dans SSMS et sélectionnez "Analyze Query in Database Engine Tuning Advisor".
  2. Exécutez l'analyse pour savoir si vous avez besoin d'index supplémentaires ou de statistiques supplémentaires.
  3. Suivez les conseils de SQL Server.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X