2012-07-19 4 views
6

Ich fragte mich, ob mir jemand mit dieser SQL-Anweisung helfen könnte?t-SQL, um Tabelle zu aktualisieren, um überlappende Zeitrahmen zu entfernen

Sag mal, ich habe eine SQL Server 2008-Tabelle wie folgt aus:

id -- INT PRIMARY KEY 
dtIn -- DATETIME2 
dtOut -- DATETIME2 
type -- INT 

id dtIn dtOut type 
1 05:00 10:00 1 
2 08:00 16:00 2 
3 02:00 08:00 1 
4 07:30 11:00 1 
5 07:00 12:00 2 

Ich brauche jederzeit zu entfernen, in der Tabelle oben überlappt. Dies kann mit diesem Diagramm veranschaulicht werden: enter image description here

So kam ich mit dieser SQL-up:

UPDATE [table] AS t 
SET dtOut = (SELECT MIN(dtIn) FROM [table] WHERE type = t.type AND t.dtIn >= dtIn AND t.dtIn < dtOut) 
WHERE type = t.type AND t.dtIn >= dtIn AND t.dtIn < dtOut 

Aber es funktioniert nicht. Irgendeine Idee, was mache ich hier falsch?

**** EDIT ****

OK, es dauerte eine Weile, um mich zu diesem zu erhalten. Scheint für eine funktionierende SQL zu sein, was ich brauche es für:

--BEGIN TRANSACTION; 

--delete identical dtIn 
DELETE dT1 
FROM tbl dT1 
WHERE EXISTS 
(
    SELECT * 
    FROM tbl dT2 
    WHERE dT1.Type = dT2.Type 
    AND dT1.dtIn = dT2.dtIn 
    AND (
      dT1.dtOut < dT2.dtOut 
      OR (dT1.dtOut = dT2.dtOut AND dT1.id < dT2.id) 
     ) 
); 

--adjust dtOuts to the max dates for overlapping section 
UPDATE tbl 
SET dtOut = COALESCE((
    SELECT MAX(dtOut) 
    FROM tbl as t1 
    WHERE t1.type = tbl.type 
    AND t1.dtIn < tbl.dtOut 
AND t1.dtOut > tbl.dtIn 
    ), dtOut); 

-- Do the actual updates of dtOut 
UPDATE tbl 
SET dtOut = COALESCE((
    SELECT MIN(dtIn) 
    FROM tbl as t2 
    WHERE t2.type = tbl.type AND 
      t2.id <> tbl.id AND 
      t2.dtIn >= tbl.dtIn AND t2.dtIn < tbl.dtOut 
    ), dtOut); 

--COMMIT TRANSACTION; 

Antwort

1

Direkt an der Spitze von meinem Kopf Ich glaube, dass man von Joe Celko die Bücher dieses Problem als Beispiel hatte. Sie finden den Auszug möglicherweise in Google.

Dies könnte näher sein. Ich denke, du hast die Unterabfrage nicht richtig gemacht.

UPDATE table 
SET dtOut = (
    SELECT MIN(t2.dtIn) 
    FROM [table] as t2 
    WHERE t2.id <> table.id AND t2.type = table.type 
     AND table.dtIn < t2.dtIn AND t2.dtIn < table.dtOut 
     AND table.dtOut <= t2.dtOut 
    ) 
WHERE EXISTS (
    SELECT 1 
    FROM [table] as t3 
    WHERE 
      t3.type = table.type 
     AND t3.id <> table.id 
     AND table.dtIn < t3.dtIn AND t3.dtIn < table.dtOut 
     AND table.dtOut <= t3.dtOut 
    ) 

EDIT übersehen ich die id-Spalte am oberen Rand der Seite so offensichtlich, dass es eine bessere Kontrolle als sicherstellen, dass die Endpunkte bis stimmen nicht überein. Die Lösung ist wahrscheinlich einfacher, wenn Sie davon ausgehen können, dass keine zwei Zeilen des gleichen Typs das dtIn haben.

Btw, es gibt keinen Grund, ein CROSS APPLY zu verwenden, wenn eine Unterabfrage genau die gleiche Aufgabe erledigt.

BEARBEITEN 2 Ich habe einige schnelle Tests durchgeführt und ich denke, dass meine Abfrage das Szenario in Ihrem Diagramm behandelt. Es gibt einen Fall, in dem es nicht tun könnte, was Sie wollen.

