2012-05-04 9 views
3

"서비스 시간"의 시작 날짜와 종료 날짜가 나열된 테이블이 있습니다. 모든 행을 검색하고 날짜의 간격을 기준으로 서비스 중단을 식별 할 수있는 쿼리를 원합니다.복잡한 SQL 쿼리 - 날짜 범위 축소

Data: 
Start   End 
1/1/2000 2/1/2001 
2/2/2001 4/1/2001 
4/1/2004 6/2/2006 
6/3/2006 9/1/2010 
8/1/2011 9/1/2012 

Desired result: 
1/1/2001 - 4/1/2001  //The first two ranges collapsed because no break in service 
4/1/2004 - 9/1/2010  // The 3rd and 4th rows collapsed because no real break in service 
8/1/2011 - 9/1/2012 

이 아마 더 쉽게 나에게 가까이 갈 수있는 SQL 부두가있는 경우 그냥 궁금, 응용 프로그램 로직 또는 저장 프로 시저에서 수행.

Table definition: 
CREATE TABLE CONG_MEMBER_TERM 
(
    CONG_MEMBER_TERM_ID NUMBER(10, 0) NOT NULL 
    , CONGRESS_ID NUMBER(10, 0) NOT NULL 
    , CHAMBER_CD VARCHAR2(30 BYTE) NOT NULL 
    , CONG_MEMBER_ID NUMBER(10, 0) NOT NULL 
    , STATE_CD CHAR(2 BYTE) NOT NULL 
    , CONG_MEMBER_TYPE_CD VARCHAR2(30 BYTE) NOT NULL 
    , DISTRICT NUMBER(10, 0) 
    , START_DT TIMESTAMP(6) WITH TIME ZONE 
    , END_DT TIMESTAMP(6) WITH TIME ZONE 
    , CREATE_DT TIMESTAMP(6) WITH TIME ZONE NOT NULL 
    , UPDATE_DT TIMESTAMP(6) WITH TIME ZONE NOT NULL 
) 

