2
votes

Performances des requêtes SQL avec la clause OR

Cette requête SQL prend 10 secondes:

SELECT * FROM A a
JOIN B b on a.idB = b.idB
JOIN C c on b.idC = c.idC
JOIN D d on c.idD = d.idD
JOIN E e  ON (e.perimeterId = a.idX AND e.level = X) 

Modification de la clause OR avec CASE , la requête prend 3 secondes:

SELECT * FROM A a
JOIN B b on a.idB = b.idB
JOIN C c on b.idC = c.idC
JOIN D d on c.idD = d.idD
JOIN E e  ON (
               CASE e.level
                    when 3 then a.idA
                    when 2 then b.idB
                    when 1 then c.idC
                    when 0 then d.idD
               END
             ) = e.perimeterId 

Si j'exécute la requête avec une clause OR séparée, les requêtes sont exécutées instantanément Avec notre clause OR :

SELECT * FROM A a
JOIN B b on a.idB = b.idB
JOIN C c on b.idC = c.idC
JOIN D d on c.idD = d.idD
JOIN E e  ON((e.perimeterId = a.idA AND e.level = 3) 
          OR (e.perimeterId = b.idB AND e.level = 2) 
          OR (e.perimeterId = c.idC AND e.level = 1)
          OR (e.perimeterId = d.idD AND e.level = 0))

Comment réécrire ma requête pour être exécutée instantanément ou avec de hautes performances possible?


6 commentaires

Si vous limitez vos données avec WHERE e.level in (0,1,2,3) , que deviendra la première requête?


Pouvez-vous clarifier: a) combien de valeurs distinctes de e.level avez-vous? b) avez-vous des valeurs en double dans les tableaux A , B , C , D pour idA < / code>, idB , idC ?


Pourquoi vous joignez-vous à a, b, c, d sans rien sélectionner parmi eux? Vous pouvez simplement faire un select e. * From e join a on e.preimeterid = a.ida where e.level = 3 union all select e. * From e join b ...


c'était une erreur j'ai mis à jour mon message @AlexYu


Ah. Je vois. Mais les questions restent les mêmes. Reformulons-les: a) Avez-vous des doublons dans votre requête de résultat? Si vous ajoutez DISTINCT à vos requêtes, le résultat change-t-il? b) Combien de valeurs distinctes de e.level avez-vous? Sont-ils dans la plage [0..4] ou par exemple [0..1024] ? c) si vous avez beaucoup de e.level s - que deviendra si vous ajoutez WHERE e.level dans (0,1,2,3) ou O e.level <4 et autres?


@AlexYu oui il y a des doublons car le résultat après distinct est différent, j'ai 7 niveau, j'ai ajouté la condition où e.level> = 0 car les autres niveaux sont (-3, -2, -1 ) mais la requête reste lente


3 Réponses :


2
votes

Vous ne voulez que des colonnes de e , donc j'écrirais ceci en utilisant exists:

SELECT e.*
FROM E e
WHERE EXISTS (SELECT 1
              FROM a
              WHERE e.perimeterId = a.idA AND e.level = 3
             ) OR
      EXISTS (SELECT 1
              FROM a JOIN
                   b
                   ON a.idB = b.idB
              WHERE e.perimeterId = b.idA AND e.level = 2
             ) OR
      EXISTS (SELECT 1
              FROM a JOIN
                   b
                   ON a.idB = b.idB JOIN
                   c
                   ON c.idC = b.idC
              WHERE e.perimeterId = c.idA AND e.level = 1
             ) OR
      EXISTS (SELECT 1
              FROM a JOIN
                   b
                   ON a.idB = b.idB JOIN
                   c
                   ON c.idC = b.idC JOIN
                   d
                   ON d.idD = c.idD
              WHERE e.perimeterId = d.idA AND e.level = 0
             );

Je ne suis pas sûr à 100% que les JOIN sont nécessaires dans les sous-requêtes, mais je les ai quand même laissés.


3 commentaires

La première requête écrite par OP duplique des lignes. Est intentionnellement ou non est inconnu. Et je suppose que cette performance pourrait être le filtrage des valeurs e.level sur (0,1,2,3)


@AlexYu. . . Je suis tout à fait conscient que les requêtes ne sont pas exactement les mêmes. D'après mon expérience, les doublons ne sont pas souhaitables, mais l'OP peut avoir un cas d'utilisation différent.


@GordonLinoff après avoir réécrit ma requête en suivant votre exemple, cela fonctionne très bien, merci beaucoup, j'accepterai votre message comme réponse



0
votes

1) créer des index sur a.idB, b.idB, b.idC, c.idC, c.idD, d.idD, e.perimeterId, e.level

2) Essayez ceci

SELECT * FROM A a
JOIN B b on a.idB = b.idB
JOIN C c on b.idC = c.idC
JOIN D d on c.idD = d.idD
JOIN E e  ON(e.perimeterId = a.idA AND e.level = 3) 
UNION ALL
SELECT * FROM A a
JOIN B b on a.idB = b.idB
JOIN C c on b.idC = c.idC
JOIN D d on c.idD = d.idD
JOIN E e  ON(e.perimeterId = a.idB AND e.level = 2) 
UNION ALL
SELECT * FROM A a
JOIN B b on a.idB = b.idB
JOIN C c on b.idC = c.idC
JOIN D d on c.idD = d.idD
JOIN E e  ON(e.perimeterId = a.idC AND e.level = 1) 
UNION ALL
SELECT * FROM A a
JOIN B b on a.idB = b.idB
JOIN C c on b.idC = c.idC
JOIN D d on c.idD = d.idD
JOIN E e  ON(e.perimeterId = a.idD AND e.level = 0) 


0 commentaires

0
votes

La plupart des problèmes de performances sont résolus avec des index bien construits, dans votre clause, essayez toujours d'utiliser les indeces primaires si vous n'avez pas à créer une colonne de filtres correspondante.


0 commentaires