【问题标题】:Calculate time period overlap and use aggregation to find out remaining time计算时间段重叠并使用聚合找出剩余时间
【发布时间】:2021-04-12 00:12:53
【问题描述】:

我有一个timings 表,其定义如下:

   Column   |            Type             | Collation | Nullable | Default
------------+-----------------------------+-----------+----------+---------
 start_time | timestamp without time zone |           | not null |
 end_time   | timestamp without time zone |           | not null |
 type       | integer                     |           | not null |

它有这样的数据:

     start_time      |      end_time       | type
---------------------+---------------------+------
 2020-11-10 23:50:00 | 2020-11-10 23:50:17 |    1
 2020-11-11 23:50:17 | 2020-11-11 23:50:34 |    1
 2020-11-10 23:50:08 | 2020-11-10 23:50:15 |    2
 2020-11-15 23:50:08 | 2020-11-16 23:50:08 |    2

计算

对于type = 1 所在的每个日期,我想计算start_timeend_time 之间的时间差(以秒为单位)。所以对于 第 1 行,时间差是 17 秒

然后,我想减去/删除与type = 2 相同的日期的任何时间重叠。例如第 3 行 相差 7 秒,start_timeend_time 都属于 第 1 行start_timeend_time(它们是同一日期)。所以我们从17 中删除了7,剩下的是10

如果带有type = 2 的行的日期与带有type = 1 的任何日期都不匹配,则可以忽略该行(例如,完全忽略第4 行)。

我希望所有日期都发生这种情况,然后总结所有日期的总数。在这种情况下,我总共会有 27,因为我们只需要添加 第 2 行 的时间差 (17),所以10 + 17 给了我们@987654339 @。

期望的输出

   type_one_seconds   
----------------------
 27

我以前没有在 SQL 中使用过时间段计算,我不确定从哪里开始。

假设

我有一个评论/问题消失了。它询问当我有两行具有相同日期的type = 1 时会发生什么。我必须检查我的数据并回复你,看看是否会发生这种情况。现在,让我们假设每个type = 1 都有一个唯一的日期。所以我们只是总结了他们的时差。但我会检查并确定。

我创建了一个db-fiddle

【问题讨论】:

  • 很好的问题。为详细信息和小提琴链接投票。我只有一个问题。如果任何类型 2 的行与类型 1 的行部分重叠,是否会扣除部分重叠的时间?
  • 谢谢@KaziMohammadAliNur,是的,任何部分重叠的时间都将被扣除。
  • 好的。那么请让我修改我的答案。
  • 如果 type=2 的行的 start_time 和 end_time 没有完全落在 type=1 的行的 start_time 和 end_time 中,而只是部分落在 start_time 和 end_time 中怎么办?
  • @forpas 然后只计算落入的时间。

标签: sql postgresql


【解决方案1】:

您可以尝试将条件拆分为CASEs:

SELECT sum(sec) FROM (
SELECT 
  CASE 
    WHEN type = 1 THEN
      EXTRACT(SECOND FROM end_time-start_time)
    WHEN type = 2 AND NOT EXISTS (SELECT 1 FROM timings 
                                  WHERE type = 1 AND
                                    (start_time::date = t.start_time::date OR
                                     end_time::date = t.end_time::date)) THEN 0
    WHEN type = 2 AND EXISTS (SELECT 1 FROM timings
                              WHERE start_time < t.start_time AND 
                                    end_time > t.end_time AND
                                    type = 1) THEN        
      EXTRACT(SECOND FROM end_time-start_time) * -1    
  END AS sec
FROM timings t) j;

演示:db&lt;&gt;fiddle

