2012-07-19 7 views
6

Zastanawiam się, czy ktoś może mi pomóc z tym oświadczeniem SQL?t-SQL do aktualizacji tabeli w celu usunięcia nakładających się ramek czasowych

Powiedz, mam tabeli SQL Server 2008 jak poniżej:

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 

muszę usunąć dowolny czas zachodzi w powyższej tabeli. Można to zilustrować z tym schematem: enter image description here

Więc wymyśliłem tego SQL:

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 

Ale to nie działa. Masz pojęcie, co tu robię źle?

**** EDIT ****

OK, zajęło mi trochę czasu, aby dostać się do tego. Wydaje się, że SQL pracuje na co muszę go:

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

Odpowiedz

1

Tuż przy mojej głowie wierzę, że jedna z książek Joe Čelko miał to jako przykład problemu. Możesz znaleźć fragment dostępny w Google.

To może być bliżej. Myślę, że naprawdę nie robiłeś podzapytania we właściwy sposób.

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 przeoczyłem Kolumna ID w górnej części strony, więc oczywiście, że to lepiej sprawdzić niż upewniając się, że punkty końcowe nie zgadzają się. Rozwiązanie jest prawdopodobnie łatwiejsze, jeśli można założyć, że żadne dwa wiersze identycznego typu nie mają dtIn.

Btw, nie ma powodu, aby używać CROSS APPLY, gdy podzapytanie wykona dokładnie to samo zadanie.

EDIT 2 Zrobiłem kilka szybkich testów i myślę, że moje zapytanie obsługuje scenariusz w diagramie. Jest jeden przypadek, w którym może nie robić tego, co chcesz.

Dla danego typu, pomyśl o ostatnich dwóch segmentach S1 i S2 w kolejności czasu rozpoczęcia. S2 rozpoczyna się po S1, ale także wyobraź sobie, że kończy się przed S1. S2 jest w pełni zawarty w przedziale S1, więc jest albo nie do pomyślenia, albo informacja dla dwóch segmentów musi zostać podzielona na trzeci segment, i tam problem staje się trudniejszy.

To rozwiązanie zakłada tylko, że można je zignorować.


EDIT 3 opiera się na komentarzu o łączących aktualizacjach

SQLFiddle napisanych przez OP

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

Albo jeden z dwóch aktualizacjach poniżej należy wymienić dwie aktualizacje powyżej.

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

Dzięki. Muszę to wypróbować. Tylko z ciekawości, jakiej książki i gdzie w niej jest odniesienie do kodu, o którym wspomniałeś? – ahmd0

+0

Myślę, że rozdział 29 z Joe Celko's SQL for Smarties: Advanced SQL Programming. Widziałem spis treści, a nie rozdziały. – shawnt00

+0

Również w przypadku tego samego sprawdzania wiersza nie będzie łatwiej sprawdzać kolumnę id? – ahmd0

2

myślę CROSS APPLY może załatwić sprawę:

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

Interesujące. Nie jestem jednak jasny - do czego służy ostatni SELECT * FROM @T? – ahmd0

+0

Właśnie opuściłem to z testów, aby pokazać dane. Nie ma to wpływu na oświadczenie aktualizacji. – GarethD