S-M*_*Man 6 sql postgresql window-functions
免责声明:显示的问题比我最初预期的要普遍得多。下面的例子取自另一个问题的解决方案。但是现在我正在使用这个示例来解决更多问题 - 主要与时间序列有关(查看右侧栏中的“链接”部分)。
所以我想先更一般地解释这个问题:
我正在使用 PostgreSQL,但我确信这个问题也存在于其他支持 DBMS 的窗口函数(MS SQL Server、Oracle 等)中。
窗口函数可用于通过公共属性或值将某些值组合在一起。例如,您可以按日期对行进行分组。然后您可以计算每个日期内的最大值或平均值或计数行或其他任何内容。
这可以通过定义一个PARTITION. 按日期分组将适用于PARTITION BY date_column. 现在,您想要在您的组中执行一个需要特殊顺序的操作(计算行号或对列求和)。这可以通过PARTITON BY date_column ORDER BY an_attribute_column.
现在考虑更精细的时间序列分辨率。如果您没有日期但有时间戳怎么办。然后你不能再按时间列分组。但是,按添加顺序分析数据可能很重要(也许时间戳是数据集的创建时间)。然后您意识到一些连续的行具有相同的值,并且您希望按此公共值对数据进行分组。但线索是行有不同的时间戳。
这里的问题是你不能做一个PARTITION BY value_column. 因为先PARTITION BY强制排序。因此,您的表将按value_column分组前排序,不再按时间戳排序。这会产生您意想不到的结果。
更一般地说:问题是即使有序列不是创建的分区的一部分,也要确保特殊排序。
例子:
我有下表:
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
Run Code Online (Sandbox Code Playgroud)
我有一个问题,我必须对列的所有绑定值进行分组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
Run Code Online (Sandbox Code Playgroud)
第一次尝试是使用rank通常可以完成这项工作的窗口函数:
SELECT
*,
rank() OVER (PARTITION BY val ORDER BY ts)
FROM
test
Run Code Online (Sandbox Code Playgroud)
但在这种情况下,这不起作用,因为该PARTITION BY子句首先按其分区列(val在本例中)然后按其ORDER BY列对表进行排序。所以订单是 byval, ts而不是预期的 order by 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
Run Code Online (Sandbox Code Playgroud)
问题是:如何获取与订单相关的组 IDts?
编辑:我在下面添加了自己的解决方案,但我对此感到非常不舒服。好像太复杂了 我想知道是否有更好的方法来实现这个结果。
我自己想出了这个解决方案(希望其他人能得到更好的解决方案):
tsval值lag(https://www.postgresql.org/docs/current/static/tutorial-window.html)0或 a1SUM。这会生成我正在寻找的组。他们对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
Run Code Online (Sandbox Code Playgroud)
结果:
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
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1632 次 |
| 最近记录: |