【问题标题】:How to group following rows by not unique value如何按非唯一值对以下行进行分组
【发布时间】:2015-09-01 21:10:30
【问题描述】:

我有这样的数据:

table1
_____________
id way time
1  1   00:01
2  1   00:02
3  2   00:03
4  2   00:04
5  2   00:05
6  3   00:06
7  3   00:07
8  1   00:08
9  1   00:09

我想知道我在哪个时间段内走哪条路:

desired output
_________________
id  way from   to    
1   1   00:01  00:02
3   2   00:03  00:05
6   3   00:06  00:07
8   1   00:08  00:09

我尝试使用窗口函数:

SELECT DISTINCT
  first_value(id) OVER w AS id, 
  first_value(way) OVER w as way,
  first_value(time) OVER w as from,
  last_value(time) OVER w as to
FROM table1
WINDOW w AS (
  PARTITION BY way ORDER BY ID
  range between unbounded preceding and unbounded following);

我得到的是:

ID  way from   to    
 1   1  00:01  00:09
 3   2  00:03  00:05
 6   3  00:06  00:07

这是不正确的,因为在 1 路我不是从 00:01 到 00:09。 是否有可能按照顺序进行分区,意味着只分组以下相等的属性?

【问题讨论】:

  • way 2 00:03 - 00:05way 3 00:06-00:07 怎么样?这非常令人困惑。
  • 错了,我修好了。谢谢。
  • 你让它看起来像idtime 两者都将严格平行上升。是这样吗?你确定吗?如果idserial 列,则很可能不是总是如此。这意味着一个时间片的最小值id 和最小值time 可能位于不同的行中。那么结果应该是什么?
  • 不,他们不是。 id 严格按照我开车经过的顺序升序。但是不需要唯一,有些路的开始和结束时间是一样的(结束时间基本上就是下一个路的开始时间,例子中没有显示),理论上下一行的开始时间可以在之前(以及结束时间)。我从 GPS 轨道的地图匹配到 Openstreetmap 网络获取数据。这些方法的顺序是正确的。但是,我通过加入最近邻 GPS 点将时间戳分配给边缘,这可能会导致错误。

标签: sql postgresql greatest-n-per-group window-functions gaps-and-islands


【解决方案1】:

我想你想要这样的东西:

select min(id), way, 
       min(time), max(time)
from (
select id, way, time,
       ROW_NUMBER() OVER (ORDER BY id) - 
       ROW_NUMBER() OVER (PARTITION BY way ORDER BY time) AS grp
from table1 ) t
group by way, grp

grp 标识连续 way 值的“孤岛”。在外部查询中使用此计算字段,我们可以分别使用MINMAX 聚合函数获得way 区间的开始结束时间。

Demo here

【讨论】:

  • @Nassim OP 想要识别 successive way 值的孤岛。发布的示例数据中有 4 个。请查看 desired outputnot what I get 输出。
  • 是的,我误解了这个问题,所以我删除了我的答案,在这种情况下你的答案更准确+1
【解决方案2】:

如果您的情况与示例值所建议的一样简单,@Giorgos' answer 可以很好地发挥作用。

但是,通常情况并非如此。如果id 列是serial,则不能假设具有较早time 的行也具有较小的id
此外,time 值(或您可能拥有的 timestamp)很容易重复,您需要明确排序顺序。

假设两者都可能发生,并且您希望每个时间片具有最早 time 的行中的 id(实际上,最小 id 对于最早的时间,可能有联系),此查询将正确处理这种情况:

SELECT *
FROM  (
   SELECT DISTINCT ON (way, grp)
          id, way, time AS time_from
        , max(time) OVER (PARTITION BY way, grp) AS time_to
   FROM (
      SELECT *
           , row_number() OVER (ORDER BY time, id)  -- id as tie breaker
           - row_number() OVER (PARTITION BY way ORDER BY time, id) AS grp
      FROM   table1
      ) t
   ORDER  BY way, grp, time, id
   ) sub
ORDER  BY time_from, id;
  • ORDER BY time, id 是明确的。假设时间不是唯一的,添加(假定唯一的)id 以避免任意结果 - 这可能会以偷偷摸摸的方式在查询之间发生变化。

  • max(time) OVER (PARTITION BY way, grp):如果没有ORDER BY,窗口框架会跨越 PARTITION 的所有行,因此我们得到每个时间片的绝对最大值。

  • 外部查询层只需要在结果中产生所需的排序顺序,因为我们在子查询sub 中使用DISTINCT ON 绑定到不同的ORDER BY。详情:

SQL Fiddle 演示用例。

如果您希望优化性能,在这种情况下使用 plpgsql 函数可能会更快。密切相关的答案:

另外:不要使用基本类型名称time 作为标识符(也是reserved word in standard SQL)。

【讨论】:

    猜你喜欢
    • 2023-04-09
    • 2022-07-07
    • 2016-08-28
    • 2017-11-20
    • 2012-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多