Conditions Postgres JOIN vs conditions WHERE

12

Postgres newbie here.

Je me demande si cette requête est optimisée ou non? J'ai essayé de JOIN ON uniquement les valeurs qui sont 100% nécessaires et en laissant toutes les conditions dynamiques dans la clause WHERE. Voir ci-dessous.

SELECT *
    FROM
      myapp_employees
    JOIN myapp_users ON
      myapp_users.user_id=myapp_employees.user_id
    JOIN myapp_contacts_assoc ON
      myapp_contacts_assoc.user_id=myapp_users.user_id
    JOIN myapp_contacts ON
      myapp_contacts.contact_id=myapp_contacts_assoc.contact_id
    WHERE
      myapp_contacts.value='[email protected]' AND
      myapp_contacts.type=(1)::INT2 AND
      myapp_contacts.is_primary=(1)::INT2 AND
      myapp_contacts.expired_at IS NULL AND
      myapp_employees.status=(1)::INT2 AND
      myapp_users.status=(1)::INT2
    LIMIT 1;

Remarque: Pour le contexte, ce proc vérifie si un utilisateur est également un employé (privilèges élevés / type d'utilisateur différent).

Quoi qu'il en soit, est-ce la bonne façon de procéder? Le JOIN ON doit-il contenir plus d'instructions comme la vérification de expired_at IS NULL, par exemple? Pourquoi ou pourquoi cela n'a-t-il pas de sens?

Dan
la source
Qu'en est-il de votre version de Postgres? ( SELECT version();)
Erwin Brandstetter
@ErwinBrandstetter J'utilise PostgreSQL 9.3.14. Cela devrait-il être quelque chose dont j'ai besoin dans chaque fonction?
Dan
2
Non, sur dba.SE, nous vous demandons de déclarer les versions logicielles pertinentes, car elles font la différence pour de nombreuses questions.
Erwin Brandstetter

Réponses:

14

Logiquement , cela ne fait aucune différence que vous placiez des conditions dans la clause join d'un INNER JOINou dans la même WHEREclause SELECT. L'effet est le même.

(Pas le cas pour OUTER JOIN!)

Tout en fonctionnant avec les paramètres par défaut, cela ne fait aucune différence pour le plan de requête ou les performances . Postgres est libre de réarranger et rejoint JOINet WHEREconditions dans sa quête pour le meilleur plan de requête - tant que le nombre de tables est pas supérieure à la join_collapse_limit(par défaut 8). Détails:

Pour la lisibilité et la maintenabilité, il est logique de placer des conditions qui relient les tables dans la JOINclause respective et les conditions générales dans la WHEREclause.

Votre requête semble très bien. J'utiliserais des alias de table pour réduire le bruit, cependant.

Détail mineur:

int2 '1'ou même 1::int2sont plus sensibles que (1)::INT2. Et tout en se comparant à une valeur de type de données numérique bien défini, une constante numérique simple 1est également suffisante.

Erwin Brandstetter
la source
2

Quelques points ..

  1. Si vous vous joignez à une condition du même nom ( user_id) dans votre cas, vous pouvez utiliser USING (user_id)plutôt que ON (a.user_id = b.user_id). Cela évite également qu'une colonne redondante ne soit potentiellement sortie (si vous exécutez SELECT *en production).

  2. 1::int2est problématique. Soit status, et is_primaryet d'autres sont déjà int2dans ce cas, le littéral 1 sera automatiquement casté en int2, ou int2 casté en int comme pg le juge approprié. Ou, si vous les stockez en tant qu'ints réguliers et que vous les jetez comme si cela faisait une différence dans le calcul - ce qui n'est pas le cas, le casting seul en fait une proposition perdante.

  3. Lorsque cela est possible, tous les :: int2 devraient probablement être stockés en tant que boolean. Ensuite, vous pouvez également écrire votre WHEREcondition pour être plus simple.

  4. Pour votre type et votre statut, vous souhaiterez peut-être un ENUMtype.

Evan Carroll
la source