web-dev-qa-db-fra.com

Conditions Postgres JOIN vs conditions WHERE

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? Est-ce que JOIN ON devrait contenir plus d'instructions comme vérifier expired_at IS NULL, par exemple? Pourquoi ou pourquoi cela n'a-t-il pas de sens?

9
Dan

Logiquement , cela ne fait aucune différence que vous placiez des conditions dans la clause join d'un INNER JOIN ou la clause WHERE de la même 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éorganiser les jointures et les conditions JOIN & WHERE dans sa quête du meilleur plan de requête - tant que le nombre de tables n'est pas supérieur à (join_collapse_limit (par défaut 8). Détails:

Pour la lisibilité et la maintenabilité , il est logique de placer les conditions qui connectent les tables dans la clause JOIN respective et les conditions générales dans la WHERE clause.

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::int2 sont plus sensés que (1)::INT2. Et en comparant à une valeur de type de données numérique bien défini, une constante numérique simple 1 est assez bien aussi.

13
Erwin Brandstetter

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::int2 Est problématique. Soit status, et is_primary Et d'autres sont déjà int2, Auquel cas le littéral 1 sera automatiquement casté en int2, ou int2 casté en int comme pg le juge bon. 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 sous boolean. Ensuite, vous pouvez écrire votre condition WHERE pour être aussi plus simple.

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

2
Evan Carroll