2014-11-18 1 views
1

동일한 그룹의 데이터를 축소 할 필요가 있습니다.SQL 서버 데이터를 decollapse하는 방법

예를 들어 설명하겠습니다.

데모 데이터 :

+-------------+----+------------+-----------+ 
| Primarykey | ID | START_Point| STOP_Point| 
+-------------+----+------------+-----------+ 
|   1 | 1 |   1 |  10 | 
|   2 | 1 |   2 |   4 | 
|   3 | 1 |   5 |   5 | 
|   4 | 1 |   5 |   5 | 
|   5 | 1 |   5 |  15 | 
|   6 | 2 |   5 |   5 | 
|   7 | 2 |   2 |   2 | 
|   8 | 2 |   1 |  10 | 
|   9 | 2 |   1 |  20 | 
+-------------+----+------------+-----------+ 
데이터는 그룹으로 decollapse되어야

:

예상 출력 :

+------------+-----------+------------+-----------+ 
| primarykey | SubjectID | START_Point| STOP_Point| 
+------------+-----------+------------+-----------+ 
|   1 |   1 |   1 |   1 | 
|   1 |   1 |   2 |   4 | 
|   1 |   1 |   5 |   5 | 
|   1 |   1 |   6 |  10 | 
|   2 |   1 |   2 |   4 | 
|   3 |   1 |   5 |   5 | 
|   4 |   1 |   5 |   5 | 
|   5 |   1 |   5 |   5 | 
|   5 |   1 |   6 |  10 | 
|   5 |   1 |   11 |  15 | 
|   6 |   2 |   5 |   5 | 
|   7 |   2 |   2 |   2 | 
|   8 |   2 |   1 |   1 | 
|   8 |   2 |   2 |   2 | 
|   8 |   2 |   3 |   4 | 
|   8 |   2 |   5 |   5 | 
|   8 |   2 |   6 |  10 | 
|   9 |   2 |   1 |   1 | 
|   9 |   2 |   2 |   2 | 
|   9 |   2 |   3 |   4 | 
|   9 |   2 |   5 |   5 | 
|   9 |   2 |   6 |  10 | 
|   9 |   2 |   11 |  20 | 
+------------+-----------+------------+-----------+ 

로직 : 어떤 Start_Point가 없어야 - Stop_Point 범위를 오버랩 같은 그룹 내에서.

설명 : 우리는 = 1 start_point하고 Stop_point 지금 = 10

우리가 다른 행을 선택하면

"ID"기본 키 = 1의 경우

그룹으로, ID = 1 (그룹 별 ID이므로) 기본 키 2,3,4 및 5가 1 - 10 범위 내에서 중복됩니다.

그래서 w e는 decolapse 1-10에서 1-1,2-4,5-5,6-10으로 축소하려고합니다.

각 행에 대해 동일한 로직을 적용해야합니다.

대용량 데이터에 내 쿼리가 적용되고 처리 속도가 느려지므로 행 가져 오기 및 처리 논리별로 커서 나 행을 사용하고 싶지 않습니다.

아무 것도 설명하지 않거나 혼란스러운 것으로 보이는 경우 알려주십시오.

약간의 도전이지만 스택 오버플로에 대해 많은 전문가가 있다는 것을 알고 있습니다.

약간의 해결책을 찾고 있습니다.

미리 감사드립니다. - 원래 테이블에 포함 된 모든 ID의 각 시간 범위에 대한 엔드 포인트

SELECT ID, Idx, StartStop, 
     LEAD(Idx) OVER (PARTITION BY ID ORDER BY IdxRowNum) AS NextIdx, 
     LEAD(StartStop) OVER (PARTITION BY ID ORDER BY IdxRowNum) AS NextStartStop 
FROM (
    SELECT ID, Idx, StartStop, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Idx, StartStop) AS IdxRowNum       
    FROM 
     (SELECT ID, START_Point, STOP_Point 
     FROM @T) Src 
    UNPIVOT (
     Idx FOR StartStop IN (START_Point, STOP_Point) 
    ) AS Unpvt 
) t 

