web-dev-qa-db-fra.com

LEFT JOIN Significativement plus rapide que INNER JOIN

J'ai une table (MainTable) avec un peu plus de 600 000 enregistrements. Il se joint à lui-même via une 2ème table (JoinTable) dans une relation de type parent/enfant:

SELECT   Child.ID, Parent.ID
FROM     MainTable
AS       Child
JOIN     JoinTable
      ON Child.ID = JoinTable.ID
JOIN     MainTable
AS       Parent
      ON Parent.ID = JoinTable.ParentID
     AND Parent.SomeOtherData = Child.SomeOtherData

Je sais que chaque enregistrement enfant a un enregistrement parent et les données de JoinTable sont précises.

Lorsque j'exécute cette requête, l'exécution prend littéralement quelques minutes. Cependant, si je me joins à Parent à l'aide d'une jointure gauche, il faut <1 seconde pour exécuter:

SELECT   Child.ID, Parent.ID
FROM     MainTable
AS       Child
JOIN     JoinTable
      ON Child.ID = JoinTable.ID
LEFT JOIN MainTable
AS       Parent
      ON Parent.ID = JoinTable.ParentID
     AND Parent.SomeOtherData = Child.SomeOtherData
WHERE    ...[some info to make sure we don't select parent records in the child dataset]...

Je comprends la différence de résultats entre un INNER JOIN et un LEFT JOIN. Dans ce cas, il renvoie exactement le même résultat que chaque enfant a un parent. Si je laisse les deux requêtes s'exécuter, je peux comparer les ensembles de données et ils sont exactement les mêmes.

Pourquoi est-ce qu'un LEFT JOIN tourne tellement plus vite qu'un INNER JOIN?


MISE À JOUR Vérifiez les plans de requête et lorsque vous utilisez une jointure interne, elle commence par l'ensemble de données parent. Lorsque vous effectuez une jointure gauche, elle commence par l'ensemble de données enfant.

Les index qu'il utilise sont tous les mêmes.

Puis-je l'obliger à toujours commencer avec l'enfant? L'utilisation d'une jointure gauche fonctionne, cela ne semble pas correct.


Des questions similaires ont déjà été posées ici, mais aucune ne semble répondre à ma question.

par exemple. la réponse sélectionnée dans INNER JOIN vs performances LEFT JOIN dans SQL Server indique que les jointures gauches sont toujours plus lentes que les jointures internes. L'argument est logique, mais ce n'est pas ce que je vois.

22
Greg

La jointure gauche semble être plus rapide car SQL est obligé de faire d'abord la sélection la plus petite, puis de se joindre à ce petit ensemble d'enregistrements. Pour une raison quelconque, l'optimiseur ne veut pas le faire naturellement.

3 façons de forcer les jointures à se produire dans le bon ordre:

  1. Sélectionnez le premier sous-ensemble de données dans une table temporaire (ou variable de table) puis joignez-vous dessus
  2. Utilisez les jointures gauches (et rappelez-vous que cela pourrait renvoyer des données différentes car il s'agit d'une jointure gauche et non d'une jointure interne)
  3. utilisez le mot-clé FORCE ORDER. Notez que si la taille des tables ou les schémas changent, le plan de requête peut ne pas être correct (voir https://dba.stackexchange.com/questions/45388/forcing-join-order )
13
Greg

Essaye celui-là. Même résultat, approche différente:

SELECT c.ID, p.ID 
FROM
(SELECT   Child.ID, JoinTable.ParentID
FROM     MainTable
AS       Child
JOIN     JoinTable
      ON Child.ID = JoinTable.ID) AS c
INNER JOIN 
(SELECT   Parent.ID, JoinTable.ID
FROM     MainTable
AS       Parent
JOIN     JoinTable
      ON Parent.ID = JoinTable.ParentID
     AND Parent.SomeOtherData = Child.SomeOtherData) AS p
ON c.ParentID = p.ID

Si cela n'aide pas, utilisez cte:

;WITH cte AS
(SELECT   Child.ID, JoinTable.ParentID
FROM     MainTable
AS       Child
JOIN     JoinTable
      ON Child.ID = JoinTable.ID)
SELECT cte.ID, Parent.ID
FROM cte INNER JOIN 
MainTable
AS       Parent
      ON Parent.ID = cte.ParentID
     AND Parent.SomeOtherData = cte.SomeOtherData
2
cha