如何用trino或impala在window函数中添加条件分区?
How to add conditional partition in a window function with trino or impala?
例如,我有如下数据集:
time
action
03:00:00
block
04:00:00
unblock
05:00:00
block
06:00:00
unblock
07:00:00
unblock
08:00:00
block
现在对于每一行,我想获取列 action
等于当前行时间之前的“块”的最后时间 。例如,对于time
等于“07:00:00”且action
等于“unblock”的第五行,最后一次action
等于“block”应该是第三行,预计时间是“05:00:00”。
我的最终预期结果是:
time
action
last_time
03:00:00
block
03:00:00
04:00:00
unblock
03:00:00
05:00:00
block
05:00:00
06:00:00
unblock
05:00:00
07:00:00
unblock
05:00:00
08:00:00
block
08:00:00
如何使用 window 函数 而不通过自身加入 来获得上述结果?
(p.s。如果不能达到上面的结果,下面的输出也可以:
time
action
last_time
03:00:00
block
NULL
04:00:00
unblock
03:00:00
05:00:00
block
03:00:00
06:00:00
unblock
05:00:00
07:00:00
unblock
05:00:00
08:00:00
block
05:00:00
一旦我们确定 action = 'block'
应该开始一个新的块并且我们识别了这些块 (block_no
),我们就可以使用 window 由 block_no
分区的函数] 找到每个块中的最小值 time
。
如果时间不是单调递增,我们可以使用FIRST_VALUE
window函数代替,如果我们有另一种排序方式,或者只是使用另一个case表达式来获取时间只有当action = 'block',这将使其他行保留为空,这很容易通过 MAX/MIN/etc.
忽略
然而,根据当前数据,我认为我们无法绕过这样的假设,即对于每个块之间的所有行,时间需要单调增加或至少从块到块增加。
试试这个:
WITH cte1 AS (
SELECT *, SUM(CASE WHEN action = 'block' THEN 1 END) OVER (ORDER BY time) AS block_no FROM test
)
SELECT *, MIN(time) OVER (PARTITION BY block_no) AS block_time FROM cte1
ORDER BY time
;
结果:
time
action
block_no
block_time
03:00:00
block
1
03:00:00
04:00:00
unblock
1
03:00:00
05:00:00
block
2
05:00:00
06:00:00
unblock
2
05:00:00
07:00:00
unblock
2
05:00:00
08:00:00
block
3
08:00:00
设置:
CREATE TABLE test (time varchar(20), action varchar(20));
INSERT INTO test VALUES
('03:00:00', 'block')
, ('04:00:00', 'unblock')
, ('05:00:00', 'block')
, ('06:00:00', 'unblock')
, ('07:00:00', 'unblock')
, ('08:00:00', 'block')
;
例如,我有如下数据集:
time | action |
---|---|
03:00:00 | block |
04:00:00 | unblock |
05:00:00 | block |
06:00:00 | unblock |
07:00:00 | unblock |
08:00:00 | block |
现在对于每一行,我想获取列 action
等于当前行时间之前的“块”的最后时间 。例如,对于time
等于“07:00:00”且action
等于“unblock”的第五行,最后一次action
等于“block”应该是第三行,预计时间是“05:00:00”。
我的最终预期结果是:
time | action | last_time |
---|---|---|
03:00:00 | block | 03:00:00 |
04:00:00 | unblock | 03:00:00 |
05:00:00 | block | 05:00:00 |
06:00:00 | unblock | 05:00:00 |
07:00:00 | unblock | 05:00:00 |
08:00:00 | block | 08:00:00 |
如何使用 window 函数 而不通过自身加入 来获得上述结果?
(p.s。如果不能达到上面的结果,下面的输出也可以:
time | action | last_time |
---|---|---|
03:00:00 | block | NULL |
04:00:00 | unblock | 03:00:00 |
05:00:00 | block | 03:00:00 |
06:00:00 | unblock | 05:00:00 |
07:00:00 | unblock | 05:00:00 |
08:00:00 | block | 05:00:00 |
一旦我们确定 action = 'block'
应该开始一个新的块并且我们识别了这些块 (block_no
),我们就可以使用 window 由 block_no
分区的函数] 找到每个块中的最小值 time
。
如果时间不是单调递增,我们可以使用FIRST_VALUE
window函数代替,如果我们有另一种排序方式,或者只是使用另一个case表达式来获取时间只有当action = 'block',这将使其他行保留为空,这很容易通过 MAX/MIN/etc.
然而,根据当前数据,我认为我们无法绕过这样的假设,即对于每个块之间的所有行,时间需要单调增加或至少从块到块增加。
试试这个:
WITH cte1 AS (
SELECT *, SUM(CASE WHEN action = 'block' THEN 1 END) OVER (ORDER BY time) AS block_no FROM test
)
SELECT *, MIN(time) OVER (PARTITION BY block_no) AS block_time FROM cte1
ORDER BY time
;
结果:
time | action | block_no | block_time |
---|---|---|---|
03:00:00 | block | 1 | 03:00:00 |
04:00:00 | unblock | 1 | 03:00:00 |
05:00:00 | block | 2 | 05:00:00 |
06:00:00 | unblock | 2 | 05:00:00 |
07:00:00 | unblock | 2 | 05:00:00 |
08:00:00 | block | 3 | 08:00:00 |
设置:
CREATE TABLE test (time varchar(20), action varchar(20));
INSERT INTO test VALUES
('03:00:00', 'block')
, ('04:00:00', 'unblock')
, ('05:00:00', 'block')
, ('06:00:00', 'unblock')
, ('07:00:00', 'unblock')
, ('08:00:00', 'block')
;