는 시작의 테이블을 만들기 : 당신이 다음 쿼리를 사용할 수있는 시작점으로

+0

그래서 우리는 decollapse 1-10을 1-1,2-4,5-5,6-10으로 축소하려고합니다. –

+1

'primarykey = 5'는'start_point'와'stop_point'를 5-15로 보여줍니다. '6-10'은 어떻게 될까요? 'id = 2'에서 어떻게 작동할까요? –

+2

우리는 당신이 해결책을 찾도록 도울 수 있습니다. 하지만 먼저 질문에 대한 세부 정보를 제공해야합니다. 데이터를 어떻게 든 확장하려고하지만 비즈니스 규칙이 무엇인지 이해하지 못합니다. 당신이 올린 글에서 그것은 나에게 의미가 없습니다. 그리고 "decollapse"는 단어가 아닙니다, 나는 당신이 확장을 의미한다고 생각합니다. –

답변

1

.

SELECT DISTINCT ID, StartIdx, StopIdx 
FROM 
(
    SELECT ID, 
      CASE 
      WHEN StartStop = 'START_Point' AND NextStartStop = 'START_Point' THEN 
       CASE WHEN NextIdx > Idx THEN Idx ELSE NULL END 
      WHEN StartStop = 'STOP_Point' AND NextStartStop = 'STOP_Point' THEN 
       CASE WHEN NextIdx > Idx THEN Idx + 1 ELSE NULL END 
      WHEN StartStop = 'STOP_Point' AND NextStartStop = 'START_Point' THEN 
       CASE WHEN NextIdx > Idx THEN Idx + 1 ELSE NULL END 
      WHEN StartStop = 'START_Point' AND NextStartStop = 'STOP_Point' THEN Idx   
      END AS StartIdx, 
      CASE 
      WHEN StartStop = 'START_Point' AND NextStartStop = 'START_Point' THEN 
       CASE WHEN NextIdx > Idx THEN NextIdx - 1 ELSE NULL END 
      WHEN StartStop = 'STOP_Point' AND NextStartStop = 'STOP_Point' THEN 
       CASE WHEN NextIdx > Idx THEN NextIdx ELSE NULL END 
      WHEN StartStop = 'START_Point' AND NextStartStop = 'STOP_Point' THEN NextIdx 
      WHEN StartStop = 'STOP_Point' AND NextStartStop = 'START_Point' THEN 
       CASE WHEN NextIdx = Idx + 1 THEN NextIdx WHEN NextIdx > Idx THEN NextIdx- 1 ELSE NULL END 
      ELSE Idx 
      END AS StopIdx 
    FROM 
    (
     SELECT ID, Idx, StartStop, 
       LEAD(Idx) OVER (PARTITION BY ID ORDER BY IdxRowNum) AS NextIdx, 
       LEAD(StartStop) OVER (PARTITION BY ID ORDER BY IdxRowNum) AS NextStartStop 
     FROM (
      SELECT ID, Idx, StartStop, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Idx, StartStop) AS IdxRowNum       
      FROM 
       (SELECT ID, START_Point, STOP_Point 
       FROM @T) Src 
      UNPIVOT (
       Idx FOR StartStop IN (START_Point, STOP_Point) 
      ) AS Unpvt 
     ) t 
    ) s 
) u 
WHERE StartIdx IS NOT NULL AND StopIdx IS NOT NULL 

위의 쿼리의 출력은 다음과 같습니다 :

ID StartIdx StopIdx 
----------------------- 
1 1  1 
1 2  4 
1 5  5 
1 6  10 
1 11  15 
2 1  1 
2 2  2 
2 3  4 
2 5  5 
2 6  10 
2 11  20 

CTE를에 이전 쿼리를 사용하여 각 ID에 대한 기존의 모든 간격을 얻을 수있는 근거로 위의 쿼리를 사용

CROSS APPLY을 실행하면 원하는 결과를 얻을 수 있습니다.

