web-dev-qa-db-fra.com

Requête pour trouver des références circulaires

J'ai deux tables

ID  Task
1   1
2   2
3   3
4   4

Col1  depend
2     3
2     4
3     1
4     2

ID et Col1 sont liés à travers la contrainte FK. Je veux trouver toutes les références circulaires. Ici ID et Col1 est juste pour combiner des lignes de 2 tables, par exemple:

Task 1 can start anytime.
Task 2 can start only after completion of 3, 4 etc

1 –
2 – 3, 4, 1, 2   -- for 2 there is circular dependency
3 – 1
4 – 2, 3, 4      -- also circular dependency

Cas 2:

Col1  depend
2     3
2     4
3     1
4     5
5     2

ID  Task
1   1
2   2
3   3
4   4
5   5

1
2 – 3, 4, 1, 5, 2     -- circular reference
3 – 1
4 – 5, 2, 3, 4        -- circular reference
5 – 2, 3, 4, 5        -- circular reference

Les références circulaires peuvent être disponibles à tout niveau de récursivité. Comment trouver de telles références circulaires?
[.____] Nous avons essayé une requête récursive, mais nous sommes allés dans une boucle infinie. Comment écrire une requête récursive pour cela?

6
GaNeSh GoRdE

J'ai adapté l'exemple à http://www.postgresql.org/docs/8.4/static/queries-with.html à votre cas:

WITH RECURSIVE search_graph(id, depends, depth, path, cycle) AS (
        SELECT g.Col1, g.depends, 1,
          ARRAY[g.Col1],
          false
        FROM deps g
      UNION ALL
        SELECT g.Col1, g.depends, sg.depth + 1,
          path || g.Col1,
          g.Col1 = ANY(path)
        FROM deps g, search_graph sg
        WHERE g.Col1 = sg.depends AND NOT cycle
)
SELECT distinct id FROM search_graph where cycle = true;

résultats:

ID
 4
 2

pour le premier exemple,

ID
 4
 2
 5

pour la seconde

Vous pouvez trouver le FIDDLE SQL AT http://sqlfiddle.com/#!15/87A96/2

4
Leo

Construire également sur le exemple dans le manuel :

WITH RECURSIVE graph AS (
   SELECT col1 AS id
        , ARRAY[depend, col1] AS path
        , (depend = col1) AS cycle    -- first step could be circular
   FROM   dep

   UNION ALL
   SELECT d.col1, d.depend || path, d.depend = ANY(path)
   FROM   graph g
   JOIN   dep   d ON d.col1 = g.path[1]
   WHERE  NOT g.cycle
   )
SELECT DISTINCT id
FROM   graph
WHERE  cycle;

Résultat

id
 2
 4
 5

SQL FIDDLE.

Ceci est un peu plus simple et plus rapide:

  • Pas de colonnes non pertinentes.
  • A besoin d'une instance de moins parce qu'elle vérifie.
  • Identifie immédiatement les lignes de court-circuit. (Il n'a pas été exclu qu'une ligne peut être liée à elle-même.)

Celui-ci prepends le chemin avec le nouvel article respectif, afin que nous puissions utiliser path[1] Au lieu d'une colonne supplémentaire. À ma connaissance, aussi vite que Ajout .
[.____] L'accès au premier élément d'un tableau doit être presque aussi bon marché qu'une colonne supplémentaire - qui rendrait la rangée plus large. Testez et comparez si la performance est importante:

WITH RECURSIVE graph AS (
   SELECT col1 AS id, depend AS col1  -- simplify join condition
        , ARRAY[col1, depend] AS path
        , (col1 = depend) AS cycle    -- simplify if short-circuit impossible
   FROM   dep

   UNION ALL
   SELECT d.col1, d.depend
        , path || d.depend, d.depend = ANY(path)
   FROM   graph g
   JOIN   dep   d USING (col1)
   WHERE  NOT g.cycle
   )
SELECT DISTINCT id
FROM   graph
WHERE  cycle;
2