web-dev-qa-db-fra.com

Jointure externe gauche sur un problème de performances sur deux colonnes

J'utilise une requête SQL semblable au formulaire suivant:

SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.person_uid = table2.person_uid
AND table1.period = table2.period

Et c'est soit trop lent, soit quelque chose d'impasse, car il faut au moins 4 minutes pour revenir. Si je devais le changer en ceci:

SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.person_uid = table2.person_uid
WHERE table1.period = table2.period

alors cela fonctionne bien (bien que ne renvoyant pas le bon nombre de colonnes). Est-il possible d'accélérer le processus?

UPDATE: Il en va de même si je change les deux dernières lignes de la dernière requête:

SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.period = table2.period
WHERE table1.person_uid = table2.person_uid

UPDATE 2: Ce sont en fait des vues que je rejoins. Malheureusement, ils ne figurent pas sur une base de données et ne peuvent donc pas (facilement) modifier l’indexation. Je suis enclin à convenir qu'il s'agit d'un problème d'indexation. J'attendrai un peu avant d'accepter une réponse au cas où il y aurait un moyen magique d'ajuster cette requête que je ne connais pas. Sinon, je vais accepter l'une des réponses actuelles et essayer de trouver un autre moyen de faire ce que je veux faire. Merci pour l'aide de tout le monde jusqu'à présent.

11
Jason Baker

N'oubliez pas que les énoncés 2 et 3 sont différents du premier.

Comment? Eh bien, vous faites une jointure externe gauche et votre clause WHERE n'en tient pas compte (comme le fait la clause ON). Au minimum, essayez:

SELECT col1, col2
FROM table1, table2
WHERE table1.person_uid = table2.person_uid (+)
AND table1.period = table2.period (+)

et voyez si vous rencontrez le même problème de performances.

Quels index avez-vous sur ces tables? Cette relation est-elle définie par une contrainte de clé étrangère?

Ce dont vous avez probablement besoin, c'est d'un index composite à la fois sur person_uid et sur period (sur les deux tables).

16
cletus

Je pense que vous devez comprendre pourquoi les deux dernières ne sont pas la même requête que la première. Si vous faites une jointure gauche, puis ajoutez une clause where référençant un champ de la table du côté droit de la jointure (celle qui peut ne pas toujours avoir un enregistrement correspondant à la première table), alors vous avez effectivement changé la jointure en une jointure interne. Il y a une exception à cela: si vous faites référence à quelque chose comme

SELECT col1, col2
FROM table1
LEFT OUTER JOIN table2
ON table1.person_uid = table2.person_uid
WHERE table2.person_uid is null

Dans ce cas, vous demandez l'enregistrement qui n'a pas d'enregistrement dans le second tableau. Mais à part ce cas particulier, vous modifiez la jointure gauche en une jointure interne si vous référencez un champ dans table2 dans la clause where.

Si votre requête n'est pas assez rapide, je regarderais votre indexation.

5
HLGEM

Tout ce que quelqu'un vous dit en fonction des informations que vous avez fournies est une supposition.

Examinez le plan d'exécution de la requête. Si vous ne voyez pas de raison à la lenteur du plan, postez-le ici.

http://download.Oracle.com/docs/cd/B28359_01/server.111/b28274/ex_plan.htm#PFGRF009

4
Dave Costa

Avez-vous des index de couverture sur person_uid et period pour les deux tables?

Sinon, ajoutez-les et réessayez.

Examinez le plan d'exécution et voyez ce que fait réellement la requête.

Aussi: Quels sont les types de données des champs? Sont-ils les mêmes dans les deux tableaux? Une distribution implicite peut vraiment ralentir les choses.

3
Andrew Rollings

Dans une jointure à gauche, vous scanneriez table1 pour chaque combinaison unique de (personne_uid, période), puis recherchiez table2 pour tous les enregistrements correspondants. Si table2 ne possède pas d'index approprié, vous pouvez également analyser l'intégralité de cette table.

Ma meilleure hypothèse, sans voir de plan d'exécution, est que la première requête (la seule qui semble être correcte) est d'avoir à analyser table table2 ainsi que table1.

Comme vous dites que vous ne pouvez pas modifier les index, vous devez modifier la requête. Autant que je sache, il n'y a qu'une seule alternative réaliste ...

