窗口函数:在第一列之后按一列划分

时间:2018-09-13 18:21:34

标签: sql postgresql window-functions

免责声明:所示问题比我最初预期的要普遍得多。下面的示例来自一个解决方案,另一个问题。但是,现在我拿这个样本来解决更多问题-主要与时间序列有关(请看一下右侧栏中的“链接”部分)。

所以我想首先更一般地解释这个问题:


我正在使用PostgreSQL,但我肯定在支持DBMS的其他窗口函数(MS SQL Server,Oracle等)中也存在此问题。


Window functions可用于通过公用属性或值将某些值组合在一起。例如,您可以按日期对行进行分组。然后,您可以计算每个日期内的最大值,平均值或计数行数之类的值。

这可以通过定义PARTITION来实现。按日期分组将与PARTITION BY date_column一起使用。现在,您想执行一个需要在组内进行特殊排序的操作(计算行号或汇总一列)。可以通过PARTITON BY date_column ORDER BY an_attribute_column完成。

现在考虑考虑更好的时间序列分辨率。如果没有日期但是有时间戳怎么办。然后,您将无法再按时间列进行分组。但是,尽管如此,按添加顺序分析数据可能很重要(也许时间戳就是数据集的创建时间)。然后,您意识到一些连续的行具有相同的值,并且您希望通过该公用值对数据进行分组。但是线索是这些行具有不同的时间戳。

这里的问题是您无法执行PARTITION BY value_column。因为PARTITION BY首先要强制排序。因此,您的表将在分组之前由value_column进行排序,并且不再由时间戳进行排序。这样会产生出乎您意料的结果。

更笼统地说:问题是即使已排序的列不属于已创建分区的一部分,也要确保进行特殊排序


示例:

db<>fiddle

我有下表:

ts      val
100000  50
130100  30050
160100  60050 
190200  100
220200  30100 
250200  30100 
300000  300
500000  100
550000  1000  
600000  1000
650000  2000  
700000  2000
720000  2000
750000  300

我有一个问题,我必须对列val的所有绑定值进行分组。但是我想在ts之前保留订单。为此,我想为每个val组添加一个具有唯一ID的列

预期结果:

ts      val     group
100000  50      1
130100  30050   2
160100  60050   3
190200  100     4
220200  30100   5     \ same group
250200  30100   5     /
300000  300     6
500000  100     7
550000  1000    8     \ same group
600000  1000    8     /
650000  2000    9     \
700000  2000    9     | same group
720000  2000    9     /
750000  300     10

首次尝试是使用rank窗口函数,该函数通常可以完成此工作:

SELECT 
    *,
    rank() OVER (PARTITION BY val ORDER BY ts)
FROM 
    test

但是在这种情况下,这是行不通的,因为PARTITION BY子句首先通过分区列(在这种情况下为val)然后通过其ORDER BY列对表进行排序。因此,顺序为val, ts,而不是期望的顺序ts。因此结果当然不是预期的。

ts       val     rank
100000   50      1
190200   100     1
500000   100     2
300000   300     1
750000   300     2
550000   1000    1
600000   1000    2
650000   2000    1
700000   2000    2
720000   2000    3
130100   30050   1
220200   30100   1
250200   30100   2
160100   60050   1

问题是:如何通过ts 获取有关订单的组ID?


编辑:我在下面添加了自己的解决方案,但对此感到非常不舒服。似乎太复杂了。 我想知道是否有更好的方法来达到这个结果。

1 个答案:

答案 0 :(得分:0)

我自己提出了这个解决方案(希望别人会得到更好的解决方案):

demo:db<>fiddle

  1. ts排序
  2. 使用val窗口函数(https://www.postgresql.org/docs/current/static/tutorial-window.html)给出下一个lag
  3. 检查下一个和当前值是否相同。然后我可以打印出01
  4. 使用有序SUM汇总这些值。这将生成我要寻找的组。他们将val列进行分组,但通过ts列确保排序。

查询:

SELECT 
    *, 
    SUM(is_diff) OVER (ORDER BY ts) 
FROM (
    SELECT 
        *,
        CASE WHEN val = lag(val) over (order by ts) THEN 0 ELSE 1 END as is_diff
    FROM test 
)s

结果:

ts       val     is_diff   sum
100000   50      1         1
130100   30050   1         2
160100   60050   1         3
190200   100     1         4
220200   30100   1         5    \ group
250200   30100   0         5    /
300000   300     1         6
500000   100     1         7
550000   1000    1         8    \ group
600000   1000    0         8    /
650000   2000    1         9    \
700000   2000    0         9    | group
720000   2000    0         9    /
750000   300     1         10