Frage

Ich habe (vereinfacht für das Beispiel) eine Tabelle mit den folgenden Daten

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

Die Daten über einen Zeitraum in der Zeit darstellen, die ID ist der Staat ein System während dieser Zeit war und die Menge ist ein Wert in diesem Zustand zusammen.

Was ich tun möchte, ist es, die Beträge, die für neben Zeilen mit der gleichen ID-Nummer aggregieren, aber die gleiche Gesamtsequenz zu halten, so dass zusammenhängende Läufe kombiniert werden können. So mag ich mit Daten, um am Ende wie:

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

Ich bin nach einer T-SQL-Lösung, die in eine SP gesetzt werden kann, aber ich kann nicht sehen, wie mit einfachen Abfragen zu tun. Ich vermute, dass es Iteration irgendeine Art erfordern kann, aber ich will nicht auf diesem Weg gehen.

Der Grund, warum ich diese Aggregation tun möchte, ist, dass der nächste Schritt in dem Prozess ist durch die eindeutige ID des, die innerhalb der Sequenz auftreten, eine SUM () und Count () gruppiert zu tun, damit meine Enddaten etwas aussehen wird :

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

Allerdings, wenn ich eine einfache tun

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

Auf der ursprünglichen Tabelle bekomme ich so etwas wie

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

Was ist nicht das, was ich will.

Keine korrekte Lösung

Andere Tipps

Wenn Sie das Buch lesen „Die Entwicklung von zeitorientierte Datenbankanwendungen in SQL“ von RT Snodgrass (die pdf von denen aus seiner Website unter Publikationen zur Verfügung steht), und so weit wie Abbildung 6.25 auf p165-166 erhalten, werden Sie die nicht-triviale SQL finden, die die verschiedenen in dem aktuellen Beispiel zu einer Gruppe verwendet werden können, Zeilen mit dem gleichen ID-Wert und kontinuierlichen Zeitintervallen.

Die Abfrage Entwicklung unten ist in der Nähe zu korrigieren, aber es ist ein Problem, direkt am Ende entdeckt, die ihre Quelle in der ersten SELECT-Anweisung hat. Ich habe noch nicht aufgespürt, warum die falsche Antwort gegeben wird. [Wenn jemand die SQL auf dem DBMS testen und mir sagen, ob die erste Abfrage richtig es funktioniert, wäre es eine große Hilfe sein!]

Es sieht so etwas wie:

-- 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)));

Die Ausgabe von dieser Abfrage lautet:

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

Edited : Es gibt ein Problem mit der vorletzten Reihe - es sollte nicht da sein. Und ich bin nicht klar, (noch) nicht, wo es herkommt.

Nun müssen wir diese komplexen Ausdruck als Abfrageausdruck in der FROM-Klausel einer anderen SELECT-Anweisung behandeln, die die Menge Werte für eine bestimmte ID über die Einträge summieren werden, die mit den maximalen Bereiche überlappen oben gezeigt.

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;

Dies ergibt:

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

Edited : Dies ist fast die korrekten Daten fest, auf das die COUNT und SUM-Aggregation durch die ursprüngliche Frage gebeten zu tun, so dass die endgültige Antwort ist:

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

Überprüfen : Oh! Verflixt ... der Eintrag für 3 hat zweimal die ‚Menge‘, die es haben sollte. Zurück ‚bearbeitet‘ Teile angeben, wo die Dinge begannen schief zu gehen. Es sieht aus, als ob entweder die erste Abfrage auf subtile Weise falsch ist (vielleicht ist es für eine andere Frage soll) oder der Optimierer ich bin arbeitet misbehaving. Dennoch sollte es eine Antwort seines engen Zusammenhang damit, dass die richtigen Werte geben.

Für das Protokoll:. Getestet auf IBM Informix Dynamic Server 11.50 auf Solaris 10 sollte jedoch auf andere mäßig Standard-konformen SQL-DBMS funktioniert

müssen wahrscheinlich einen Cursor und eine Schleife durch die Ergebnisse erstellen, zu verfolgen, welche id Sie arbeiten und die Daten auf dem Weg zu akkumulieren. Wenn die ID ändert können Sie die gesammelten Daten in eine temporäre Tabelle einfügen und in der Tabelle am Ende des Verfahrens zurückzukehren (wählen Sie alle von ihm). Eine tabellenbasierte Funktion könnte besser sein, als Sie, dann können nur in die Rück Tabelle einfügen, wie Sie gehen.

  

Ich vermute, dass es Iteration irgendeine Art erfordern kann, aber ich will nicht, diesen Weg zu gehen.

Ich glaube, dass der Weg ist Sie nehmen müssen, verwenden Sie einen Cursor eine Tabelle Variable zu füllen. Wenn Sie eine große Anzahl von Datensätzen haben könnten Sie eine permanente Tabelle verwenden, um die Ergebnisse zu speichern, dann, wenn Sie die Daten abzurufen, müssen Sie nur die neuen Daten nicht verarbeiten konnte.

würde ich ein Bit-Feld mit einem Standardwert von 0 auf die Quelltabelle hinzufügen, um zu verfolgen, welche Datensätze verarbeitet wurden. Unter der Annahme, niemanden ist mit select * auf dem Tisch, eine Spalte mit einem Standardwert hinzugefügt wird nicht den Rest Ihrer Anwendung beeinflussen.

Fügen Sie einen Kommentar zu diesem Beitrag, wenn Sie helfen wollen, die Lösung Codierung.

Nun habe ich beschlossen, die Iteration Weg zu gehen mit einer Mischung aus Joins und Cursor. Durch das Verbinden der Datentabelle gegen sich selbst kann ich eine Linkliste der nur die Datensätze erstellen, die aufeinanderfolgend sind.

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)

Dann kann ich die Liste entspannen Sie mit einem Cursor über sie iterieren und Updates zurück auf die Datentabelle zu tun anzupassen (und die jetzt Fremd Datensätze aus der Datentabelle löschen)

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

Dies alles funktioniert und hat eine akzeptable Leistung für typische Daten, die ich verwende.

Ich habe ein kleines Problem mit dem obigen Code finden. Ursprünglich war die Aktualisierung ich die Datentabelle auf jeder Schleife durch den Cursor. Aber das hat nicht funktioniert. Es scheint, dass Sie nur ein Update auf einem Datensatz tun, und dass mehrere Aktualisierungen (zu halten, um Hinzufügen von Daten) zu der Lese des ursprünglichen Inhalt des Datensatz wieder zurück.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top