选择排名最高的重叠片段SQL

时间:2016-07-20 14:12:44

标签: sql sql-server-2008-r2 ranking

我正在寻找一种方法来选择排名最高的重叠片段。表格看起来像这样:

CODE   START                STOP                 RANK
shift  2016-07-20 05:00 AM  2016-07-20 08:00 AM  5 
late   2016-07-20 05:00 AM  2016-07-20 05:08 AM  1
break  2016-07-20 06:00 AM  2016-07-20 06:30 AM  2

这就是我想要的输出:

CODE   START                STOP                 
late   2016-07-20 05:00 AM  2016-07-20 05:08 AM   
shift  2016-07-20 05:08 AM  2016-07-20 06:00 AM  
break  2016-07-20 06:00 AM  2016-07-20 06:30 AM
shift  2016-07-20 06:30 AM  2016-07-20 08:00 AM  

所以我只想看看排名最高的片段对这个人的状态有什么看法,但如果他们没有标准“转变”部分以外的任何其他州,那么只是表明他们正在轮班

有意义吗?请拍摄任何问题或建议的解决方案。我现在似乎无法想到任何事情。我可以选择排名靠前的细分,但不能选择重叠细分。

编辑:正如您在所需输出中看到的那样, shift 段被后期段覆盖从05:00 AM到05:08 AM,排名较高(排名较低,排名较高,排名较高),但从早上05:08起,由于没有任何段覆盖它,我们将返回默认段< em> shift 从05:08 AM到06:00 AM。

然后在06:00 AM和06:30 AM之间有一个预定的中断段,它再次覆盖 shift 段。完成此操作后,我们会在 shift 结束时从上午06:30到上午08:00返回默认段 shift

我希望这是有道理的。

1 个答案:

答案 0 :(得分:1)

是的,一个SQL拼图,我无法抗拒! :d

这是一种可能的解决方案。我手头没有SQLServer(使用my favorite database :)),但SQL应该主要是可移植的:

create or replace table ranges(
        code varchar,
        beg timestamp_ntz,
        end timestamp_ntz,
        rank integer);
insert into ranges values
        ('shift', '2016-07-20 05:00:00', '2016-07-20 08:00:00', 5),
        ('late',  '2016-07-20 05:00:00', '2016-07-20 05:00:08', 1),
        ('break', '2016-07-20 06:00:00', '2016-07-20 06:30:00', 2);

WITH PERIODS AS (
  select beg, lead(beg, 1) over (order by beg) AS end
  from (select beg from ranges union select end from ranges)
),
MATCHING_RANGES AS (
  select periods.beg, periods.end, ranges.code, ranges.rank
  from periods
  join ranges on (periods.beg >= ranges.beg and periods.end <= ranges.end)
  where periods.end is not null 
),
RANKED_RANGES AS ( 
  select beg, end, code, row_number() over (partition by beg order by rank) in_period_rank 
  from matching_ranges 
)
select code, beg, end from ranked_ranges
where in_period_rank = 1
order by beg;

-------+---------------------+---------------------+
 CODE  |         BEG         |         END         |
-------+---------------------+---------------------+
 late  | 2016-07-20 05:00:00 | 2016-07-20 05:00:08 |
 shift | 2016-07-20 05:00:08 | 2016-07-20 06:00:00 |
 break | 2016-07-20 06:00:00 | 2016-07-20 06:30:00 |
 shift | 2016-07-20 06:30:00 | 2016-07-20 08:00:00 |
-------+---------------------+---------------------+

说明(我对原始表使用“范围”,对于这些表的切片使用“句点”,如输出中所需的那样):

  • 在PERIODS中,我们会及时创建所有不同的时刻,并使用LAG查找下一个。输出是:

    ---------------------+---------------------+
             BEG         |         END         |
    ---------------------+---------------------+
     2016-07-20 05:00:00 | 2016-07-20 05:00:08 |
     2016-07-20 05:00:08 | 2016-07-20 06:00:00 |
     2016-07-20 06:00:00 | 2016-07-20 06:30:00 |
     2016-07-20 06:30:00 | 2016-07-20 08:00:00 |
     2016-07-20 08:00:00 | [NULL]              |
    ---------------------+---------------------+
    
  • 然后在MATCHING_RANGES中,对于每个“句点”,我们从定义的表中找到所有可能的范围(也删除最后一行,NULL),输出:

    ---------------------+---------------------+-------+------+
             BEG         |         END         | CODE  | RANK |
    ---------------------+---------------------+-------+------+
     2016-07-20 05:00:00 | 2016-07-20 05:00:08 | shift | 5    |
     2016-07-20 05:00:00 | 2016-07-20 05:00:08 | late  | 1    |
     2016-07-20 05:00:08 | 2016-07-20 06:00:00 | shift | 5    |
     2016-07-20 06:00:00 | 2016-07-20 06:30:00 | shift | 5    |
     2016-07-20 06:00:00 | 2016-07-20 06:30:00 | break | 2    |
     2016-07-20 06:30:00 | 2016-07-20 08:00:00 | shift | 5    |
    ---------------------+---------------------+-------+------+
    

    请注意,这会创建与

  • 匹配的范围和句点的所有组合
  • 然后在每一行的RANKED_RANGES中,我们计算其期间的优先级:

    ---------------------+---------------------+-------+----------------+
             BEG         |         END         | CODE  | IN_PERIOD_RANK |
    ---------------------+---------------------+-------+----------------+
     2016-07-20 05:00:00 | 2016-07-20 05:00:08 | late  | 1              |
     2016-07-20 05:00:00 | 2016-07-20 05:00:08 | shift | 2              |
     2016-07-20 05:00:08 | 2016-07-20 06:00:00 | shift | 1              |
     2016-07-20 06:00:00 | 2016-07-20 06:30:00 | break | 1              |
     2016-07-20 06:00:00 | 2016-07-20 06:30:00 | shift | 2              |
     2016-07-20 06:30:00 | 2016-07-20 08:00:00 | shift | 1              |
    ---------------------+---------------------+-------+----------------+
    
  • 然后我们只选择排名为1的行:)