PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Joindre deux tables en utilisant l'identifiant et les descendants de l'arborescence comme une table

Intégrer la requête

Améliorant la logique à plusieurs endroits, vous pouvez intégrer l'ensemble de l'opération dans une seule requête. L'encapsulation dans une fonction SQL est facultative :

CREATE OR REPLACE FUNCTION f_elems(_action_id integer)
  RETURNS SETOF integer AS
$func$
   WITH RECURSIVE l AS (
      SELECT a.category_id, l.local_id
      FROM   action a
      JOIN   local  l USING (local_id)
      WHERE  a.action_id = $1

      UNION ALL 
      SELECT l.category_id, c.local_id
      FROM   l
      JOIN   local c ON c.parent_id = l.local_id  -- c for "child"
      )
   SELECT e.element_id
   FROM   l
   JOIN   element e USING (category_id, local_id);
$func$  LANGUAGE sql STABLE;

Récupère tous les element_id pour le même et les enfants locaux d'un action_id donné .

Appel :

SELECT * FROM f_elem(3);

element_id
-----------
6
7

db<>violon ici
ANCIEN sqlfiddle

Cela devrait être substantiellement déjà plus vite pour plusieurs raisons. Les plus évidents étant :

  • Remplacez le SQL pur par une boucle lente dans plpgsql.
  • Réduire l'ensemble de départ de la requête récursive.
  • Supprimez les IN inutiles et notoirement lents construire.

J'appelle avec SELECT * FROM ... au lieu de simplement SELECT , même si la ligne n'a qu'une seule colonne, pour obtenir le nom de la colonne de OUT paramètre (element_id ) J'ai déclaré dans l'en-tête de la fonction.

Plus rapide, mais

Indices

Un index sur action.action_id est fourni par la clé primaire.

Mais vous avez peut-être manqué l'index sur local.parent_id . Pendant que vous y êtes, faites-en un index multi-colonnes couvrant (Postgres 9.2+) avec parent_id comme premier élément et local_id comme deuxième. Cela devrait beaucoup aider si la table local est grand. Pas tellement ou pas du tout pour une petite table :

CREATE INDEX l_mult_idx ON local(parent_id, local_id);

Pourquoi? Voir :

Enfin, un index multi-colonnes sur la table element devrait aider un peu plus :

CREATE INDEX e_mult_idx ON element (category_id, local_id, element_id);

La troisième colonne element_id n'est utile que pour en faire un index de couverture . Si votre requête récupère plus de colonnes de la table element , vous pouvez ajouter plus de colonnes à l'index ou supprimer element_id . L'un ou l'autre le rendra plus rapide.

Vue matérialisée

Si vos tables reçoivent peu ou pas de mises à jour, une vue matérialisée fournissant l'ensemble pré-calculé de toutes les paires (action_id, element_id) partager la même catégorie rendrait cela rapide comme l'éclair . Faire (action_id, element_id) (dans cet ordre) la clé primaire.