2017-10-20 23 views
1

Je suis un peu fou à la recherche d'une solution à ce problème:Postgres. Comment obtenir tous les parents qui répondent aux critères de l'enfant?

Je suis quelque chose comme ce tableau:

Tableau de données

enter image description here

Et je veux une requête pour obtenir tous les éléments qui réussissent la condition et tous les parents, je veux dire, ce résultat:

Résultat de la requête

enter image description here

J'ai pensé à la requête:

SELECT a.* FROM table a 
    JOIN table b ON b.id = a.id 
    WHERE a.id IN (SELECT DISTINCT c.parent_id FROM table c WHERE c.condition = TRUE) 
    OR b.id IN (SELECT DISTINCT c.id FROM table c WHERE c.condition = TRUE); 

Mais je ne peux obtenir un niveau de différence avec cette méthode, je veux dire, je ne peux pas obtenir plus de 1 parent sans condition. Merci beaucoup.

Répondre

2

Vous pouvez utiliser un recursive CTE pour cela:

WITH RECURSIVE recCTE AS 
(
    /*Get all the true children to seed the recursive query*/ 
    SELECT 
     id, 
     parent_id, 
     condition as initial_condition, 
     1 as depth, 
     CAST(id as varchar(50)) as path 
    FROM 
     table a 
    WHERE 
     a.id NOT IN (SELECT DISTINCT parent_id from table) 
     and a.condition = 'true' 

    UNION ALL 

    /*Recursive bit that refers back to itself. Find the parents*/ 
    SELECT 
     b.id, 
     b.parent_id, 
     a.initial_condition, 
     depth + 1 as depth, 
     cast(path || '>' || b.id as varchar(50)) as path   

    FROM 
     recCTE a 
     INNER JOIN table b ON 
      a.parent_id = b.id 
    WHERE 
     /*avoid going too deep in case of cycling*/ 
     depth <= 20 
) 
SELECT * FROM recCTE 

Le CTE récursive utilise deux parties:

  1. La graine récursive: Ceci est la première moitié de la requête UNION. Dans ce, nous identifions tous les enfants (ID qui ne sont pas aussi Parent_IDs) qui sont "True"

  2. Le terme récursif: C'est la deuxième moitié de la requête UNION. Il se réfère à lui-même (recCTE) dans la clause FROM et joint table à nouveau; reliant le recCTE.parent_id (itérations précédentes parent_id) à id de la table. Puis tire toutes les informations nécessaires pour cette itération.

Je presque toujours suivies de la profondeur récursive (combien de récurrences at-il fallu pour arriver à ce dossier), et le chemin (à partir du fond plus enfant que d'autres nœuds de cette hiérarchie ne nous a frappé pour se rendre à cet enregistrement). J'utilise la profondeur pour m'assurer que nous n'allons pas trop loin dans le trou du lapin. Dans le cas où vous avez des documents tels que:

+----+-----------+ 
| id | parent_id | 
+----+-----------+ 
| 1 |   5 | 
| 5 |   7 | 
| 7 |   1 | 
+----+-----------+ 

qui provoqueraient une boucle infinie (cycliste) le scénario du pire est-il arrêtera après il va 20 cycles profonds (1> 5> 7> 1> 5> 7> 1> 5> 7> 1> 5> 7> 1> 5> 7> 1> 5> 7> 1> 5). Il y a d'autres façons d'arrêter de faire du vélo, comme utiliser le champ de chemin: WHERE a.path NOT LIKE '%' || a.parent_id || '%' par exemple.

Vous pouvez obtenir un peu plus d'élégance avec cette sélection finale si vous en avez besoin, mais cela vous permettra d'atteindre 95% du chemin.

+0

Wow man, explication vraiment sympa, je suis devenu fou avec ça, je lis un peu sur le CTE récursif et suis émerveillé! Entièrement sauvé la journée grâce à vous. J'ai adapté la requête à ma table et était bien: D –

+0

Great! Je suis content que cela a marché pour vous dès la sortie de la boîte. Il y a certainement une courbe d'apprentissage pour les CTE récursifs, mais une fois que vous les avez compris, ils sont tout à fait compréhensibles et constituent un excellent outil pour la boîte à outils. – JNevill