Insert into CONG_MEMBER_TERM (CONG_MEMBER_TERM_ID,CONGRESS_ID,CHAMBER_CD,CONG_MEMBER_ID,STATE_CD,CONG_MEMBER_TYPE_CD,DISTRICT,START_DT,END_DT,CREATE_DT,UPDATE_DT) values (2945,104,'H',494,'OK','REP',2,to_timestamp_tz('04-JAN-95 01.00.00.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('04-OCT-96 01.00.00.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('02-MAY-12 09.45.47.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('02-MAY-12 09.45.48.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR')); 
Insert into CONG_MEMBER_TERM (CONG_MEMBER_TERM_ID,CONGRESS_ID,CHAMBER_CD,CONG_MEMBER_ID,STATE_CD,CONG_MEMBER_TYPE_CD,DISTRICT,START_DT,END_DT,CREATE_DT,UPDATE_DT) values (2946,105,'H',494,'OK','REP',2,to_timestamp_tz('07-JAN-97 01.00.00.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('19-DEC-98 01.00.00.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('02-MAY-12 09.45.47.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('02-MAY-12 09.45.49.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR')); 
Insert into CONG_MEMBER_TERM (CONG_MEMBER_TERM_ID,CONGRESS_ID,CHAMBER_CD,CONG_MEMBER_ID,STATE_CD,CONG_MEMBER_TYPE_CD,DISTRICT,START_DT,END_DT,CREATE_DT,UPDATE_DT) values (2947,106,'H',494,'OK','REP',2,to_timestamp_tz('06-JAN-99 01.00.00.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('15-DEC-00 01.00.00.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('02-MAY-12 09.45.47.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('02-MAY-12 09.45.49.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR')); 
Insert into CONG_MEMBER_TERM (CONG_MEMBER_TERM_ID,CONGRESS_ID,CHAMBER_CD,CONG_MEMBER_ID,STATE_CD,CONG_MEMBER_TYPE_CD,DISTRICT,START_DT,END_DT,CREATE_DT,UPDATE_DT) values (2948,109,'S',494,'OK','SEN',null,to_timestamp_tz('04-JAN-05 01.00.00.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('09-DEC-06 01.00.00.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('02-MAY-12 09.45.48.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('02-MAY-12 09.45.49.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR')); 
Insert into CONG_MEMBER_TERM (CONG_MEMBER_TERM_ID,CONGRESS_ID,CHAMBER_CD,CONG_MEMBER_ID,STATE_CD,CONG_MEMBER_TYPE_CD,DISTRICT,START_DT,END_DT,CREATE_DT,UPDATE_DT) values (2949,110,'S',494,'OK','SEN',null,to_timestamp_tz('04-JAN-07 01.00.00.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('02-JAN-09 01.00.00.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('02-MAY-12 09.45.48.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('02-MAY-12 09.45.49.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR')); 
Insert into CONG_MEMBER_TERM (CONG_MEMBER_TERM_ID,CONGRESS_ID,CHAMBER_CD,CONG_MEMBER_ID,STATE_CD,CONG_MEMBER_TYPE_CD,DISTRICT,START_DT,END_DT,CREATE_DT,UPDATE_DT) values (2951,111,'S',494,'OK','SEN',null,to_timestamp_tz('06-JAN-09 01.00.00.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('22-DEC-10 01.00.00.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('02-MAY-12 09.45.48.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('02-MAY-12 09.45.49.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR')); 
Insert into CONG_MEMBER_TERM (CONG_MEMBER_TERM_ID,CONGRESS_ID,CHAMBER_CD,CONG_MEMBER_ID,STATE_CD,CONG_MEMBER_TYPE_CD,DISTRICT,START_DT,END_DT,CREATE_DT,UPDATE_DT) values (2950,112,'S',494,'OK','SEN',null,to_timestamp_tz('05-JAN-11 01.00.00.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),null,to_timestamp_tz('02-MAY-12 09.45.48.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR'),to_timestamp_tz('02-MAY-12 09.45.49.000000000 AM -05:00','DD-MON-RR HH.MI.SS.FF AM TZR')); 

이전 서비스와 다음 서비스 사이의 간격이 24개월보다 큰 경우,

, 그것은 서비스의 "차이"로 간주됩니다.

+0

"서비스 중단"은 무엇을 의미합니까? 한 달 이상 간격이 있습니까? –

+0

[유사한 질문] (SQL 서버에 대한 http://stackoverflow.com/questions/6068619/merging-date-intervals-in-sql-server) - 올바른 방향으로 당신을 가리킬 수도 있지만 모든 대답은 CTE가 필요합니다 - SQL 서버 기능. – SWeko

+0

PostgreSQL, SQLite, DB2, Oracle 및 SQL Server는 CTE (WITH 절)를 지원하지 않습니다. 공정하게 SqlLite가 2013에 추가했습니다. –

답변

3

다음은 분석 함수를 사용하여 SQL에서 시간 범위를 축소하는 표준 방법입니다.

테이블 :

SQL> create table mytable (startdate,enddate) 
    2 as 
    3 select date '2000-01-01', date '2001-02-01' from dual union all 
    4 select date '2001-02-02', date '2001-04-01' from dual union all 
    5 select date '2004-04-01', date '2006-06-02' from dual union all 
    6 select date '2006-06-03', date '2010-09-01' from dual union all 
    7 select date '2011-08-01', date '2012-09-01' from dual 
    8/

Table created. 

쿼리 :

그것은 세 가지 단계로 작동
SQL> select min(startdate) startdate 
    2  , max(enddate) enddate 
    3 from (select startdate 
    4    , enddate 
    5    , max(rn) over (order by startdate) maxrn 
    6    from (select startdate 
    7       , enddate 
    8       , case lag(enddate) over (order by startdate) 
    9       when startdate-1 then 
10        null 
11       else 
12        rownum 
13       end rn 
14      from mytable 
15     ) 
16  ) 
17 group by maxrn 
18 order by startdate 
19/

STARTDATE   ENDDATE 
------------------- ------------------- 
01-01-2000 00:00:00 01-04-2001 00:00:00 
01-04-2004 00:00:00 01-09-2010 00:00:00 
01-08-2011 00:00:00 01-09-2012 00:00:00 

3 rows selected. 

: a의 시작 만 레코드에 고유 한 ROWNUM을 할당

  1. 을 그룹
  2. 그룹의 시작이 아닌 레코드를 제공 sa 그룹의 시작으로 나를 수
  3. 그룹 번호

으로 집계 그리고이 쿼리의 진정한 아름다움 (슬라이딩 윈도우와 분석 기능 MAX를 사용하여) 하나의 TABLE ACCESS FULL가 필요하다는 것입니다 :

SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last')) 
    2/

PLAN_TABLE_OUTPUT 
------------------------------------------------------------------------------------------------------------------------- 
SQL_ID 8v1suw8j53tqz, child number 0 
------------------------------------- 
select min(startdate) startdate  , max(enddate) enddate from (select startdate    , enddate 
      , max(rn) over (order by startdate) maxrn   from (select startdate 
, enddate      , case lag(enddate) over (order by startdate)       when 
startdate-1 then       null       else       rownum 
         end rn      from mytable    )  ) group by maxrn 
order by startdate 

Plan hash value: 2933657513 

------------------------------------------------------------------------------------------------------------------------- 
| Id | Operation    | Name | Starts | E-Rows | A-Rows | A-Time | Buffers | OMem | 1Mem | Used-Mem | 
------------------------------------------------------------------------------------------------------------------------- 
| 1 | SORT ORDER BY   |   |  1 |  5 |  3 |00:00:00.01 |  3 | 2048 | 2048 | 2048 (0)| 
| 2 | HASH GROUP BY   |   |  1 |  5 |  3 |00:00:00.01 |  3 |  |  |   | 
| 3 | VIEW     |   |  1 |  5 |  5 |00:00:00.01 |  3 |  |  |   | 
| 4 |  WINDOW BUFFER  |   |  1 |  5 |  5 |00:00:00.01 |  3 | 2048 | 2048 | 2048 (0)| 
| 5 |  VIEW    |   |  1 |  5 |  5 |00:00:00.01 |  3 |  |  |   | 
| 6 |  WINDOW SORT  |   |  1 |  5 |  5 |00:00:00.01 |  3 | 2048 | 2048 | 2048 (0)| 
| 7 |  COUNT    |   |  1 |  |  5 |00:00:00.01 |  3 |  |  |   | 
| 8 |   TABLE ACCESS FULL| MYTABLE |  1 |  5 |  5 |00:00:00.01 |  3 |  |  |   | 
------------------------------------------------------------------------------------------------------------------------- 


24 rows selected. 

감사합니다.
Rob.

+0

사례를 사용하여 추가 인라인 뷰를 제거하는 것이 좋습니다. 나는 다음 시간 동안 그 것을 훔쳐 야 할 것이다 ;-) –

1

이이 분석을 사용하여 수행,하지만 난 쿼리 나올 원하는 것을 확실하지 않다 할 수 _mike

. 예를 들어

:

drop table mydates; 

create table mydates (sdate date, edate date); 

insert into mydates values (to_date('2000-01-01' ,'YYYY-MM-DD'), to_date('2000-01-02' ,'YYYY-MM-DD')); 

insert into mydates values (to_date('2000-01-02' ,'YYYY-MM-DD'), to_date('2000-02-01' ,'YYYY-MM-DD')); 
-- insert a gap 
insert into mydates values (to_date('2001-01-01' ,'YYYY-MM-DD'), to_date('2001-01-02' ,'YYYY-MM-DD')); 

insert into mydates values (to_date('2001-01-02' ,'YYYY-MM-DD'), to_date('2001-02-01' ,'YYYY-MM-DD')); 

는 여기에서 우리는 그들 사이의 서비스 중단으로 두 행의 두 그룹이있다. 지연 기능을 사용하여 start_date가 이전 행 end_date와 다른 모든 행을 쉽게 찾을 수 있습니다. 이 SQL은 각 그룹의 시작 행을 줄 것입니다 :

select * 
    from 
    (
    select lag(edate, 1, null) over (order by sdate asc) as previous_end, 
    sdate sd, 
    edate ed 
    from mydates 
) 
where previous_end != sd or previous_end is null; 

그러나 나는 당신이 원하는 것을 확신하지 못합니다. 모든 행을 멋지게 처리하고 축소하여 연속 그룹당 하나의 행을 얻을 수 있습니다.

더 완벽한 예제 (오브젝트 및 데이터를 작성하는 스크립트 포함)를 게시하는 경우 유용 할 수 있습니다.

select min(sd) sd, max(ed) ed 
from 
(
    select max(grp) over (order by sd) grp, 
     sd, ed 
    from 
    (
    select 
     case 
     when previous_end != sd or previous_end is null then 
      rn 
     else 
      null 
     end grp, 
     sd, 
     ed 
    from 
    (
     select lag(edate, 1, null) over (order by sdate asc) as previous_end, 
     row_number() over (order by sdate asc) as rn, 
     sdate sd, 
     edate ed 
     from mydates 
     order by sdate asc 
    ) 
) 
) group by grp 
order by sd asc;