문제

다음 데이터가 포함된 테이블이 있습니다(예를 들어 단순화됨).

Row Start       Finish       ID  Amount
--- ---------   ----------   --  ------
  1 2008-10-01  2008-10-02   01      10
  2 2008-10-02  2008-10-03   02      20
  3 2008-10-03  2008-10-04   01      38
  4 2008-10-04  2008-10-05   01      23
  5 2008-10-05  2008-10-06   03      14
  6 2008-10-06  2008-10-07   02       3
  7 2008-10-07  2008-10-08   02       8
  8 2008-10-08  2008-11-08   03      19

날짜는 기간을 나타내며, ID는 해당 기간 동안 시스템이 있었던 상태이고, 금액은 해당 상태와 관련된 값입니다.

내가 하고 싶은 것은 다음에 대한 금액을 집계하는 것입니다. 인접한같은 ID 번호는 동일하지만 연속 실행이 결합될 수 있도록 동일한 전체 순서를 유지합니다.따라서 나는 다음과 같은 데이터로 끝나고 싶습니다.

Row Start       Finish       ID  Amount
--- ---------   ----------   --  ------
  1 2008-10-01  2008-10-02   01      10
  2 2008-10-02  2008-10-03   02      20
  3 2008-10-03  2008-10-05   01      61
  4 2008-10-05  2008-10-06   03      14
  5 2008-10-06  2008-10-08   02      11
  6 2008-10-08  2008-11-08   03      19

SP에 넣을 수 있는 T-SQL 솔루션을 찾고 있지만 간단한 쿼리로 이를 수행하는 방법을 알 수 없습니다.나는 그것이 일종의 반복이 필요할 것이라고 생각하지만 나는 그런 길을 가고 싶지 않습니다.

내가 이 집계를 수행하려는 이유는 프로세스의 다음 단계가 시퀀스 내에서 발생하는 고유 ID별로 그룹화된 SUM() 및 Count()를 수행하여 최종 데이터가 다음과 같게 되기 때문입니다.

ID  Counts Total
--  ------ -----
01       2    71
02       2    31
03       2    33

그러나 내가 간단한 작업을 수행하면

SELECT COUNT(ID), SUM(Amount) FROM data GROUP BY ID

원래 테이블에서 나는 다음과 같은 것을 얻습니다.

ID  Counts Total
--  ------ -----
01       3    71
02       3    31
03       2    33

그것은 내가 원하는 것이 아닙니다.

올바른 솔루션이 없습니다

다른 팁

"SQL의 시간 지향 데이터베이스 응용 프로그램 개발"책을 읽는 경우 rt snodgrass (PDF는 그의 웹 사이트에서 출판물로 제공됩니다), P165-166에서 그림 6.25까지 얻을 수 있으면 현재 예제에서 사용할 수있는 비 사소한 SQL을 찾을 수 있습니다. 동일한 ID 값 및 연속 시간 간격.

아래의 쿼리 개발은 올바른 곳에 가깝지만 결국 첫 번째 선택 문에 소스가있는 문제가 있습니다. 왜 잘못된 대답이 주어 지는지 아직 추적하지 않았습니다. 누군가 DBMS에서 SQL을 테스트하고 첫 번째 쿼리가 올바르게 작동하는지 말해 줄 수 있다면 큰 도움이 될 것입니다!

그것은 다음과 같습니다.

-- Derived from Figure 6.25 from Snodgrass "Developing Time-Oriented
-- Database Applications in SQL"
CREATE TABLE Data
(
    Start   DATE,
    Finish  DATE,
    ID      CHAR(2),
    Amount  INT
);

INSERT INTO Data VALUES('2008-10-01', '2008-10-02', '01', 10);
INSERT INTO Data VALUES('2008-10-02', '2008-10-03', '02', 20);
INSERT INTO Data VALUES('2008-10-03', '2008-10-04', '01', 38);
INSERT INTO Data VALUES('2008-10-04', '2008-10-05', '01', 23);
INSERT INTO Data VALUES('2008-10-05', '2008-10-06', '03', 14);
INSERT INTO Data VALUES('2008-10-06', '2008-10-07', '02',  3);
INSERT INTO Data VALUES('2008-10-07', '2008-10-08', '02',  8);
INSERT INTO Data VALUES('2008-10-08', '2008-11-08', '03', 19);