SELECT
   col1, col2
FROM
   table2
FULL OUTER JOIN
   table1
      ON table1.person_uid = table2.person_uid
      AND table1.period = table2.period
WHERE
   table1.person_uid IS NOT NULL

L'espoir ici est que vous analysiez table2 pour chaque combinaison unique de (personne_uid, période), mais que vous utilisiez les index de la table1. (Contrairement à l'analyse de table1 et à l'utilisation d'index sur table2, ce à quoi je m'attendais de votre requête.)

Si table1 ne possède pas d'index approprié, il est très peu probable que vous constatiez une amélioration des performances ...

Dems.

2
MatBailie

Ces tables ont-elles des index sur les colonnes que vous rejoignez? Installez le produit SQLDeveloper gratuit d'Oracle et utilisez-le pour effectuer une "explication" sur cette requête et voir si elle effectue des analyses séquentielles des deux tables.

2
Paul Tomblin

Dans l'une des mises à jour, l'OP indique qu'il interroge en fait des vues et non des tables. Dans ce cas, les performances pourraient être améliorées en interrogeant directement les tables dont il a besoin, en particulier si les vues sont complexes et en se joignant à de nombreuses autres tables ne contenant pas les informations dont il a besoin ou qui appellent des vues. 

0
HLGEM

La syntaxe de jointure ANSI fournit une distinction très claire entre les conditions JOIN et les prédicats FILTER; c'est très important lorsque vous écrivez des jointures externes. À l'aide des tables emp/dept, examinez les résultats des deux jointures externes suivantes.

Q1

SELECT dname, d.deptno, e.ename, e.mgr, d.loc
FROM dept d
LEFT OUTER JOIN emp e
on  d.deptno = e.deptno
and loc in ('NEW YORK','BOSTON' )
;

DNAME              DEPTNO ENAME             MGR LOC
-------------- ---------- ---------- ---------- -------------
ACCOUNTING             10 CLARK            7839 NEW YORK
ACCOUNTING             10 KING                  NEW YORK
ACCOUNTING             10 MILLER           7782 NEW YORK
RESEARCH               20                       DALLAS
SALES                  30                       CHICAGO
OPERATIONS             40                       BOSTON

====

Q2
SELECT dname, d.deptno, e.ename, e.mgr, d.loc
FROM dept d
LEFT OUTER JOIN emp e
on  d.deptno = e.deptno
where loc in ('NEW YORK','BOSTON' )
;

DNAME              DEPTNO ENAME             MGR LOC
-------------- ---------- ---------- ---------- -------------
ACCOUNTING             10 CLARK            7839 NEW YORK
ACCOUNTING             10 KING                  NEW YORK
ACCOUNTING             10 MILLER           7782 NEW YORK
OPERATIONS             40                       BOSTON

Le premier exemple, que montre Q1, est un exemple de "jonction sur une constante". Essentiellement, la condition de filtre est appliquée avant d'effectuer la jointure externe. Vous éliminez donc les lignes, qui sont ensuite rajoutées dans la jointure externe. Ce n'est pas nécessairement faux, mais est-ce la requête que vous avez vraiment demandée? Ce sont souvent les résultats indiqués en Q2 qui sont requis, où le filtre est appliqué après la jointure (externe).

Cela a également une incidence sur les performances, pour les grands ensembles de données. Dans de nombreux cas, l'optimiseur doit résoudre la jointure sur une constante de manière interne en créant une vue latérale, qui ne peut généralement être optimisée que via une jointure de boucle imbriquée plutôt qu'une jointure de hachage.

Pour les développeurs connaissant la syntaxe de jointure externe Oracle, la requête aurait probablement été écrite sous la forme suivante:

SELECT dname, d.deptno, e.ename, e.mgr, d.loc
FROM dept d
        ,emp e
where  d.deptno = e.deptno(+)
and loc in ('NEW YORK','BOSTON' )

Cette requête est sémantiquement équivalente à Q2 ci-dessus. 

Donc, en résumé, il est extrêmement important que vous compreniez la différence entre la clause JOIN et la clause WHERE lorsque vous écrivez des jointures externes ANSI.

0
BobC