PostgreSQL - min/max 连续的一天 - 表包含多个相等的日子
PostgreSQL - consecutive day with min/max - tables contains multiple equal days
几个小时以来我一直在尝试和谷歌搜索,但没有找到有效的解决方案。这是我的问题:
我有一个 table 结构如下:
id SERIAL,数据 DATE,其他信息 VARCHAR(50)
数据可以有相同的日期条目:
id datum otherinfo
1 2019-12-28 testdata1
2 2019-12-28 testdata2
3 2019-12-29 testdata3
4 2019-12-29 testdata4
5 2019-12-31 testdata5-begin longest consecutive days
6 2019-12-31 testdata6
7 2020-01-01 testdata7
8 2020-01-01 testdata8
9 2020-01-02 testdata9
10 2020-01-03 testdata10
11 2020-01-04 testdata11
12 2020-01-04 testdata12
13 2020-01-05 testdata13-end longest consecutive days
14 2020-01-22 testdata14
15 2020-01-29 testdata15
16 2020-01-30 testdata16
我有兴趣获取包含开始日期和结束日期的连续天数。像这样的输出:
count | date MIN | date MAX
6 2019-12-31 2020-01-05
2 2019-12-28 2019-12-29
2 2020-01-29 2020-01-30
我在 Whosebug 上找到了一些解决方法,但它似乎总是与多个相等的日期条目发生冲突。
我在以下 SQL 查询中取得的最大成功:
SELECT COUNT(*) -1 "count", MAX(datum), MIN(datum) FROM (SELECT *, date(datum) - row_number() OVER (PARTITION BY datum ORDER BY date(datum)) * INTERVAL '1 day' "filter" FROM table ) t1 GROUP BY filter HAVING COUNT(*) -1 > 0 ORDER BY count DESC
遗憾的是,它给出了连续天数的错误计数,并且计算的天数甚至与开始/结束日期不匹配。
非常感谢您的想法
马丁
这是一个缺口和孤岛问题。您可以使用传统的解决方案:
select
(max(datum) - min(datum)) + 1 as cnt,
min(datum) as date_min,
max(datum) as date_max
from (
select x.*, sum(i) over(order by datum) as g
from (
select t.*,
case when datum > lag(datum) over(order by datum) + 1
then 1 else 0 end as i
from t
) x
) y
group by g
这称为间隙和孤岛问题。解决此问题的典型方法是对行进行编号(此处使用 DENSE_RANK
,因为日期尚不唯一)并将这些数字与相对位置进行比较。当您处理日期时,我们的立场是从固定日期算起的天数。
select count(*), min(datum), max(datum)
from
(
select distinct
datum,
datum - date '1900-01-01' - dense_rank() over (order by datum) as grp
from mytable
) grouped
group by grp
order by grp;
演示:https://dbfiddle.uk/?rdbms=postgres_14&fiddle=f89e579db31ffd956fdea5d437625b68
如果您觉得它更具可读性,您可以添加一个步骤:首先使行不同,然后 运行 通过行并获得位置和行号的差异(然后您可以使用 ROW_NUMBER
而不是 DENSE_RANK
),然后聚合。
这些是来自 @TheImpaler 的 和 @Thorsten Kettner 的 答案的附加 MySQL 版本的答案。 FIDDLE
@TheImpaler 的解决方案
select
datediff(max(datum), min(datum)) + 1 as cnt,
min(datum) as date_min,
max(datum) as date_max
from (
select x.*, sum(i) over(order by datum) as g
from (
select t.*,
case when datediff(datum, lag(datum) over(order by datum)) > 1
then 1 else 0 end as i
from t
) x
) y
group by g;
@Thorsten Kettner 的解决方案
select
count(*) as cnt,
min(datum) as date_min,
max(datum) as date_max
from
(
select distinct
datum,
datediff(datum, date('1900-01-01')) - dense_rank() over (order by datum) as grp
from t order by datum
) grouped
group by grp
order by grp;
几个小时以来我一直在尝试和谷歌搜索,但没有找到有效的解决方案。这是我的问题:
我有一个 table 结构如下: id SERIAL,数据 DATE,其他信息 VARCHAR(50)
数据可以有相同的日期条目:
id datum otherinfo
1 2019-12-28 testdata1
2 2019-12-28 testdata2
3 2019-12-29 testdata3
4 2019-12-29 testdata4
5 2019-12-31 testdata5-begin longest consecutive days
6 2019-12-31 testdata6
7 2020-01-01 testdata7
8 2020-01-01 testdata8
9 2020-01-02 testdata9
10 2020-01-03 testdata10
11 2020-01-04 testdata11
12 2020-01-04 testdata12
13 2020-01-05 testdata13-end longest consecutive days
14 2020-01-22 testdata14
15 2020-01-29 testdata15
16 2020-01-30 testdata16
我有兴趣获取包含开始日期和结束日期的连续天数。像这样的输出:
count | date MIN | date MAX
6 2019-12-31 2020-01-05
2 2019-12-28 2019-12-29
2 2020-01-29 2020-01-30
我在 Whosebug 上找到了一些解决方法,但它似乎总是与多个相等的日期条目发生冲突。
我在以下 SQL 查询中取得的最大成功:
SELECT COUNT(*) -1 "count", MAX(datum), MIN(datum) FROM (SELECT *, date(datum) - row_number() OVER (PARTITION BY datum ORDER BY date(datum)) * INTERVAL '1 day' "filter" FROM table ) t1 GROUP BY filter HAVING COUNT(*) -1 > 0 ORDER BY count DESC
遗憾的是,它给出了连续天数的错误计数,并且计算的天数甚至与开始/结束日期不匹配。
非常感谢您的想法
马丁
这是一个缺口和孤岛问题。您可以使用传统的解决方案:
select
(max(datum) - min(datum)) + 1 as cnt,
min(datum) as date_min,
max(datum) as date_max
from (
select x.*, sum(i) over(order by datum) as g
from (
select t.*,
case when datum > lag(datum) over(order by datum) + 1
then 1 else 0 end as i
from t
) x
) y
group by g
这称为间隙和孤岛问题。解决此问题的典型方法是对行进行编号(此处使用 DENSE_RANK
,因为日期尚不唯一)并将这些数字与相对位置进行比较。当您处理日期时,我们的立场是从固定日期算起的天数。
select count(*), min(datum), max(datum)
from
(
select distinct
datum,
datum - date '1900-01-01' - dense_rank() over (order by datum) as grp
from mytable
) grouped
group by grp
order by grp;
演示:https://dbfiddle.uk/?rdbms=postgres_14&fiddle=f89e579db31ffd956fdea5d437625b68
如果您觉得它更具可读性,您可以添加一个步骤:首先使行不同,然后 运行 通过行并获得位置和行号的差异(然后您可以使用 ROW_NUMBER
而不是 DENSE_RANK
),然后聚合。
这些是来自 @TheImpaler 的 和 @Thorsten Kettner 的 答案的附加 MySQL 版本的答案。 FIDDLE
@TheImpaler 的解决方案
select
datediff(max(datum), min(datum)) + 1 as cnt,
min(datum) as date_min,
max(datum) as date_max
from (
select x.*, sum(i) over(order by datum) as g
from (
select t.*,
case when datediff(datum, lag(datum) over(order by datum)) > 1
then 1 else 0 end as i
from t
) x
) y
group by g;
@Thorsten Kettner 的解决方案
select
count(*) as cnt,
min(datum) as date_min,
max(datum) as date_max
from
(
select distinct
datum,
datediff(datum, date('1900-01-01')) - dense_rank() over (order by datum) as grp
from t order by datum
) grouped
group by grp
order by grp;