snippetsqlMinor
How to "group" based on consecutive occurrences of an element in a column in postgres
Viewed 0 times
occurrencesgroupcolumnpostgreselementbasedhowconsecutive
Problem
I have a table that, when ordered, describes a path by point descriptions in each row.
A column tells whether a row is a special (stop) point within that path and is empty otherwise. The path starts and ends with such a row and has many in between. What I want to do is create line segments from the point rows that start at such a row and also end at such a row.
I want to call
A column tells whether a row is a special (stop) point within that path and is empty otherwise. The path starts and ends with such a row and has many in between. What I want to do is create line segments from the point rows that start at such a row and also end at such a row.
point stop
1 yes
2
3 yes
4
5
6 yesI want to call
ST_Makeline from PostGIS to turn the ordered points into these line segments. How can I enforce that ST_Makeline, being a "summary" function is only called for the inclusive point groups 1 to 3 and 3 to 6?Solution
I personally think this is not a good design because all intermediate stop points must be included into two segments.
IMHO, you should be able to transform it into something similar to:
But using your current schema you could try by using:
That basically returns an array of points like this:
grp | points
--: | :------------------------
1 | {"(1,2)","(2,3)"}
2 | {"(3,4)","(4,5)","(5,6)"}
Then using ST_MakePoint and ST_MakeLine you can get:
grp | lines
--: | :-----------------------------------------------------------------------------------------------------------------
1 | 010200000002000000000000000000F03F000000000000004000000000000000400000000000000840
2 | 010200000003000000000000000000084000000000000010400000000000001040000000000000144000000000000014400000000000001840
db<>fiddle here
IMHO, you should be able to transform it into something similar to:
id | start | end | stop
---|-------|-----|-----
1 | 1 | 2 |
2 | 2 | 3 | yes
3 | 3 | 4 |
4 | 4 | 5 |
5 | 5 | 6 | yesBut using your current schema you could try by using:
WITH x AS
(
SELECT
point,
stop,
LEAD(point) OVER (ORDER BY point) nxt,
SUM(CASE WHEN stop = true THEN 1 ELSE 0 END) OVER (ORDER BY point) grp
FROM
tbl
ORDER BY
point
)
SELECT
grp, ST_MakeLine(array_agg(ST_MakePoint(point, nxt::int))) lines
FROM
x
WHERE
nxt IS NOT NULL
GROUP BY
grp
ORDER BY
grp;That basically returns an array of points like this:
grp | points
--: | :------------------------
1 | {"(1,2)","(2,3)"}
2 | {"(3,4)","(4,5)","(5,6)"}
Then using ST_MakePoint and ST_MakeLine you can get:
grp | lines
--: | :-----------------------------------------------------------------------------------------------------------------
1 | 010200000002000000000000000000F03F000000000000004000000000000000400000000000000840
2 | 010200000003000000000000000000084000000000000010400000000000001040000000000000144000000000000014400000000000001840
db<>fiddle here
Code Snippets
id | start | end | stop
---|-------|-----|-----
1 | 1 | 2 |
2 | 2 | 3 | yes
3 | 3 | 4 |
4 | 4 | 5 |
5 | 5 | 6 | yesWITH x AS
(
SELECT
point,
stop,
LEAD(point) OVER (ORDER BY point) nxt,
SUM(CASE WHEN stop = true THEN 1 ELSE 0 END) OVER (ORDER BY point) grp
FROM
tbl
ORDER BY
point
)
SELECT
grp, ST_MakeLine(array_agg(ST_MakePoint(point, nxt::int))) lines
FROM
x
WHERE
nxt IS NOT NULL
GROUP BY
grp
ORDER BY
grp;Context
StackExchange Database Administrators Q#227856, answer score: 2
Revisions (0)
No revisions yet.