2016-08-05 10 views
7

mam dane w moim stole jak tennormalizacji danych z nieznormalizowaną tabeli

RepID|Role|Status|StartDate |EndDate | 
-----|----|------|----------|----------| 
10001|R1 |Active|01/01/2015|01/31/2015| 
-----|----|------|----------|----------| 
10001|R1 |Leavee|02/01/2015|02/12/2015| 
-----|----|------|----------|----------| 
10001|R1 |Active|02/13/2015|02/28/2015| 
-----|----|------|----------|----------| 
10001|R2 |Active|03/01/2015|03/18/2015| 
-----|----|------|----------|----------| 
10001|R2 |Leave |03/19/2015|04/10/2015| 
-----|----|------|----------|----------| 
10001|R2 |Active|04/11/2015|05/10/2015| 
-----|----|------|----------|----------| 
10001|R1 |Active|05/11/2015|06/13/2015| 
-----|----|------|----------|----------| 
10001|R1 |Leave |06/14/2015|12/31/9998| 
-----|----|------|----------|----------| 

szukam wyjścia jak ta,

RepID|Role|StartDate |EndDate | 
-----|----|----------|----------| 
10001|R1 |01/01/2015|02/28/2015| 
-----|----|----------|----------| 
10001|R2 |03/01/2015|05/10/2015| 
-----|----|----------|----------| 
10001|R1 |05/11/2015|12/31/9998| 
-----|----|----------|----------| 

Gdy tylko zamiana ról dzieje, muszę przechwytuj początek i datę zakończenia. Próbowałem na różne sposoby, ale nie mogłem uzyskać danych wyjściowych.

Każda pomoc jest doceniana.

Poniżej SQL próbowałem, ale to nie robi z pomocy,

SELECT T1.RepID, T1.Role, Min(T1.StartDate)  AS StartDate,  Max(T1.EndDate) AS EndDate 
FROM 
(SELECT rD1.RepID, rD1.Role, rD1.StartDate, rD1.EndDate 
FROM repDetails rD1 
INNER JOIN repDetails rD2 
    ON rD2.RepID = rD1.RepID AND rD2.StartDate = DateAdd (Day, 1, rD1.EndDate)  AND (rD2.Role = rD1.Role OR (rD2.Role IS NULL AND rD1.Role IS NULL)   OR (rD2.Role = '' AND rD1.Role = '')) 

UNION 

SELECT rD2.RepID, rD2.Role, rD2.StartDate, rD2.EndDate 
FROM repDetails rD1 
INNER JOIN repDetails rD2 
    ON rD2.RepID = rD1.RepID AND rD2.StartDate = DateAdd (Day, 1, rD1.EndDate)  AND (rD2.Role = rD1.Role OR (rD2.Role IS NULL AND rD1.Role IS NULL)   OR (rD2.Role = '' AND rD1.Role = '')) 
    ) T1 
GROUP BY T1.RepID, T1.Role 

UNION 

SELECT EP.RepID, EP.Role AS DataValue, EP.StartDate, EP.EndDate 
FROM repDetails EP 
LEFT OUTER JOIN 
(SELECT rD1.RepID, rD1.Role, rD1.StartDate, rD1.EndDate 
FROM repDetails rD1 
INNER JOIN repDetails rD2 
    ON rD2.RepID = rD1.RepID AND rD2.StartDate = DateAdd (Day, 1, rD1.EndDate)  AND (rD2.Role = rD1.Role OR (rD2.Role IS NULL AND rD1.Role IS NULL)   OR (rD2.Role = '' AND rD1.Role = '')) 

UNION 

SELECT rD2.RepID, rD2.Role , rD2.StartDate, rD2.EndDate 
FROM repDetails rD1 
INNER JOIN repDetails rD2 
    ON rD2.RepID = rD1.RepID AND rD2.StartDate = DateAdd (Day, 1, rD1.EndDate)  AND (rD2.Role = rD1.Role OR (rD2.Role IS NULL AND rD1.Role IS NULL)   OR (rD2.Role = '' AND rD1.Role = '')) 
    ) T1 
ON EP.RepID = T1.RepID AND EP.StartDate = T1.StartDate 
WHERE T1.RepID IS NULL 
+2

Jakie są sposoby próbowałeś? Jaki był wynik? – dbmitch

+0

Byłoby to trudne z podstawową kwerendą - może guru SQL mógłby to zrobić, ale byłoby to bardzo proste przy użyciu procedury przechowywanej. – dbmitch

+0

Nie mogę użyć żadnej procedury przechowywanej w aplikacji. Próbowałem z funkcjami MAX i MIN jak poniżej SQL. – Naveen

Odpowiedz

2

Kluczem tutaj jest zidentyfikowanie ciągłych wiersze aż do zmiany roli. Można tego dokonać, porównując rolę następnego rzędu z funkcją lead i dodatkową logiką, aby sklasyfikować wszystkie poprzednie wiersze w tej samej grupie.

Po zaklasyfikowaniu ich do grup wystarczy użyć min i max, aby uzyskać daty rozpoczęcia i zakończenia.

with groups as (
select x.* 
,case when grp = 1 then 0 else 1 end + sum(grp) over(partition by repid order by startdate) grps 
from (select t.* 
     ,case when lead(role) over(partition by repid order by startdate) = role then 0 else 1 end grp 
     from t) x 
) 
select distinct repid,role 
,min(startdate) over(partition by repid,grps) startdt 
,max(enddate) over(partition by repid,grps) enddt 
from groups 
order by 1,3 

Sample demo

+0

Dziękuję VKP, to działało świetnie !! Nie wiedziałem o funkcji nadmiernej !! – Naveen

+0

@VKP, dlaczego użyłeś opcji select distinct/over w drugiej instrukcji zamiast grupowania według wartości grps? – Beth

+0

@ Beth .. ponieważ każda kombinacja id i role może mieć różne daty rozpoczęcia i zakończenia .. 'min() over()' lub 'max() over()' bez 'group by' zwraca wszystkie wiersze w danych. Aby tego uniknąć użyłem słowa 'odrębny'. –

0

po prostu chcesz min (start)/MAX (Koniec) termin dla każdego repID i roli? Jeśli tak, spróbuj:

Select 
    repID, role, 
    min(starDate), 
    max(endDate) 
from 
    tbl 
group by 
    repID, role 

- Bardziej szczegółowa roztworu, co odpowiada VKP to:

SELECT 
    repid, ROLE, grpID, 
    MIN(startdate) AS min_startDateOverRole, 
    MAX(endDate) AS max_endDateOverRole 
FROM 
    (SELECT 
     *, CASE WHEN isGrpEnd = 1 THEN 0 ELSE 1 end + 
     -- when on group end row, don't increment grpID. 
     -- Wait until start of next group 
     SUM(isGrpEnd) OVER(ORDER BY startdate) grpID 
     -- sum(all group end rows up to this one) 
    FROM 
      (SELECT 
       *, 
       CASE WHEN lead(ROLE) OVER(ORDER BY startdate) = ROLE 
         THEN 0 ELSE 1 end isGrpEnd 
      FROM t) x ) 
GROUP BY 
    repid, ROLE, grpid 
ORDER BY 
    1,3 
+0

Dzięki za odpowiedź Beth !! Po prostu nie chcę, aby min i maksimum opierały się na robieniu i roli. Kiedy zmienia się rola Repa, potrzebuję daty początkowej i końcowej w jednym rekordzie, aby zobaczyć, jak długo są w tej roli. – Naveen

Powiązane problemy