SELECT DISTINCT F.ID, F.Start, L.Finish
    FROM Data AS F, Data AS L
    WHERE F.Start < L.Finish
      AND F.ID = L.ID
      -- There are no gaps between F.Finish and L.Start
      AND NOT EXISTS (SELECT *
                        FROM Data AS M
                        WHERE M.ID = F.ID
                        AND F.Finish < M.Start
                        AND M.Start < L.Start
                        AND NOT EXISTS (SELECT *
                                            FROM Data AS T1
                                            WHERE T1.ID = F.ID
                                              AND T1.Start <  M.Start
                                              AND M.Start  <= T1.Finish))
      -- Cannot be extended further
      AND NOT EXISTS (SELECT *
                          FROM Data AS T2
                          WHERE T2.ID = F.ID
                            AND ((T2.Start <  F.Start  AND F.Start  <= T2.Finish)
                              OR (T2.Start <= L.Finish AND L.Finish <  T2.Finish)));

해당 쿼리의 출력은 다음과 같습니다.

01  2008-10-01      2008-10-02
01  2008-10-03      2008-10-05
02  2008-10-02      2008-10-03
02  2008-10-06      2008-10-08
03  2008-10-05      2008-10-06
03  2008-10-05      2008-11-08
03  2008-10-08      2008-11-08

편집: 두 번째 행에 문제가 있습니다. 거기에있어서는 안됩니다. 그리고 나는 그것이 어디에서 왔는지 명확하지 않습니다.

이제 우리는 해당 복잡한 표현식을 다른 select 문의 절에서 쿼리 표현식으로 취급해야하며, 이는 위에 표시된 최대 범위와 겹치는 항목에 대한 주어진 ID의 금액 값을 요약합니다.

SELECT M.ID, M.Start, M.Finish, SUM(D.Amount)
    FROM Data AS D,
         (SELECT DISTINCT F.ID, F.Start, L.Finish
              FROM Data AS F, Data AS L
              WHERE F.Start < L.Finish
                AND F.ID = L.ID
                -- There are no gaps between F.Finish and L.Start
                AND NOT EXISTS (SELECT *
                                    FROM Data AS M
                                    WHERE M.ID = F.ID
                                    AND F.Finish < M.Start
                                    AND M.Start < L.Start
                                    AND NOT EXISTS (SELECT *
                                                        FROM Data AS T1
                                                        WHERE T1.ID = F.ID
                                                          AND T1.Start <  M.Start
                                                          AND M.Start  <= T1.Finish))
                  -- Cannot be extended further
                AND NOT EXISTS (SELECT *
                                    FROM Data AS T2
                                    WHERE T2.ID = F.ID
                                      AND ((T2.Start <  F.Start  AND F.Start  <= T2.Finish)
                                        OR (T2.Start <= L.Finish AND L.Finish <  T2.Finish)))) AS M
    WHERE D.ID = M.ID
      AND M.Start  <= D.Start
      AND M.Finish >= D.Finish
    GROUP BY M.ID, M.Start, M.Finish
    ORDER BY M.ID, M.Start;

이것은 :

ID  Start        Finish       Amount
01  2008-10-01   2008-10-02   10
01  2008-10-03   2008-10-05   61
02  2008-10-02   2008-10-03   20
02  2008-10-06   2008-10-08   11
03  2008-10-05   2008-10-06   14
03  2008-10-05   2008-11-08   33              -- Here be trouble!
03  2008-10-08   2008-11-08   19

편집: 이것은 거의 원래 질문에 의해 요청 된 카운트 및 합계를 수행 할 올바른 데이터 세트는 다음과 같습니다.

SELECT I.ID, COUNT(*) AS Number, SUM(I.Amount) AS Amount
    FROM (SELECT M.ID, M.Start, M.Finish, SUM(D.Amount) AS Amount
            FROM Data AS D,
                 (SELECT DISTINCT F.ID, F.Start, L.Finish
                      FROM  Data AS F, Data AS L
                      WHERE F.Start < L.Finish
                        AND F.ID = L.ID
                        -- There are no gaps between F.Finish and L.Start
                        AND NOT EXISTS
                            (SELECT *
                                FROM  Data AS M
                                WHERE M.ID = F.ID
                                  AND F.Finish < M.Start
                                  AND M.Start < L.Start
                                  AND NOT EXISTS
                                      (SELECT *
                                          FROM Data AS T1
                                          WHERE T1.ID = F.ID
                                            AND T1.Start <  M.Start
                                            AND M.Start  <= T1.Finish))
                          -- Cannot be extended further
                        AND NOT EXISTS
                            (SELECT *
                                FROM  Data AS T2
                                WHERE T2.ID = F.ID
                                  AND ((T2.Start <  F.Start  AND F.Start  <= T2.Finish) OR
                                       (T2.Start <= L.Finish AND L.Finish <  T2.Finish)))
                 ) AS M
            WHERE D.ID = M.ID
              AND M.Start  <= D.Start
              AND M.Finish >= D.Finish
            GROUP BY M.ID, M.Start, M.Finish
          ) AS I
        GROUP BY I.ID
        ORDER BY I.ID;

