如何对非空值重置的空值执行 运行 计数
How to perform a running count on null values that resets on non-null values
我正在尝试计算执行连续空值 运行 计数的列,但 运行 计数将在非空值时重置。
我目前正在尝试在此版本的 redshift 上实现此目的:
PostgreSQL 8.0.2 on i686-pc-linux-gnu,由 GCC gcc (GCC) 3.4.2 20041017 (Red Hat 3.4.2-6.fc3)、Redshift 1.0.8187 编译
我尝试使用此 window 函数,但这只是不断增加每个空值的数字。
ROW_NUMBER() OVER (PARTITION BY ID, VAL ORDER BY VAL ROWS UNBOUNDED PRECEDING)
例如,如果我有这样的数据集:
id | date | val
----+-------+-------
1 | 1/1 | NULL
1 | 1/2 | NULL
1 | 1/3 | NULL
1 | 1/4 | 1
1 | 1/5 | NULL
1 | 1/6 | NULL
1 | 1/7 | 1
2 | 1/8 | 2
2 | 1/9 | NULL
2 | 1/1 | NULL
2 | 1/2 | 1
2 | 1/3 | NULL
2 | 1/4 | 0
2 | 1/5 | NULL
2 | 1/6 | NULL
我希望输出如下所示:
id | date | val | foo
----+-------+-------+-------
1 | 1/1 | NULL | 1
1 | 1/2 | NULL | 2
1 | 1/3 | NULL | 3
1 | 1/4 | 1 |
1 | 1/5 | NULL | 1
1 | 1/6 | NULL | 2
1 | 1/7 | 1 |
2 | 1/8 | 2 |
2 | 1/9 | NULL | 1
2 | 1/1 | NULL | 2
2 | 1/2 | 1 |
2 | 1/3 | NULL | 1
2 | 1/4 | 0 |
2 | 1/5 | NULL | 1
2 | 1/6 | NULL | 2
初学者
我认为您的样本数据存在问题,在下面突出显示的记录中:
id | date | val | foo
----+-------+-------+-------
1 | 1/1 | NULL | 1
1 | 1/2 | NULL | 2
1 | 1/3 | NULL | 3
1 | 1/4 | 1 |
1 | 1/5 | NULL | 1
1 | 1/6 | NULL | 2
1 | 1/7 | 1 |
2 | 1/8 | 2 | --> this record is not in sequence
2 | 1/9 | NULL | 1 --> neither this one
2 | 1/1 | NULL | 2 --> so this record should have foo = 1, not 2
2 | 1/2 | 1 |
2 | 1/3 | NULL | 1
2 | 1/4 | 0 |
2 | 1/5 | NULL | 1
2 | 1/6 | NULL | 2
我只是从数据集中删除了这三个记录。如果您对此不满意,请不要继续阅读...
回答
这是间隙和孤岛问题的变体。为了解决这个问题,想法是建立由连续的空记录组成的组。为此,我们在两个不同的分区上计算 row_number()
s(id
vs id
and null/not null val
).行号之间的差异定义了组。
然后,剩下要做的就是为每个记录分配新的行号,该记录在其所属的组中具有空 val
。
查询:
select
id,
date,
val,
case when val is null
then row_number() over(partition by id, rn1 - rn2 order by date)
else null
end foo
from (
select
t.*,
row_number()
over(order by id, date) rn1,
row_number()
over(partition by id, case when val is null then 1 else 0 end order by date ) rn2
from mytable t
) t
order by id, date
| id | date | val | foo |
| --- | ------------------------ | --- | --- |
| 1 | 2019-01-01T00:00:00.000Z | | 1 |
| 1 | 2019-01-02T00:00:00.000Z | | 2 |
| 1 | 2019-01-03T00:00:00.000Z | | 3 |
| 1 | 2019-01-04T00:00:00.000Z | 1 | |
| 1 | 2019-01-05T00:00:00.000Z | | 1 |
| 1 | 2019-01-06T00:00:00.000Z | | 2 |
| 1 | 2019-01-07T00:00:00.000Z | 1 | |
| 2 | 2019-01-02T00:00:00.000Z | 1 | |
| 2 | 2019-01-03T00:00:00.000Z | | 1 |
| 2 | 2019-01-04T00:00:00.000Z | 0 | |
| 2 | 2019-01-05T00:00:00.000Z | | 1 |
| 2 | 2019-01-06T00:00:00.000Z | | 2 |
我正在尝试计算执行连续空值 运行 计数的列,但 运行 计数将在非空值时重置。
我目前正在尝试在此版本的 redshift 上实现此目的:
PostgreSQL 8.0.2 on i686-pc-linux-gnu,由 GCC gcc (GCC) 3.4.2 20041017 (Red Hat 3.4.2-6.fc3)、Redshift 1.0.8187 编译
我尝试使用此 window 函数,但这只是不断增加每个空值的数字。
ROW_NUMBER() OVER (PARTITION BY ID, VAL ORDER BY VAL ROWS UNBOUNDED PRECEDING)
例如,如果我有这样的数据集:
id | date | val
----+-------+-------
1 | 1/1 | NULL
1 | 1/2 | NULL
1 | 1/3 | NULL
1 | 1/4 | 1
1 | 1/5 | NULL
1 | 1/6 | NULL
1 | 1/7 | 1
2 | 1/8 | 2
2 | 1/9 | NULL
2 | 1/1 | NULL
2 | 1/2 | 1
2 | 1/3 | NULL
2 | 1/4 | 0
2 | 1/5 | NULL
2 | 1/6 | NULL
我希望输出如下所示:
id | date | val | foo
----+-------+-------+-------
1 | 1/1 | NULL | 1
1 | 1/2 | NULL | 2
1 | 1/3 | NULL | 3
1 | 1/4 | 1 |
1 | 1/5 | NULL | 1
1 | 1/6 | NULL | 2
1 | 1/7 | 1 |
2 | 1/8 | 2 |
2 | 1/9 | NULL | 1
2 | 1/1 | NULL | 2
2 | 1/2 | 1 |
2 | 1/3 | NULL | 1
2 | 1/4 | 0 |
2 | 1/5 | NULL | 1
2 | 1/6 | NULL | 2
初学者
我认为您的样本数据存在问题,在下面突出显示的记录中:
id | date | val | foo
----+-------+-------+-------
1 | 1/1 | NULL | 1
1 | 1/2 | NULL | 2
1 | 1/3 | NULL | 3
1 | 1/4 | 1 |
1 | 1/5 | NULL | 1
1 | 1/6 | NULL | 2
1 | 1/7 | 1 |
2 | 1/8 | 2 | --> this record is not in sequence
2 | 1/9 | NULL | 1 --> neither this one
2 | 1/1 | NULL | 2 --> so this record should have foo = 1, not 2
2 | 1/2 | 1 |
2 | 1/3 | NULL | 1
2 | 1/4 | 0 |
2 | 1/5 | NULL | 1
2 | 1/6 | NULL | 2
我只是从数据集中删除了这三个记录。如果您对此不满意,请不要继续阅读...
回答
这是间隙和孤岛问题的变体。为了解决这个问题,想法是建立由连续的空记录组成的组。为此,我们在两个不同的分区上计算 row_number()
s(id
vs id
and null/not null val
).行号之间的差异定义了组。
然后,剩下要做的就是为每个记录分配新的行号,该记录在其所属的组中具有空 val
。
查询:
select
id,
date,
val,
case when val is null
then row_number() over(partition by id, rn1 - rn2 order by date)
else null
end foo
from (
select
t.*,
row_number()
over(order by id, date) rn1,
row_number()
over(partition by id, case when val is null then 1 else 0 end order by date ) rn2
from mytable t
) t
order by id, date
| id | date | val | foo |
| --- | ------------------------ | --- | --- |
| 1 | 2019-01-01T00:00:00.000Z | | 1 |
| 1 | 2019-01-02T00:00:00.000Z | | 2 |
| 1 | 2019-01-03T00:00:00.000Z | | 3 |
| 1 | 2019-01-04T00:00:00.000Z | 1 | |
| 1 | 2019-01-05T00:00:00.000Z | | 1 |
| 1 | 2019-01-06T00:00:00.000Z | | 2 |
| 1 | 2019-01-07T00:00:00.000Z | 1 | |
| 2 | 2019-01-02T00:00:00.000Z | 1 | |
| 2 | 2019-01-03T00:00:00.000Z | | 1 |
| 2 | 2019-01-04T00:00:00.000Z | 0 | |
| 2 | 2019-01-05T00:00:00.000Z | | 1 |
| 2 | 2019-01-06T00:00:00.000Z | | 2 |