将点分组为基于一列的线串,但仅限于按其他列顺序执行的顺序

时间:2017-07-13 13:14:08

标签: sql postgresql postgis

我有一个像这样结构的表:

datetime            | the_geom                   | m_status
2017-01-01T00:00:00 | POINT(13.048133,57.798882) | status_1
2017-01-01T00:00:01 | POINT(14.048133,56.798882) | status_1
2017-01-01T00:00:02 | POINT(15.048133,55.798882) | status_1
2017-01-01T00:00:04 | POINT(17.048133,53.798882) | status_2
2017-01-01T00:00:05 | POINT(18.048133,52.798882) | status_2
2017-01-01T00:00:06 | POINT(19.048133,51.798882) | status_2
2017-01-01T00:00:07 | POINT(20.048133,50.798882) | status_1
2017-01-01T00:00:08 | POINT(21.048133,49.798882) | status_1
2017-01-01T00:00:09 | POINT(22.048133,48.798882) | status_3
2017-01-01T00:00:10 | POINT(23.048133,47.798882) | status_3

我想将m_status的连续点分组到单独的Linestrings中,但前提是它们在datetime列之后按顺序排列。

感谢任何建设性的帮助。

PostgreSQL版本:x86_64-pc-linux-gnu上的PostgreSQL 9.5.2,由gcc编译(Ubuntu / Linaro 4.6.3-1ubuntu5)4.6.3,64位

编辑1:

预期产出:

id | the_geom                                                                  | m_status
1  | LINESTRING(13.048133 57.798882, 14.048133 56.798882, 15.048133 55.798882) | status_1
2  | LINESTRING(17.048133 53.798882, 18.048133 52.798882, 19.048133 51.798882) | status_2
3  | LINESTRING(20.048133 50.798882, 21.048133 49.798882)                      | status_1
4  | LINESTRING(22.048133 48.798882, 23.048133 47.798882)                      | status_3

编辑2: 我不需要每个线串的开始和结束日期,但是我需要某种id来知道它们的时间顺序(参见上面预期输出中的id)。

1 个答案:

答案 0 :(得分:2)

您可以使用行号方法的差异对组进行分类(运行内部查询以查看组分配),然后使用ST_MakeLine将所有这些点放到一行。

select ST_MakeLine(the_geom order by datetime) as the_geom, max(m_status) as m_status
from (select t.*,
      row_number() over(order by datetime)
      -row_number() over(partition by m_status order by datetime) as grp
      from tbl t
     ) t
group by grp

编辑:根据OP的编辑,在输出中包含行号,获取先前识别的每个组的最小或最大时间,并将其用于订购。

SELECT row_number() over(ORDER BY min_time) AS id,
       ST_MakeLine(the_geom ORDER BY datetime) AS the_geom,
       max(m_status) AS m_status
FROM
  (SELECT t.*,
           min(datetime) over(partition BY grp,m_status) AS min_time
   FROM
     (SELECT t.*,
             row_number() over(ORDER BY datetime) -row_number() over(partition BY m_status ORDER BY datetime) AS grp
      FROM tbl t
   ) t 
) t
GROUP BY grp,min_time