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

Requête Postgresql ltree pour trouver le parent avec le plus d'enfants ; à l'exclusion de la racine

Solution

Pour trouver le nœud avec le plus d'enfants :

SELECT subpath(path, -1, 1), count(*) AS children
FROM   tbl
WHERE  path <> ''
GROUP  BY 1
ORDER  BY 2 DESC
LIMIT  1;

... et exclure les nœuds racine :

SELECT *
FROM  (
   SELECT ltree2text(subpath(path, -1, 1))::int AS tbl_id, count(*) AS children
   FROM   tbl
   WHERE  path <> ''
   GROUP  BY 1
   ) ct
LEFT   JOIN (
   SELECT tbl_id
   FROM   tbl
   WHERE  path = ''
   ) x USING  (tbl_id)
WHERE  x.tbl_id IS NULL
ORDER  BY children DESC
LIMIT  1

En supposant que les nœuds racine ont un ltree vide ('' ) comme chemin. Peut être NULL . Ensuite, utilisez path IS NULL ...

Le gagnant dans votre exemple est en fait 2001 , avec 5 enfants.

-> SQLfiddle

Comment ?

  • Utilisez la fonction subpath(...) fourni par le module supplémentaire ltree .

  • Obtenir le dernier nœud dans le chemin avec un décalage négatif , qui est le parent direct de l'élément.

  • Comptez la fréquence d'apparition de ce parent, excluez les nœuds racine et prenez celui qui reste avec le nombre le plus élevé.

  • Utilisez ltree2text() pour extraire la valeur de ltree .

  • Si plusieurs nœuds ont également le plus d'enfants, un nœud arbitraire est choisi dans l'exemple.

Cas de test

C'est le travail que j'ai dû faire pour arriver à un cas de test utile (après avoir coupé un peu de bruit):

Voir SQLfiddle .

En d'autres termes :n'oubliez pas de fournir un scénario de test utile la prochaine fois.

Colonnes supplémentaires

Répondez au commentaire.
Tout d'abord, développez le cas de test :

ALTER TABLE tbl ADD COLUMN postal_code text
              , ADD COLUMN whatever serial;
UPDATE tbl SET postal_code = (1230 + whatever)::text;

Jetez un œil :

SELECT * FROM tbl;

Simplement JOIN résultat au parent dans la table de base :

SELECT ct.*, t.postal_code
FROM  (
   SELECT ltree2text(subpath(path, -1, 1))::int AS tbl_id, count(*) AS children
   FROM   tbl
   WHERE  path <> ''
   GROUP  BY 1
   ) ct
LEFT   JOIN (
   SELECT tbl_id
   FROM   tbl
   WHERE  path = ''
   ) x USING  (tbl_id)
JOIN  tbl t USING (tbl_id)
WHERE  x.tbl_id IS NULL
ORDER  BY children DESC
LIMIT  1;