【问题标题】:SQL - missing time in date rangeSQL - 日期范围内缺少时间
【发布时间】:2025-12-02 04:15:01
【问题描述】:

我的表格中有数据显示是否点火truefalse

|--------------------------------------|
|             date          | ignition |
|---------------------------|----------|
|2019-06-03 10:15:00.000000 | false    |
|---------------------------|----------|
|2019-06-03 10:30:00.000000 | false    |
|---------------------------|----------|
|2019-06-03 11:30:00.000000 | true     |
|---------------------------|----------|
|2019-06-03 11:45:00.000000 | false    |
|---------------------------|----------|
|2019-06-03 11:55:00.000000 | false    |
|---------------------------|----------|

我编写了显示日期期间点火状态的 SQL 查询

SELECT min(date) as date_from, max(date) as date_to, ignition
FROM (SELECT date,
             ignition,
             row_number() over (order by date) as seqnum,
             row_number() over (partition by ignition order by date) as seqnum_s
      FROM table_data
      WHERE date >= '2019-06-01T21:00:00.000Z' AND date <= '2019-06-09T20:59:59.999Z'
    ) as raw_data
GROUP BY (seqnum - seqnum_s), ignition
ORDER BY min(date);

查询结果:

|---------------------------|----------------------------|----------|
|             date_from     | date_to                    | ignition |
|---------------------------|----------------------------|----------|
|2019-06-03 10:15:00.000000 | 2019-06-03 10:30:00.000000 | false    |
|---------------------------|----------------------------|----------|
|2019-06-03 11:30:00.000000 | 2019-06-03 11:30:00.000000 | true     |
|---------------------------|----------------------------|----------|
|2019-06-03 11:45:00.000000 | 2019-06-03 11:55:00.000000 | false    |
|---------------------------|----------------------------|----------|

但结果中缺少从 10:3011:30 和从 11:3011:45 的日期。我应该得到这个:

|---------------------------|----------------------------|----------|
|             date_from     | date_to                    | ignition |
|---------------------------|----------------------------|----------|
|2019-06-03 10:15:00.000000 | 2019-06-03 11:30:00.000000 | false    |
|---------------------------|----------------------------|----------|
|2019-06-03 11:30:00.000000 | 2019-06-03 11:45:00.000000 | true     |
|---------------------------|----------------------------|----------|
|2019-06-03 11:45:00.000000 | 2019-06-03 11:55:00.000000 | false    |
|---------------------------|----------------------------|----------|

问题在于,在聚合到一个块期间(例如,后面的 5 行 false)他只查看 false 的最后一行来确定块的结束时间,而不是后面的第一行(第一行是真的)

【问题讨论】:

    标签: sql postgresql date-range gaps-and-islands


    【解决方案1】:

    好的,只需使用窗口函数lead获取下一行的date_from的值,然后将此值设置为当前行的date_to,SQL如下:

    select 
        date_from,
        case when lead(date_from,1) over(order by date_from) is null then date_to else lead(date_from,1) over(order by date_from) end as date_to,
        ignition
    from (
    SELECT min(date) as date_from, max(date) as date_to, ignition
    FROM (SELECT date,
                 ignition,
                 row_number() over (order by date) as seqnum,
                 row_number() over (partition by ignition order by date) as seqnum_s
          FROM table_data
          WHERE date >= '2019-06-01T21:00:00.000Z' AND date <= '2019-06-09T20:59:59.999Z'
        ) as raw_data
    GROUP BY (seqnum - seqnum_s), ignition
    ORDER BY min(date)
    ) tmp;
          date_from      |       date_to       | ignition 
    ---------------------+---------------------+----------
     2019-06-03 10:15:00 | 2019-06-03 11:30:00 | f
     2019-06-03 11:30:00 | 2019-06-03 11:45:00 | t
     2019-06-03 11:45:00 | 2019-06-03 11:55:00 | f
    (3 rows)
    

    【讨论】:

    • 你使用row_number 两次的方法很巧妙,这有助于解决我的一个历史问题。谢谢! :D
    【解决方案2】:

    更简单的表达方式是:

    SELECT min(date) as date_from,
           lead(min(date)) over (order by min(date)) as date_to,
           ignition
    FROM (SELECT d.*,
                 row_number() over (order by date) as seqnum,
                 row_number() over (partition by ignition order by date) as seqnum_s
          FROM table_data d
          WHERE date >= '2019-06-01T21:00:00.000Z' AND date < '2019-06-09T21:00:00Z'
         ) as raw_data
    GROUP BY (seqnum - seqnum_s), ignition
    ORDER BY min(date)
    

    【讨论】: