2010-11-17 17 views
5

Zwei temporäre Tabellen werden erstellt und dann geladen ... Hier ist das Schema.T-SQL Skript - logische Probleme mit Timeline

Create table #SH ([date] datetime, 
     sched_id int, 
     agent_id int) 

Create table #SD (sched_id int, 
     start_minute int, 
     length int, 
     exception_code int) 

(Schema und Design etwas, das ich leider nicht ändern kann, werden beide temporären Tabellen aus flachen Dateien geladen. Ich kann neue temporäre Tabellen vorstellen und laden Sie, wenn nötig).

Ein wenig Hintergrund - Die Header-Tabelle #SH enthält einen Personenplan als 'Start_minute' und geht nach 'schedule_length' in Minuten. Zum Beispiel, wenn Start Minute und Zeitplan Länge beide 480 waren, würde dies als 8 Uhr (8 Uhr = 480. Minute) und geht bis 4 Uhr (480 Minuten später, 4 Uhr = 960. Minute)

Die # SD-Tabelle enthält Ausnahmen zu den Header. Im obigen Beispiel hätte die Person wahrscheinlich eine Ausnahme für das Mittagessen, die start_minute = 720 und die Länge von 30 (12:00 - 12:30) wäre.

Datum und agent_id das einzige, was ich bin interessiert aus #sh, die Ausnahme Informationen in #sd ist das, was mich interessiert

Diese Abfrage funktioniert.

Select [date],#sd.start_minute,#sd.length,#sd.start_minute + #sd.length as 'end_minute',agent_id 
from #SH 
inner join #SD on #SD.sched_id = #sh.sched_id 

* end_minute ist letztlich ein berechneter Wert von Start + Länge = Ende

Das gibt so etwas wie:

Date  Start length end 

1 2010-11-11 600 30 630 

2 2010-11-11 630 40 670 

3 2010-11-11 750 15 765 

4 2010-11-11 800 40 840 

Jetzt wünschte ich, ich könnte sagen, das war vorbei und weg ... aber es gibt Probleme mit der Dateneingabe. In Zeile 1 und 2 ist die Endzeit der Linie 1 Linien mit der Startzeit in Zeile 2 und kombiniert werden, sollte so aussehen meine Ergebnisse wie folgt aus:

Date  Start length  end 

1 2010-11-11 600 70 670 

2 2010-11-11 750 15 765 

3 2010-11-11 800 40 840 

Jede Idee ist, wie diese Logik zu bauen, damit ich die 3 Zeilen zurück statt 4? Ich arbeite gerade daran, die Tabelle auf # sd1.start + # sd1.length = # sd2.start für sich selbst zu verbinden.

Und noch komplizierter ... das obige Beispiel war 2 Zeilen, die kombiniert werden mussten. Ich bin auf einen Rekord gestoßen, der 30 Ein-Minuten-Einträge in Folge hatte, die ich zu einem einzigen Datensatz machen muss. Glücklicherweise können sie sich nicht überschneiden (Sie haben nicht 2 Datensätze in den gleichen Minuten), aber ich denke nicht, dass die Join-Anweisung, die ich oben betrachte, dafür geeignet ist.

+0

Habe ein paar Änderungen an meiner eigenen Grammatik gemacht ... es gibt einige Spalten mehr in #SH, aber sie sind irrelevant. – Twelfth

Antwort

2

Keine Notwendigkeit für eine CTE, alles was Sie brauchen, ist eine Hilfstabelle. Erstellen Sie es einmal, wie so:

Create Table DayMinute(Minute Integer) 
Declare @M Integer 
Set @M = 1 
While (@M <= 24*60) 
Begin 
    Insert Into DayMinute(Minute) Values(@M) 
    Set @M = @M + 1 
End 

Dann alles, was Sie brauchen, ist ein bisschen tricksiness:

Select 
    DM.Minute, 
    SD.Sched_ID 
Into #MinutesWithException 
From 
    DayMinute As DM 
    Inner Join #SD As SD 
    On DM.Minute Between SD.Start_Minute And SD.Start_Minute + Length 

