查询表

时间:2016-02-04 07:21:50

标签: sql oracle gaps-and-islands

我有一张表,其中有效的开始日期和结束日期是员工的历史数据。该表应包含该员工的所有详细信息。

我需要通过查询找出日期之间有间隔的地方。例如:

Table ABC :

EMP_NO  EFF_START_DATE   EFF_END_DATE          ORG             NAME          DOB          STATUS  
1       01-JAN-2010       28-MAR-2010         XYZ             SMITH         10-JAN-1990   SINGLE   
1       29-MAR-2010       29-AUG-2010         XYZ             SMITH         10-JAN-1990   MARRIED
1       20-OCT-2010       31-DEC-4712         XYZ             SMITH         10-JAN-1990   DIVORCEE 

2       04-FEB-2010       28-MAR-2010         XYZ             JOHN        10-JAN-1991   SINGLE   
2       29-MAR-2010       31-DEC-4712         XYZ             JOHN        10-JAN-1991   MARRIED 

3       02-FEB-2010       21-MAR-2010         XYZ             GEETA        10-JAN-1991   SINGLE   
3       29-MAR-2010       31-DEC-4712         XYZ             GEETA        10-JAN-1991   MARRIED 

现在,对于EMP NO 1和3,存在差距。例如,对于2010年8月29日之后和2010年20月20日之前的第1号,应该有一个记录。 同样在emp no。 3应该在2010年3月21日至2010年3月29日之间有记录。

我可以为此编写什么查询

3 个答案:

答案 0 :(得分:2)

这是一个典型的 Gaps and Islands 问题。您需要找出日期序列中的缺失值

例如,

SQL> WITH sample_data(dates) AS(
  2  SELECT  DATE '2015-01-01' FROM dual UNION
  3  SELECT  DATE '2015-01-02' FROM dual UNION
  4  SELECT  DATE '2015-01-03' FROM dual UNION
  5  SELECT  DATE '2015-01-05' FROM dual UNION
  6  SELECT  DATE '2015-01-06' FROM dual UNION
  7  SELECT  DATE '2015-01-07' FROM dual UNION
  8  SELECT  DATE '2015-01-10' FROM dual UNION
  9  SELECT  DATE '2015-01-11' FROM dual UNION
 10  SELECT  DATE '2015-01-12' FROM dual UNION
 11  SELECT  DATE '2015-01-13' FROM dual UNION
 12  SELECT  DATE '2015-01-20' FROM dual
 13  )
 14 -- end of sample_data mimicking real table
 15  SELECT MIN(missing_dates),
 16         MAX(missing_dates)
 17  FROM
 18    (SELECT missing_dates,
 19   missing_dates - row_number() OVER(ORDER BY missing_dates) rn
 20    FROM
 21   (SELECT min_date - 1 + LEVEL missing_dates
 22   FROM
 23  ( SELECT MIN(dates) min_date , MAX(dates) max_date FROM sample_data
 24  )
 25  CONNECT BY level <= max_date - min_date + 1
 26   MINUS
 27   SELECT dates FROM sample_data
 28   ) )
 29  GROUP BY rn ORDER BY rn;

MIN(MISSING_DATES) MAX(MISSING_DATES)
------------------ ------------------
2015-01-04         2015-01-04
2015-01-08         2015-01-09
2015-01-14         2015-01-19

注意

WITH 子句仅用于构建示例的示例数据,因为您未提供create和insert语句。实际上,您需要使用自己的 table_name 而不是 sample_data

答案 1 :(得分:2)

您还可以通过比较每一行的start_date与同一员工的前一个end_date来找到解决方案:

select 
    src.*, 
    src.start_date - src.prev_end_date gap_days 
  from (
    select 
        out_tab.emp_no, 
        (select max(in_tab.end_date) from ABC in_tab where in_tab.emp_no = out_tab.emp_no and in_tab.end_date < out_tab.start_date) prev_end_date, 
        out_tab.start_date 
      from 
        ABC out_tab 
  ) src
  where 
    start_date - prev_end_date > 1;


    EMP_NO PREV_END_DATE START_DATE   GAP_DAYS
