Leçons du module (2/4)
Self-JOIN : une table avec elle-même
Parfois, la relation que vous souhaitez traverser n'est pas entre deux
tables différentes, mais entre lignes d'une même table. Exemple typique : un
hiérarchie "catégorie → sous-catégorie", où le parent et l'enfant sont des rangées de
la même table categories, liée par un parent_id.
Pour lire ensemble une ligne (enfant) et "son" autre ligne (parent), vous utilisez un
self-join : la même table apparaît deux fois dans le FROM, avec deux
différents pseudonymes.
La syntaxe
SELECT colonne
FROM tabella AS alias_a
JOIN tabella AS alias_b ON alias_a.foreign_key = alias_b.id;L'alias est obligatoire : si on omet AS a / AS b, PostgreSQL n'a pas
moyen de savoir de laquelle des deux copies nous parlons lorsque nous écrivons name ou id.
-- Ogni sotto-categoria con il nome della categoria padre:
SELECT child.name AS subcategory,
parent.name AS category
FROM categories AS child
JOIN categories AS parent ON child.parent_id = parent.id;Lire en anglais simple : "prenez les lignes de categories nommées enfant, et
pour chacun trouvez la ligne de categories nommée parent dont id est le
parent_id de l'enfant".
Le résultat exclut les catégories racines (celles avec parent_id = NULL)
car un INNER JOIN saute les lignes sans correspondance. Si vous souhaitez inclure
eux, utilisez LEFT JOIN.
Auto-adhésion avec LEFT pour "garder aussi les racines"
-- Tutte le categorie, con nome del padre o NULL se sono radice:
SELECT child.name AS category,
parent.name AS parent
FROM categories AS child
LEFT JOIN categories AS parent ON child.parent_id = parent.id;Vous obtenez maintenant 9 lignes (chaque catégorie de l'ensemble de données), avec parent = NULL
pour les 3 racines.
Agrégation avec une auto-jointure
Il se combine très bien avec GROUP BY pour demander « combien d'enfants chacun a-t-il ?
le parent a :
SELECT p.name AS parent_category,
COUNT(c.id) AS children
FROM categories AS p
LEFT JOIN categories AS c ON c.parent_id = p.id
WHERE p.parent_id IS NULL -- solo le radici
GROUP BY p.name
ORDER BY p.name;Notez deux détails importants :
WHERE p.parent_id IS NULLfiltre avant le JOIN : on prend uniquement le racines comme « table de gauche ».COUNT(c.id)(pasCOUNT(*)) renvoie 0 pour les racines sans enfants, alors queCOUNT(*)renverrait 1 à cause de la ligneLEFT JOINavec NULL.
Essayez-le vous-même
Pour chaque sous-catégorie (catégorie avec parent_id NOT NULL), affiche le nom de la sous-catégorie et le nom de votre catégorie-père. Colonne due : sous-catégorie, catégorie. Ordina par catégorie poi par sous-catégorie.
Afficher l'indice
La condition du self-JOIN est child.parent_id = parent.id.
Solution disponible après 3 tentatives
Exercice de révision
Pour chaque catégorie de base (parent_id IS NULL), affichez le nom et le numéro de sous-catégorie. Colonne due : nom, enfants. Ordina par nom. Inclut une éventuelle racine sans figure avec 0.
Afficher l'indice
LEFT JOIN sur les catégories deux fois (alias p pour parent, c pour enfant). Utilisez COUNT(c.id), et non COUNT(*), pour obtenir 0 sur les racines sans enfants.
Solution disponible après 3 tentatives