SQL-如何识别给定数据中的1小时时段孤岛?

SQL-如何识别给定数据中的1小时时段孤岛?,sql,gaps-and-islands,Sql,Gaps And Islands,目标是接受收到的第一次投诉,并在第一次投诉后的1小时内拒绝收到的所有投诉。例如,我有下面的数据 抱怨的 日期时间 1. 2019年12月24日下午1:07 2. 2019年12月24日下午1:20 3. 2019年12月24日下午1:40 4. 2019年12月24日下午2:00 5. 2019年12月24日下午2:10 6. 2019年12月24日下午2:12 7. 2019年12月24日下午2:50 8. 2019年12月24日下午2:55 9 2019年12月24日下午3:00 10 20

目标是接受收到的第一次投诉,并在第一次投诉后的1小时内拒绝收到的所有投诉。例如,我有下面的数据

抱怨的 日期时间 1. 2019年12月24日下午1:07 2. 2019年12月24日下午1:20 3. 2019年12月24日下午1:40 4. 2019年12月24日下午2:00 5. 2019年12月24日下午2:10 6. 2019年12月24日下午2:12 7. 2019年12月24日下午2:50 8. 2019年12月24日下午2:55 9 2019年12月24日下午3:00 10 2019年12月24日下午3:08 11 2019年12月24日下午4:00 12 2019年12月24日下午4:50 13 2019年12月24日下午7:00 14 2019年12月26日下午7:01
通常情况下,您可以应用超前/滞后,但此处不适用。超前/滞后是所需的不可预测范围的问题。同样,递归CTE似乎不可行,因为它需要在递归部分使用MIN函数;然而,这是不允许的。因为函数是令人满意的,所以最好的函数可能是返回一个表。看


不幸的是,由于它涉及游标循环,因此对于大数据量来说,性能将是一个问题。

这是一个简单的方法。将每个datetime截断一小时,然后在每个小时内将第一个或最小datetime作为接受,将另一个作为拒绝

p.S我使用表名作为投诉更改。在Postgresql 8中测试。

SELECT ComplaintID,DateTime,CASE WHEN row_number() over(partition by hour order by 
DateTime)=1 THEN 'Accept' else 'Reject' end as Status from 
(select ComplaintID,DateTime ,date_trunc('hour',DateTime)as hour  from complaint)A ;
  • 使用投诉ID的连续性,查询为:

  • 我认为这需要一个递归CTE——这对大量数据来说是无效的。当你说“在SQL中”时,你实际上是指纯SQL,还是说它必须发生在DBMS中?它必须是单个查询,还是可以是一个过程?限制的原因是什么?过程/UDF也可以。sql-server或postgres?查看
    LAG()
    LEAD()
    分析sql函数,并尝试在数据中找到
    会话的开始,以获得结果。或者我认为你误解了目标。请查看所需输出以更好地理解。根据您的输出图像,2019年12月24日下午1:07接受,然后2019年12月24日下午3:08拒绝应为“接受”,因为在第一次投诉的2小时后,从3:08到下午3:07是下一个小时。根据我第一次的理解,一小时应该开始,在这种情况下,每一小时都是从2:07,3:07,4:07开始的?
    
    SELECT ComplaintID,DateTime,CASE WHEN row_number() over(partition by hour order by 
    DateTime)=1 THEN 'Accept' else 'Reject' end as Status from 
    (select ComplaintID,DateTime ,date_trunc('hour',DateTime)as hour  from complaint)A ;
    
    with recursive cte as (
      select 1 ComplaintID, min(DateTime) DateTime,
        min(DateTime) prev
        from main_table
      union all
      select t2.ComplaintID, t2.DateTime,
        case when t1.prev + interval '1 hour' < t2.DateTime
             then t2.DateTime else t1.prev end
        from cte t1 join main_table t2
        on t1.ComplaintID+1 = t2.ComplaintID
    )
    select ComplaintID, DateTime,
      case when DateTime=prev
        then 'Accept' else 'Reject' end Status
      from cte
      order by ComplaintID
    
    with recursive cte as (
      (
        select ComplaintID, DateTime, 'Accept' Status
          from main_table order by DateTime limit 1
      )
      union all
      (
        select t2.ComplaintID, t2.DateTime, 'Accept'
          from cte t1 join main_table t2
          on t1.DateTime + interval '1 hour' < t2.DateTime
          order by t2.DateTime limit 1
      )
    )
    select t1.ComplaintID, t1.DateTime, coalesce(t2.Status, 'Reject') Status
      from main_table t1 left join cte t2
      on t1.ComplaintID=t2.ComplaintID
      order by t1.ComplaintID