---------- ------------- ---------- ----------
         1 29-AUG-10     20-OCT-10          52 
         3 21-MAR-10     29-MAR-10           8 

答案 2 :(得分:1)

最简单的方法是将行的结束日期与下一行的开始日期进行比较。您可以使用LEAD分析功能轻松完成此操作,如下所示:

with abc as (select 1 emp_no, to_date('01/01/2010', 'dd/mm/yyyy') eff_start_date, to_date('28/03/2010', 'dd/mm/yyyy') eff_end_date, 'XYZ' org, 'SMITH' name, to_date('10/01/1990', 'dd/mm/yyyy') dob, 'SINGLE' status from dual union all
             select 1 emp_no, to_date('29/03/2010', 'dd/mm/yyyy') eff_start_date, to_date('29/08/2010', 'dd/mm/yyyy') eff_end_date, 'XYZ' org, 'SMITH' name, to_date('10/01/1990', 'dd/mm/yyyy') dob, 'MARRIED' status from dual union all
             select 1 emp_no, to_date('20/10/2010', 'dd/mm/yyyy') eff_start_date, to_date('31/12/4712', 'dd/mm/yyyy') eff_end_date, 'XYZ' org, 'SMITH' name, to_date('10/01/1990', 'dd/mm/yyyy') dob, 'DIVORCEE' status from dual union all
             select 2 emp_no, to_date('04/02/2010', 'dd/mm/yyyy') eff_start_date, to_date('28/03/2010', 'dd/mm/yyyy') eff_end_date, 'XYZ' org, 'JOHN' name, to_date('10/01/1990', 'dd/mm/yyyy') dob, 'SINGLE' status from dual union all
             select 2 emp_no, to_date('29/03/2010', 'dd/mm/yyyy') eff_start_date, to_date('31/12/4712', 'dd/mm/yyyy') eff_end_date, 'XYZ' org, 'JOHN' name, to_date('10/01/1990', 'dd/mm/yyyy') dob, 'MARRIED' status from dual union all
             select 3 emp_no, to_date('02/02/2010', 'dd/mm/yyyy') eff_start_date, to_date('21/03/2010', 'dd/mm/yyyy') eff_end_date, 'XYZ' org, 'GEETA' name, to_date('10/01/1990', 'dd/mm/yyyy') dob, 'SINGLE' status from dual union all
             select 3 emp_no, to_date('29/03/2010', 'dd/mm/yyyy') eff_start_date, to_date('31/12/4712', 'dd/mm/yyyy') eff_end_date, 'XYZ' org, 'GEETA' name, to_date('10/01/1990', 'dd/mm/yyyy') dob, 'MARRIED' status from dual)
-- end of mimicking your abc table; you won't need the above subquery, as you already have a table called abc.
select emp_no,
       eff_end_date + 1 gap_start_date,
       next_eff_start_date - 1 gap_end_date,
       org,
       name,
       'UKNOWN' status
from   (select emp_no,
               eff_start_date,
               eff_end_date,
               lead(eff_start_date) over (partition by emp_no order by eff_start_date) next_eff_start_date,
               org,
               name,
               dob,
               status
        from   abc)
where  next_eff_start_date - eff_end_date > 1;

    EMP_NO GAP_START_DATE GAP_END_DATE ORG NAME  STATUS
---------- -------------- ------------ --- ----- ------
         1 30-AUG-2010    19-OCT-2010  XYZ SMITH UKNOWN
         3 22-MAR-2010    28-MAR-2010  XYZ GEETA UKNOWN

N.B。你没有说出你期望看到的那种输出,所以我已经给你了差距的开始和结束日期。

此外,和Lalit一样,我在WITH子句中使用了子查询来生成示例数据。你不需要那个子查询,因为你已经有了一个&#34; abc&#34;表