感谢Mike建议添加创建/插入语句。
create table test (
pid integer not null,
date date not null,
primary key (pid, date)
);
insert into test values
(1,'2014-10-1')
, (1,'2014-10-2')
, (1,'2014-10-3')
, (1,'2014-10-5')
, (1,'2014-10-7')
, (2,'2014-10-1')
, (2,'2014-10-2')
, (2,'2014-10-3')
, (2,'2014-10-5')
, (2,'2014-10-7');
我想添加一个新列,这是当前连胜的“天”。 所以结果看起来像:
pid | date | in_streak
-------|-----------|----------
1 | 2014-10-1 | 1
1 | 2014-10-2 | 2
1 | 2014-10-3 | 3
1 | 2014-10-5 | 1
1 | 2014-10-7 | 1
2 | 2014-10-2 | 1
2 | 2014-10-3 | 2
2 | 2014-10-4 | 3
2 | 2014-10-6 | 1
我一直在尝试使用
中的答案但是我无法弄清楚如何将dense_rank()
技巧与其他窗口函数一起使用来获得正确的结果。
答案 0 :(得分:10)
在此表的基础上构建(不使用SQL keyword "date"作为列名。):
CREATE TABLE tbl(
pid int
, the_date date
, PRIMARY KEY (pid, the_date)
);
查询:
SELECT pid, the_date
, row_number() OVER (PARTITION BY pid, grp ORDER BY the_date) AS in_streak
FROM (
SELECT *
, the_date - '2000-01-01'::date
- row_number() OVER (PARTITION BY pid ORDER BY the_date) AS grp
FROM tbl
) sub
ORDER BY pid, the_date;
从另一个date
中减去date
会产生integer
。由于您正在寻找连续几天,因此一个的每一行都会更大。如果我们从中减去row_number()
,则整个条纹最终会出现在grp
的同一组(pid
)中。然后,每组处理数量很简单。
grp
使用两次减法计算,这应该是最快的。一个同样快速的替代方案可能是:
the_date - row_number() OVER (PARTITION BY pid ORDER BY the_date) * interval '1d' AS grp
一次乘法,一次减法。字符串连接和转换更昂贵。使用EXPLAIN ANALYZE
进行测试。
请勿忘记在两个步骤中按pid
进行分区,否则您将无意中混合应该分开的群组。
使用子查询,因为它通常比CTE更快。这里没有什么是普通的子查询无法做到的。
既然你提到过:dense_rank()
显然不在这里是必要的。基本row_number()
完成了这项工作。
答案 1 :(得分:3)
如果在问题中包含CREATE TABLE语句和INSERT语句,您将获得更多关注。
create table test (
pid integer not null,
date date not null,
primary key (pid, date)
);
insert into test values
(1,'2014-10-1'), (1,'2014-10-2'), (1,'2014-10-3'), (1,'2014-10-5'),
(1,'2014-10-7'), (2,'2014-10-1'), (2,'2014-10-2'), (2,'2014-10-3'),
(2,'2014-10-5'), (2,'2014-10-7');
原则很简单。连续日期减去row_number()的连续日期是常量。您可以按常量分组,并在结果上使用dense_rank()。
with grouped_dates as (
select pid, date,
(date - (row_number() over (partition by pid order by date) || ' days')::interval)::date as grouping_date
from test
)
select * , dense_rank() over (partition by grouping_date order by date) as in_streak
from grouped_dates
order by pid, date
pid date grouping_date in_streak -- 1 2014-10-01 2014-09-30 1 1 2014-10-02 2014-09-30 2 1 2014-10-03 2014-09-30 3 1 2014-10-05 2014-10-01 1 1 2014-10-07 2014-10-02 1 2 2014-10-01 2014-09-30 1 2 2014-10-02 2014-09-30 2 2 2014-10-03 2014-09-30 3 2 2014-10-05 2014-10-01 1 2 2014-10-07 2014-10-02 1