Select 
    MWE.Sched_ID, 
    SH.[Date], 
    SH.Agent_ID, 
    [Start_Minute] = MWE.Minute, 
    [End_Minute] = (Select Min(Last.Minute) -- First one to have no successor 
        From #MinutesWithException As Last 
        Where Last.Sched_ID = MWE.Sched_ID 
        And Last.Minute > MWE.Minute 
        And Not Exists(Select * 
            From #MinutesWithException As Next 
            Where Next.Sched_ID = MWE.Sched_iD 
            And Next.Minute = Last.Minute + 1)) 
From 
    #MinutesWithException As MWE 
    Inner Join #SH As SH 
    On MWE.Sched_ID = SH.Sched_ID 
Where 
    Not Exists(Select * -- All those without predecessor 
      From #MinutesWithException As Previous 
      Where Previous.Sched_ID = MWE.Sched_ID 
       And Previous.Minute = MWE.Minute - 1) 

Denken Sie daran, kann eine Menge von SQL-Probleme gelöst werden, indem sie Umformulierung. Frag nicht "welche Bereiche keine Lücke haben", frag "welche Minuten ein Intervall haben". Der Rest folgt von dort.

+0

Ich dachte das Gleiche auf diesem Stu ... neu, es musste eine alternative Betrachtungsweise geben. Ich muss ein paar Modifikationen vornehmen, um es in den Rest des Drehbuchs zu integrieren, ich werde dich wissen lassen, wie es läuft. Und wow, Prozessorintensiv. – Twelfth

+0

Ist das wirklich effizienter oder einfacher zu lesen? –

+0

Nicht sicher auf dem effizienteren ... es pinning meinen Prozessor bei 100%, wenn ich es betreibe. Es scheint, zumindest den Trick zu machen, ich frage mich, wie es reagieren wird, wenn ich es 20k Datensätze anstelle von 4 füttere. – Twelfth

1

Wenn Sie einen rekursiven CTE verwenden, um die Ergebnisse der oben genannten Abfrage zu kombinieren, können Sie bis zu 32767 Datensätze miteinander verketten. Sie könnten diesen Ansatz in Betracht ziehen, wenn Sie nicht glauben, dass Sie jemals in die Nähe kommen.

Ich habe ein funktionierendes Beispiel erstellt, weil ich nicht sicher war. Ihre Gruppierung wäre anders, aber das ist die allgemeine Idee:

CREATE TABLE times 
(
[Date] datetime, 
[start] int, 
[length] int, 
[end] int 
) 
INSERT INTO times([Date], [Start], [length], [End]) 
VALUES ('11/11/2010',600,30,630) 
INSERT INTO times([Date], [Start], [length], [End]) 
VALUES ('11/11/2010',630,40,670) 
INSERT INTO times([Date], [Start], [length], [End]) 
VALUES ('11/11/2010',750,15,765) 
INSERT INTO times([Date], [Start], [length], [End]) 
VALUES ('11/11/2010',800,40,840) 

;WITH chaintimes AS 
(
    SELECT t1.Date, t1.start, t1.length, t1.[end] 
    FROM times t1 LEFT JOIN times t2 ON t1.start = t2.[end] 
    WHERE t2.[end] IS NULL 
    UNION ALL 
    SELECT times.Date, chaintimes.start, chaintimes.length + times.length AS length, times.[end] 
    FROM times INNER JOIN chaintimes ON times.start = chaintimes.[end] 
) 
, start_maxlength AS 
(
    SELECT date, start, max(length) AS maxlength 
    FROM chaintimes 
    group by date, start 
) 
SELECT * FROM chaintimes ct 
INNER JOIN start_maxlength ml 
ON ct.Date = ml.Date AND ct.start = ml.start AND ct.length = ml.maxlength 
+0

Ich hätte meine Umgebung auf diesem ... MSSQL 2000 erwähnt. Dies sieht PL/SQL. Ich werde es versuchen, aber ich vermute einen Fehler. Ich glaube, ich verstehe die Logik, die Sie hier mindestens haben ... Lassen Sie mich versuchen, meinen aktuellen Code mit dieser Art von Logik zu ändern – Twelfth

+0

Ah, ja CTE wurde in MS SQL 2005 eingeführt, keine Notwendigkeit zu stören. –

+0

Obwohl ich stimme überhaupt nicht zu, dass dieser Mangel an Kontext ein guter Grund für eine * downvote * ist. –