; WITH IntervalsByID AS 
(
    SELECT DISTINCT ID, StartIdx, StopIdx 
    FROM 
    (
     SELECT ID, 
       CASE 
       WHEN StartStop = 'START_Point' AND NextStartStop = 'START_Point' THEN 
        CASE WHEN NextIdx > Idx THEN Idx ELSE NULL END 
       WHEN StartStop = 'STOP_Point' AND NextStartStop = 'STOP_Point' THEN 
        CASE WHEN NextIdx > Idx THEN Idx + 1 ELSE NULL END 
       WHEN StartStop = 'STOP_Point' AND NextStartStop = 'START_Point' THEN 
        CASE WHEN NextIdx > Idx THEN Idx + 1 ELSE NULL END 
       WHEN StartStop = 'START_Point' AND NextStartStop = 'STOP_Point' THEN Idx   
       END AS StartIdx, 
       CASE 
       WHEN StartStop = 'START_Point' AND NextStartStop = 'START_Point' THEN 
        CASE WHEN NextIdx > Idx THEN NextIdx - 1 ELSE NULL END 
       WHEN StartStop = 'STOP_Point' AND NextStartStop = 'STOP_Point' THEN 
        CASE WHEN NextIdx > Idx THEN NextIdx ELSE NULL END 
       WHEN StartStop = 'START_Point' AND NextStartStop = 'STOP_Point' THEN NextIdx 
       WHEN StartStop = 'STOP_Point' AND NextStartStop = 'START_Point' THEN 
        CASE WHEN NextIdx = Idx + 1 THEN NextIdx WHEN NextIdx > Idx THEN NextIdx- 1 ELSE NULL END 
       ELSE Idx 
       END AS StopIdx 
     FROM 
     (
      SELECT ID, Idx, StartStop, 
        LEAD(Idx) OVER (PARTITION BY ID ORDER BY IdxRowNum) AS NextIdx, 
        LEAD(StartStop) OVER (PARTITION BY ID ORDER BY IdxRowNum) AS NextStartStop 
      FROM (
       SELECT ID, Idx, StartStop, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Idx, StartStop) AS IdxRowNum       
       FROM 
        (SELECT ID, START_Point, STOP_Point 
        FROM @T) Src 
       UNPIVOT (
        Idx FOR StartStop IN (START_Point, STOP_Point) 
       ) AS Unpvt 
      ) t 
     ) s 
    ) u 
    WHERE StartIdx IS NOT NULL AND StopIdx IS NOT NULL 
) 
SELECT t.PrimaryKey, t.ID, s.StartIdx, s.StopIdx 
FROM @T AS t 
CROSS APPLY 
( 
    SELECT * 
    FROM IntervalsByID 
    WHERE ID = t.ID AND t.START_Point <= StartIdx AND t.STOP_Point >= StopIdx 
) s 
ORDER BY PrimaryKey, StartIdx 

출력 :

PrimaryKey ID StartIdx StopIdx 
    -------------------------------------- 
    1   1 1   1 
    1   1 2   4 
    1   1 5   5 
    1   1 6   10 
    2   1 2   4 
    3   1 5   5 
    4   1 5   5 
    5   1 5   5 
    5   1 6   10 
    5   1 11   15 
    6   2 5   5 
    7   2 2   2 
    8   2 1   1 
    8   2 2   2 
    8   2 3   4 
    8   2 5   5 
    8   2 6   10 
    9   2 1   1 
    9   2 2   2 
    9   2 3   4 
    9   2 5   5 
    9   2 6   10 
    9   2 11   20 
+0

입니다. Sir Hats off ... 당신의 솔루션은 매력처럼 작동합니다. 많이 고맙습니다. 당신은 정말로 별이 있습니다.이 질문은 간단하지만 솔루션은 똑같은 복합체입니다. 당신은 훌륭한 논리를 적용합니다 ... 제가 어떤 사이트 나 블로그에서 당신을 따를 수 있는지 알려주세요 .. 다시 한번 고마워요. –

관련 문제