Für einen bestimmten Typ, denken Sie an die letzten beiden Segmente S1 und S2 in der Reihenfolge der Startzeit. S2 beginnt nach S1, aber stellen Sie sich auch vor, dass S1 endet. S2 ist vollständig in dem Intervall von S1 enthalten, so dass es entweder ignorierbar ist oder die Informationen für die zwei Segmente in ein drittes Segment aufgeteilt werden müssen, und das ist das Problem, in dem das Problem schwieriger wird.

Also diese Lösung nimmt nur an, dass sie ignoriert werden können.


EDIT 3 über die Kombination von Updates auf dem Kommentar auf Basis von OP

geschrieben

SQLFiddle

-- eliminate redundant rows DELETE dT1 /* FROM tbl dT1 -- unnecessary */ WHERE EXISTS ( SELECT * FROM tbl dT2 WHERE dT1.Type = dT2.Type AND dT1.dtIn = dT2.dtIn AND ( dT1.dtOut < dT2.dtOut OR (dT1.dtOut = dT2.dtOut AND dT1.id < dT2.id) ) ); --adjust dtOuts to the max dates UPDATE tbl SET dtOut = COALESCE(( SELECT MAX(dtOut) FROM tbl as t1 WHERE t1.type = tbl.type ), dtOut); -- Do the actual updates of dtOut UPDATE tbl SET dtOut = COALESCE(( SELECT MIN(dtIn) FROM tbl as t2 WHERE t2.type = tbl.type AND t2.id <> tbl.id AND t2.dtIn >= tbl.dtIn AND t2.dtIn < tbl.dtOut ), dtOut); 

Entweder eine der beiden Updates unten sollte über die beiden Updates ersetzen.

UPDATE tbl 
SET dtOut = (
    SELECT 
     COALESCE(
      MIN(dtIn), 
      /* as long as there's no GROUP BY, there's always one row */ 
      (SELECT MAX(dtOut) FROM tbl as tmax WHERE tmax.type = tbl.type) 
     ) 
    FROM tbl as tmin 
    WHERE tmin.type = tbl.type 

     AND tmin.dtIn > tbl.dtIn 
     /* 
     regarding the original condition in the second update: 
      t2.dtIn >= tbl.dtIn AND t2.dtIn < tbl.dtOut 

     dtIns can't be equal because you already deleted those 
     and if dtIn was guaranteed to be less than dtOut it's 
     also automatically always less than max(dtOut) 
     */ 
); 

UPDATE tbl 
SET dtOut = COALESCE(
    (
    SELECT MIN(dtIn) FROM tbl as tmin 
    WHERE tmin.type = tbl.type AND tmin.dtIn > tbl.dtIn 
), 
    (  
    SELECT MAX(dtOut) FROM tbl as tmax 
    WHERE tmax.type = tbl.type 
) 
); 
+0

Dank. Ich muss es ausprobieren. Nur aus Neugier, welches Buch und wo drin ist die von Ihnen erwähnte Code-Referenz? – ahmd0

+0

Ich denke Kapitel 29 von Joe Celko SQL für Smarties: Advanced SQL-Programmierung. Ich konnte das Inhaltsverzeichnis nicht die Kapitel sehen. – shawnt00

+0

Auch für die gleiche Zeile überprüfen, wäre es nicht einfacher, ID-Spalte stattdessen zu überprüfen? – ahmd0

2

Ich denke CROSS APPLY den Trick tun könnte:

DECLARE @T TABLE (ID INT, DTIn DATETIME2, dtOut DATETIME2, Type INT) 
INSERT @T VALUES 
(1, '05:00', '10:00', 1), 
(2, '08:00', '16:00', 2), 
(3, '02:00', '08:00', 1), 
(4, '07:30', '11:00', 1), 
(5, '07:00', '12:00', 2) 

UPDATE @T 
SET  DtOut = T3.DtOut 
FROM @T T1 
     CROSS APPLY 
     ( SELECT MIN(DtIn) [DtOut] 
      FROM @T T2 
      WHERE T2.Type = T1.Type 
      AND  T2.DtIn > T1.dtIn 
      AND  T2.DtIn < T1.dtOut 
     ) T3 
WHERE T3.dtOut IS NOT NULL 

SELECT * 
FROM @T 
+0

Interessant. Mir ist jedoch nicht klar, für was ist das letzte SELECT * FROM @T? – ahmd0

+0

Ich habe es nur aus dem Test gelassen, um die Daten zu zeigen. Es hat keinen Einfluss auf die Update-Anweisung. – GarethD

Verwandte Themen