2014-07-10 1 views
0

동일한 데이터를 저장하는 여러 레이어가있는 데이터웨어 하우스를 구축하고 있습니다. 중간 계층 중 하나의 모든 데이터는 시작 및 종료 날짜가 버전 2로 느리게 변경되는 것처럼 버전이 지정됩니다. 이 테이블을 쿼리 할 때 문제가 발생합니다. 일반적으로 쿼리의 테이블보다 더 많은 열이 있으므로 쿼리의 인접한 버전은 시작일과 종료일이 다르지만 동일하지 않습니다. 이 버전을 결합하여 테이블의 행이 변경되지 않을 때 쿼리의 열이 변경된 날짜를 표시하려고합니다.SQL을 사용하여 천천히 변하는 유형 2에서 중복을 제거합니다.

create table versions 
(id int 
, name varchar(100) Not null 
, RowStartDate datetime Not null 
, RowEndDate datetime Not null 
, primary key (id,RowStartDate) 
, check (RowStartDate < RowEndDate)); 

insert into versions values 
(1,'A','2014-01-01','9999-12-31') 
,(2,'B','2014-01-01','2014-12-31') 
,(2,'B','2014-12-31','9999-12-31') 
,(3,'C','2014-01-01','2014-12-31') 
,(3,'CC','2014-12-31','2015-12-31') 
,(3,'CC','2015-12-31','9999-12-31') 
,(4,'D','2014-01-01','2014-12-31') 
,(4,'DD','2014-12-31','2015-12-31') 
,(4,'DD','2015-12-31','2016-12-31') 
,(4,'D','2016-12-31','9999-12-31') 
,(5,'E','2014-01-01','2014-12-31') 
,(5,'E','2014-12-31','2015-12-31') 
,(5,'E','2015-12-31','2016-12-31') 
,(5,'E','2016-12-31','2017-12-31') 
,(5,'E','2017-12-31','9999-12-31') 
; 

WITH CTE_detect_duplicates AS (SELECT [id] 
     ,[name] 
     ,[RowStartDate] 
     ,[RowEndDate] 
     ,LAST_VALUE(RowEndDate) OVER (PARTITION BY id, name ORDER BY RowStartDate, RowEndDate ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) as LastEndDate 
     ,rank() OVER (PARTITION BY id, name ORDER BY RowStartDate, RowEndDate) as duplicateNumber 
    FROM versions 
) 
SELECT [id] 
     ,[name] 
     ,[RowStartDate] 
     ,LastEndDate as RowEndDate 
FROM CTE_detect_duplicates 
WHERE duplicateNumber = 1 

여기서 문제는이 ID가 "4"에 대한 두 개의 행을 반환하고, 세 원할 때

은 거의 작동 일부 SQL 있습니다. 실제 :

 
id name RowStartDate RowEndDate 
4 D 2014-01-01 00:00:00.000 9999-12-31 00:00:00.000 
4 DD 2014-12-31 00:00:00.000 2016-12-31 00:00:00.000 
희망 :
 
id name RowStartDate RowEndDate 
4 D 2014-01-01 00:00:00.000 2014-12-31 00:00:00.000 
4 DD 2014-12-31 00:00:00.000 2016-12-31 00:00:00.000 
4 D 2016-12-31 00:00:00.000 9999-12-31 00:00:00.000 
DD 값이 정확한 기간에 대해 값 D가 올바르지 않으므로 버전 쿼리 날짜가 내 쿼리의 첫 번째 행 (4, 'D')에서 올바르지 않습니다.

순수 SQL 또는 인라인 테이블 값 함수에서 이러한 중복을 제거 할 수 있기를 원합니다.이 작업은 다중 명령문 테이블 값 함수를 생성하지만 결과 함수는 제대로 수행되지 않습니다. 누구든지 아이디어가 있습니까?

답변

1

여러 CTE가 포함 된 다음 쿼리는 업데이트 날짜 범위를 압축하고 중복 값을 제거합니다.

1 첫 번째 순위는 RowStartDate를 기반으로 각 id 그룹 내에서 지정됩니다.

2 다음으로, NAME에 대해 동일한 값을 갖는 순위 범위의 최대 순위 (next_rank_no)가 결정됩니다. 따라서 예제 데이터의 경우 id = 5의 행 1에는 next_rank_no = 5가 있고 id = 4의 행 2에는 next_rank_no = 3이 있습니다. 이 버전은 NAME 열만 처리합니다. 추가 열을 처리하려는 경우 해당 열을 조건에 포함해야합니다. 당신이 LOCATION 열을 포함 할 경우 예를 들어, 다음은 조건으로 읽을 것이다 가입 :

left join sorted_versions sv2 on sv2.id = sv1.id and sv2.rank_no > sv1.rank_no and sv2.name = sv1.name and sv2.location = sv1.location 
    left join sorted_versions sv3 on sv3.id = sv1.id and sv3.rank_no > sv1.rank_no and (sv3.name <> sv1.name or sv3.location <> sv1.location) 

3 마지막으로 각 ID의 첫 번째 행이 선택됩니다. 그런 다음 next_rank_no에 해당하는 행이 순환 방식으로 선택됩니다.

with sorted_versions as --ranks are assigned within each id group 
(
    select 
    v1.id, 
    v1.name, 
    v1.RowStartDate, 
    v1.RowEndDate, 
    rank() over (partition by v1.id order by v1.RowStartDate) rank_no 
    from versions v1 
    left join versions v2 on (v1.id = v2.id and v2.RowStartDate = v1.RowEndDate) 
), 
next_rank as --the maximum rank of the range of ranks which has the same value for NAME 
(
    select 
    sv1.id id, sv1.rank_no rank_no, COALESCE(min(sv3.rank_no)-1 , COALESCE(max(sv2.rank_no), sv1.rank_no)) next_rank_no 
    from sorted_versions sv1 
    left join sorted_versions sv2 on sv2.id = sv1.id and sv2.rank_no > sv1.rank_no and sv2.name = sv1.name 
    left join sorted_versions sv3 on sv3.id = sv1.id and sv3.rank_no > sv1.rank_no and sv3.name <> sv1.name 
    group by sv1.id, sv1.rank_no 
), 
versions_cte as --the rowenddate of the "maximum rank" is selected 
(
    select sv.id, sv.name, sv.rowstartdate, sv3.rowenddate, nr.next_rank_no rank_no 
    from sorted_versions sv 
    inner join next_rank nr on sv.id = nr.id and sv.rank_no = nr.rank_no and sv.rank_no = 1 
    inner join sorted_versions sv3 on nr.id = sv3.id and nr.next_rank_no = sv3.rank_no 
    union all 
    select 
    sv2.id, 
    sv2.name, 
    sv2.rowstartdate, 
    sv3.rowenddate, 
    nr.next_rank_no 
    from versions_cte vc 
    inner join sorted_versions sv2 on sv2.id = vc.id and sv2.rank_no = vc.rank_no + 1 
    inner join next_rank nr on sv2.id = nr.id and sv2.rank_no = nr.rank_no 
    inner join sorted_versions sv3 on nr.id = sv3.id and nr.next_rank_no = sv3.rank_no 
) 
select id, name, rowstartdate, rowenddate 
from versions_cte 
order by id, rowstartdate; 

SQL Fiddle demo

관련 문제