id     number  amount
01      2      71
02      2      31
03      3      66

검토: 오! Drat ... 3의 항목은해야 할 '금액'의 두 배입니다. 이전의 '편집'부분은 상황이 잘못되기 시작한 곳을 나타냅니다. 첫 번째 쿼리가 미묘하게 틀린 것처럼 보이거나 (어쩌면 다른 질문을위한 것일 수도 있음), 내가 작업하는 옵티마이저는 잘못 행동하는 것입니다. 그럼에도 불구하고, 올바른 값을 줄 수있는 이와 밀접한 관련 대답이 있어야합니다.

레코드의 경우 : Solaris 10의 IBM Informix Dynamic Server 11.50에서 테스트되었습니다.

결과를 통해 커서와 루프를 만들어서 작업중인 ID를 추적하고 그 길을 따라 데이터를 축적해야 할 것입니다. ID가 변경되면 누적 된 데이터를 임시 테이블에 삽입하고 절차 끝에서 테이블을 반환 할 수 있습니다 (모두 선택). 테이블 기반 기능이 더 나을 수 있으므로 더 나을 수 있습니다. 그런 다음 갈 때 리턴 테이블에 삽입 할 수 있습니다.

나는 그것이 일종의 반복이 필요할 것이라고 생각하지만 나는 그런 길을 가고 싶지 않습니다.

나는 이것이 당신이 취해야 할 경로라고 생각합니다. 커서를 사용하여 테이블 변수를 채우십시오.레코드 수가 많은 경우 영구 테이블을 사용하여 결과를 저장할 수 있으며, 데이터를 검색해야 할 때 새 데이터만 처리할 수 있습니다.

어떤 레코드가 처리되었는지 추적하기 위해 소스 테이블에 기본값이 0인 비트 필드를 추가하겠습니다.아무도 테이블에서 select *를 사용하지 않는다고 가정하면 기본값이 있는 열을 추가해도 나머지 애플리케이션에는 영향을 미치지 않습니다.

솔루션 코딩에 도움이 필요하면 이 게시물에 댓글을 추가하세요.

글쎄, 나는 결합과 커서의 혼합물을 사용하여 반복 경로를 내려 가기로 결정했습니다. 데이터 테이블 자체에 가입하면 연속적인 레코드 만 링크 목록을 만들 수 있습니다.

INSERT INTO #CONSEC
  SELECT a.ID, a.Start, b.Finish, b.Amount 
  FROM Data a JOIN Data b 
  ON (a.Finish = b.Start) AND (a.ID = b.ID)

그런 다음 커서를 사용하여 반복하여 목록을 풀고 데이터 테이블로 다시 업데이트하여 조정하고 데이터 테이블에서 지금 외부 레코드를 삭제합니다).

DECLARE CCursor  CURSOR FOR
  SELECT ID, Start, Finish, Amount FROM #CONSEC ORDER BY Start DESC

@Total = 0
OPEN CCursor
FETCH NEXT FROM CCursor INTO @ID, @START, @FINISH, @AMOUNT
WHILE @FETCH_STATUS = 0
BEGIN
  @Total = @Total + @Amount
  @Start_Last = @Start
  @Finish_Last = @Finish
  @ID_Last = @ID

  DELETE FROM Data WHERE Start = @Finish
  FETCH NEXT FROM CCursor INTO @ID, @START, @FINISH, @AMOUNT
  IF (@ID_Last<> @ID) OR (@Finish<>@Start_Last)
    BEGIN
      UPDATE Data
        SET Amount = Amount + @Total
        WHERE Start = @Start_Last
      @Total = 0
    END  
END

CLOSE CCursor
DEALLOCATE CCursor

이것은 모두 작동하며 사용중인 일반적인 데이터에 대해 허용 가능한 성능을 가지고 있습니다.

위 코드에서 하나의 작은 문제를 찾았습니다. 원래 커서를 통해 각 루프의 데이터 테이블을 업데이트하고있었습니다. 그러나 이것은 작동하지 않았습니다. 레코드에서 하나의 업데이트 만 수행 할 수 있으며 (데이터 추가를 유지하기 위해) 여러 업데이트가 레코드의 원래 내용을 읽는 것으로 되돌아갑니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top