【讨论】:

    【解决方案2】:

    假设type 中的行之间没有时间重叠,我将使用tsrange 数据类型来利用其相交* 和重叠&amp;&amp; 运算符:

    with ranges as (
      select type, tsrange(start_time, end_time) as rng
        from timings
    ), find_neg as (
      select n.type, p.rng * n.rng as rng
        from ranges p
             join ranges n
               on n.type = 2
              and p.type = 1
              and n.rng && p.rng
      union all
      select type, rng
        from ranges
       where type = 1
    )
    select sum(extract(epoch from upper(rng) - lower(rng))) filter (where type = 1)
           - sum(extract(epoch from upper(rng) - lower(rng))) filter (where type = 2)
      from find_neg;
    
    

    如果type 中存在重叠,那么我将首先使用间隙和孤岛策略将它们合并。

    Fiddle

    【讨论】:

      【解决方案3】:

      使用表的自联接。
      带有type = 1 的每一行将通过LEFT 连接到带有type = 2 的对应行,并使用CASE 表达式,您可以计算每种类型的end_timestart_time 的差异并减去。
      函数LEAST()GREATEST() 将用于在type = 1 对应行的时间限制内缩小type = 2 行的间隔。
      最后与SUM()聚合:

      SELECT SUM(
                  EXTRACT(SECOND FROM t1.end_time - t1.start_time) -
                  CASE  
                    WHEN t2.type IS NULL THEN 0 
                    ELSE EXTRACT(SECOND FROM LEAST(t1.end_time, t2.end_time) - GREATEST(t1.start_time, t2.start_time)) 
                  END  
                ) type_one_seconds
      FROM timings t1 LEFT JOIN timings t2
      ON t2.start_time::date = t1.start_time::date AND t2.type = 2 
      WHERE t1.type = 1
      

      请参阅demo

      【讨论】:

        【解决方案4】:

        如果时间范围之间有部分部分重叠:

        这里tsrange()用于找出两个时间范围之间的重叠时间。

         -- create the table
         create table timings (
           start_time timestamp not null,
           end_time timestamp not null,
           type integer not null
         );
         
         
         -- insert data into the table
         insert into timings (start_time, end_time, type) values
            ('2020-11-10 23:50:00', '2020-11-10 23:50:17' ,1),
            ('2020-11-11 23:50:17', '2020-11-11 23:50:34' ,1),
            ('2020-11-10 23:50:08', '2020-11-10 23:50:15' ,2),
            ('2020-11-10 23:50:15', '2020-11-10 23:50:25' ,2),
            ('2020-11-15 23:50:08', '2020-11-16 23:50:08' ,2);
        

        查询:

         WITH TIMEONE AS
             (
                 select *, coalesce( EXTRACT(EPOCH FROM (t1.end_time - t1.start_time)),0) timeone 
                 from timings  t1 
                 where type=1
             ),
         
         timeonetwo as 
         (
             select *,
                 (select sum( coalesce( EXTRACT(EPOCH FROM (upper(diff) - lower(diff))),0) ) from
                 (select tsrange(t2.start_time, t2.end_time, '[]') * tsrange(t1.start_time, t1.end_time, '[]')diff
             from timings t2 where type=2)t)timetwosum
         from timeone t1
         )
         select sum(timeone-timetwosum)type_one_seconds  from timeonetwo
        

        输出:

        type_one_seconds
        25

        dbhere

        如果没有部分重叠:

         -- create the table
         create table timings (
           start_time timestamp not null,
           end_time timestamp not null,
           type integer not null
         );
         
         
         -- insert data into the table
         insert into timings (start_time, end_time, type) values
            ('2020-11-10 23:50:00', '2020-11-10 23:50:17' ,1),
            ('2020-11-11 23:50:17', '2020-11-11 23:50:34' ,1),
            ('2020-11-10 23:50:08', '2020-11-10 23:50:15' ,2),
            ('2020-11-15 23:50:08', '2020-11-16 23:50:08' ,2);
        

        查询:

         select sum(timeone-timetwosum)type_one_seconds  
         from
         (
             select coalesce( EXTRACT(EPOCH FROM (t1.end_time - t1.start_time)),0) timeone ,
             coalesce((select  sum(coalesce( EXTRACT(EPOCH FROM (t2.end_time - t2.start_time)),0) )
             from timings t2 where t2.type=2 
             and t2.start_time =t1.start_time and t2.end_time<=t1.end_time),0) timetwosum
         
             from timings  t1 
             where type=1
         )t
        

        输出:

        type_one_seconds
        27

        dbhere

        【讨论】:

        • 更新后,我得到34作为输出,但我认为它应该保持27,我错过了什么吗?
        猜你喜欢
        • 1970-01-01
        • 2014-07-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多