Reputation: 43
I have a table like the following:
create table path(
id integer,
start varchar(255),
finish varchar(255),
client integer
);
INSERT INTO path(id, start, finish, client) VALUES (1, '1', '2', 1);
INSERT INTO path(id, start, finish, client) VALUES (2, '2', '3', 1);
INSERT INTO path(id, start, finish, client) VALUES (3, '3', '4', 1);
INSERT INTO path(id, start, finish, client) VALUES (4, '5', '6', 1);
I've tried the following query:
select string_agg(start || ' -> ' ||finish, ', ' order by id) from path group by client;
and the result was like that:
1 -> 2, 2 -> 3, 3 -> 4, 5 -> 6
How can I get the following result if two sequenced values are the same:
1 -> 2 -> 3 -> 4, 5 -> 6
Here's the Sql Fiddle link.
Upvotes: 0
Views: 83
Reputation: 50034
As mentioned in my comment, a recursive CTE will produce this. As an example:
WITH RECURSIVE reccte AS
(
/*Recursive Seed - Records that will start recursion*/
SELECT id, start, finish, client, start || '->' || finish as path, 1 as depth
FROM path
WHERE id = 1
UNION ALL
/*Recursive Term - This will be the statement that iterates until the join fails*/
SELECT
path.id, path.start, path.finish, path.client, reccte.path || '->' || path.finish, depth + 1
FROM reccte
INNER JOIN path ON reccte.finish = path.start
WHERE depth < 20 /*make sure we don't loop endlessly*/
)
SELECT * FROM reccte;
+----+-------+--------+--------+---------------+-------+
| id | start | finish | client | path | depth |
+----+-------+--------+--------+---------------+-------+
| 1 | 1 | 2 | 1 | 1->2 | 1 |
| 2 | 2 | 3 | 1 | 1->2->3 | 2 |
| 3 | 3 | 4 | 1 | 1->2->3->4 | 3 |
| 4 | 4 | 5 | 1 | 1->2->3->4->5 | 4 |
+----+-------+--------+--------+---------------+-------+
That final SELECT
can be changed out to SELECT path FROM reccte ORDER BY depth DESC LIMIT 1;
in this example to go after the record you want.
UPDATED:
The solution is still the same here, we just tweak the recursive CTE and final select to get the different full branches/path for your hierarchy:
http://sqlfiddle.com/#!17/2892f/6
WITH RECURSIVE reccte AS
(
SELECT id as origin, id, start, finish, client, start || ' -> ' || finish as path, 1 as depth
FROM path
WHERE start NOT IN (SELECT distinct finish FROM path)
UNION ALL
SELECT
reccte.origin, path.id, path.start, path.finish, path.client, reccte.path || ' -> ' || path.finish, depth + 1
FROM reccte
INNER JOIN path ON reccte.finish = path.start
WHERE depth < 20 /*make sure we don't loop endlessly*/
)
SELECT * FROM (SELECT origin, path, depth, max(depth) OVER (PARTITION BY origin) as maxdepth FROM reccte) sub WHERE sub.maxdepth = depth;
+--------+------------------+-------+----------+
| origin | path | depth | maxdepth |
+--------+------------------+-------+----------+
| 1 | 1 -> 2 -> 3 -> 4 | 3 | 3 |
| 4 | 5 -> 6 | 1 | 1 |
+--------+------------------+-------+----------+
You can string_agg() here too in that final select to get EXACTLY the output in your updated question:
http://sqlfiddle.com/#!17/2892f/5
SELECT STRING_AGG(path, ', ' order by origin) FROM (SELECT origin, path, depth, max(depth) OVER (PARTITION BY origin) as maxdepth FROM reccte) sub WHERE sub.maxdepth = depth;
+--------------------------+
| string_agg |
+--------------------------+
| 1 -> 2 -> 3 -> 4, 5 -> 6 |
+--------------------------+
Upvotes: 1