How to convert this code from Oracle to Postgresql

Viewed 38

I'm trying to convert this code from Oracle database to Postgresql, can someone help me with that?

WITH cycles AS
(
    SELECT SYS_CONNECT_BY_PATH (child_node_id, ',') AS path, SYS_CONNECT_BY_PATH ( CASE WHEN child_node_id < CONNECT_BY_ROOT child_node_id THEN child_node_id END, ','
) AS less_path
FROM alf_child_ASSOC
WHERE CONNECT_BY_ROOT parent_node_id = child_node_id
CONNECT BY NOCYCLE parent_node_id = PRIOR child_node_id
)

SELECT *
FROM cycles
WHERE LTRIM (less_path, ',') IS NULL ;

Thanks

2 Answers

You can convert your Oracle query from a hierarchical query to a recursive query in the form:

WITH cycles (path, root_id, child_node_id) AS (
  SELECT ',' || child_node_id,
         parent_node_id AS root_id,
         child_node_id
  FROM   alf_child_assoc
  WHERE  parent_node_id > child_node_id
UNION ALL
  SELECT c.path || ',' || a.child_node_id,
         c.root_id,
         a.child_node_id
  FROM   cycles c
         INNER JOIN alf_child_assoc a
         ON (c.child_node_id = a.parent_node_id)
  WHERE  a.parent_node_id != c.root_id
)
SELECT path
FROM   cycles
WHERE  child_node_id = root_id

Oracle db<>fiddle here

Then all you need to do to convert to PostgreSQL is to add the RECURSIVE keyword:

WITH RECURSIVE cycles (path, root_id, child_node_id) AS (
  SELECT ',' || child_node_id,
         parent_node_id AS root_id,
         child_node_id
  FROM   alf_child_assoc
  WHERE  parent_node_id > child_node_id
UNION ALL
  SELECT c.path || ',' || a.child_node_id,
         c.root_id,
         a.child_node_id
  FROM   cycles c
         INNER JOIN alf_child_assoc a
         ON (c.child_node_id = a.parent_node_id)
  WHERE  a.parent_node_id != c.root_id
)
SELECT path
FROM   cycles
WHERE  child_node_id = root_id

Which, for the sample data:

CREATE TABLE alf_child_assoc (
  child_node_id  NUMERIC(10,0),
  parent_node_id NUMERIC(10,0)
);

INSERT INTO alf_child_assoc (child_node_id, parent_node_id) VALUES (2, 1);
INSERT INTO alf_child_assoc (child_node_id, parent_node_id) VALUES (3, 2);
INSERT INTO alf_child_assoc (child_node_id, parent_node_id) VALUES (1, 3);

Outputs:

PATH
,1,2,3

PostgreSQL db<>fiddle here

I found a working solution:

WITH RECURSIVE prev AS (
        SELECT alf_child_ASSOC.child_node_id, 1 AS depth, array[child_node_id] as seen, false as cycle
        FROM alf_child_ASSOC
        UNION ALL
        SELECT alf_child_ASSOC.child_node_id, prev.depth + 1, seen || alf_child_ASSOC.child_node_id as seen,
            alf_child_ASSOC.child_node_id = any(seen) as cycle
        FROM prev
        INNER JOIN alf_child_ASSOC on prev.child_node_id = parent_node_id
        AND prev.cycle = false
    )
    SELECT *
    FROM prev
    WHERE cycle = true;

Exemple of output:

 child_node_id | depth |       seen        | cycle
---------------+-------+-------------------+-------
        749254 |     2 | {749254,749254}   | t
        749675 |     2 | {749675,749675}   | t
        754208 |     2 | {754208,754208}   | t
Related