Reputation: 359
I have to come up with another question, which costs me already hours, and I am not able to solve it. I need to write a recursive Query in PostgreSQL (for univercity). I have a relation called "basedOn" where machines are basedOn other (older) versions like:
CREATE TABLE BasedON(
fromID integer,
fromSize numeric,
toID integer,
toSize numeric,
...
);
I need know to find all transitive versions which base on others (like if A bases on B, and B on C, A bases on C) using a recursive Query. My Problem now is, that I have to solve this also for Cycles like "A bases on B, B on C, and C on A", where my query loops infinite. Thats what my query looks like now:
WITH RECURSIVE tmp(fromID, fromSize, toID, toSize,c) AS (
SELECT fromID, fromSize, toID, toSize, 0 FROM BasedOn
WHERE toID= 0 AND toSize= 2.0 --start
UNION
SELECT b.fromID, b.fromSize, t.toID,t.toSIze, c+1 AS steps
FROM BasedOn b JOIN tmp t ON
t.fromID= b.toID AND t.fromSize= b.toSize
)SELECT * FROM tmp;
"c" is just use to "count" the recursive steps. It works thine for non-cyclic data. But if I insert an cycle into the data it loops infinite. Has anyone a tip, how to avoid this? Thanks in advance, Lukas
SampleData:
INSERT INTO BasedOn VALUES
(7000, 1.28, 7003, 2.52),
(7003, 2.52, 7006, 0.98), --cycle
(7006, 0.98, 7009, 4.18),
(7006, 0.98, 7003, 2.52), --cycle
(7009, 4.18, 7015, 1.33),
(7009, 4.18, 0, 2.00);
Expected output:
fromID, fromSize, toID, toSize,stepcount
7009 4.18 0 2.00 0
7006 0.98 0 2.00 1
7003 2.52 0 2.00 2
7000 1.28 0 2.00 3
Upvotes: 1
Views: 1296
Reputation: 44240
There you go:
\i tmp.sql
CREATE TABLE based_on(
from_id integer
, from_size numeric
, to_id integer
, to_size numeric
);
INSERT INTO based_on VALUES
(7000, 1.28, 7003, 2.52),
(7003, 2.52, 7006, 0.98), --cycle
(7006, 0.98, 7009, 4.18),
(7006, 0.98, 7003, 2.52), --cycle
(7009, 4.18, 7015, 1.33),
(7009, 4.18, 0, 2.00);
-- I need know to find all transitive versions which base on others (like if A bases on B, and B on C, A bases on C) using a recursive Query. My Problem now is, that I have to solve this also for Cycles like "A bases on B, B on C, and C on A", where my query loops infinite. Thats what my query looks like now:
WITH RECURSIVE tmp(from_id, from_size, to_id, to_size,c,path) AS (
SELECT from_id, from_size, to_id, to_size
, 0
, array[from_id] AS path
FROM based_on
WHERE to_id= 0 AND to_size= 2.0 --start
UNION
SELECT b.from_id, b.from_size
, t.to_id,t.to_size
, c+1 AS steps
, t.path || b.from_id
FROM based_on b
JOIN tmp t ON t.from_id= b.to_id -- AND t.from_size= b.to_size
AND NOT b.from_id = ANY(t.path)
)
SELECT * FROM tmp;
Result:
DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
INSERT 0 6
from_id | from_size | to_id | to_size | c | path
---------+-----------+-------+---------+---+-----------------------
7009 | 4.18 | 0 | 2.00 | 0 | {7009}
7006 | 0.98 | 0 | 2.00 | 1 | {7009,7006}
7003 | 2.52 | 0 | 2.00 | 2 | {7009,7006,7003}
7000 | 1.28 | 0 | 2.00 | 3 | {7009,7006,7003,7000}
(4 rows)
Upvotes: 1
Reputation: 48769
I made the exercise using ARRAY
just for the fun (?).
Anyway, here's the query:
with recursive
s as (
select
*, tosize as converted_size, 0 as step_count,
array[-1]::int[] as walked, 1 as len from basedon
where toid = 0 and tosize = 2.0 -- starting node
union all
select b.*, s.converted_size, step_count + 1,
walked || b.fromid, array_length(walked, 1) + 1
from s
join basedon b on b.toid = s.fromid and b.tosize = s.fromsize
and not (b.fromid = any(walked))
)
select * from s;
Result:
fromid fromsize toid tosize converted_size step_count walked len
------ -------- ---- ------ -------------- ---------- ------------ ---
7009 4.18 0 2 2 0 <UnknownType 1
7006 0.98 7009 4.18 2 1 <UnknownType 2
7003 2.52 7006 0.98 2 2 <UnknownType 3
7000 1.28 7003 2.52 2 3 <UnknownType 4
Here's the data script I used:
create table basedon (
fromid integer,
fromsize numeric,
toid integer,
tosize numeric
);
insert into basedon (fromid, fromsize, toid, tosize) values
(7000, 1.28, 7003, 2.52),
(7003, 2.52, 7006, 0.98), --cycle
(7006, 0.98, 7009, 4.18),
(7006, 0.98, 7003, 2.52), --cycle
(7009, 4.18, 7015, 1.33),
(7009, 4.18, 0, 2.00);
Upvotes: 2