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;