2010-08-31 11 views
6

Ich habe eine Tabelle von Daten, die ein bisschen wie folgt aussieht:Durchlaufen Daten in SQL

Name StartTime    FinishTime    Work 
Bob  2010-08-03 08:00:00 2010-08-03 12:00:00  4 
Bob  2010-08-03 13:00:00 2010-08-03 16:00:00  3 
Pete 2010-08-04 08:00:00 2010-08-04 12:00:00  4 
Mark 2010-08-04 10:00:00 2010-08-04 12:00:00  2 

Keine dieser Datumsbereiche sollten immer überspannen über Mitternacht.
Ich möchte SQL schreiben, die mir die folgende Ausgabe, da ein Eingabestartdatum von 2010-08-02 und ein Endtermin 2010-08-05

Date   Name TotalWork 
2010-08-03 Bob 7 
2010-08-03 Pete 3 
2010-08-04 Pete 4 
2010-08-04 Mark 2 

ich leben könnte, geben wird und in Tatsächlich kann schließlich müssen alle Tage haben, die im Zusammenhang haben keine Arbeit auch in den dargestellten Ergebnissen dargestellt werden, vielleicht als eine Zeile wie folgt aus:

2010-08-05  NULL 0 

ich nicht ganz sicher bin, wie durch Daten in SQL iterieren genauso wie ich es mit anderen Sprachen machen würde.

Um dies zu verdeutlichen, wird der Ausgang dieses Anschlusses letztendlich in eine Stacked Chart .Net-Steuerung gesteckt.

Könnte mir jemand einen Hinweis, einen Link zu einem Tutorial oder eine andere Hilfe geben? Sonst denke ich, dass ich tagelang damit herumhantieren werde!

Vielen Dank!

Jonathan

Antwort

7

Try this:

Select DateAdd(day, 0, DateDiff(day, 0, StartDate)) Date, 
    Name, Sum (Work) TotalWork 
From TableData 
Group By Name, DateAdd(day, 0, DateDiff(day, 0, StartDate)) 

Um die fehlenden Tage zu bekommen, ist schwieriger.

Declare @SD DateTime, @ED DateTime -- StartDate and EndDate variables 
    Select @SD = DateAdd(day, 0, DateDiff(day, 0, Min(StartDate))), 
      @ED = DateAdd(day, 0, DateDiff(day, 0, Max(StartDate))) 
    From TableData 
    Declare @Ds Table (aDate SmallDateTime) 
    While @SD <= @ED Begin 
     Insert @Ds(aDate) Values @SD 
     Set @SD = @SD + 1 
    End 
-- ---------------------------------------------------- 
Select DateAdd(day, 0, DateDiff(day, 0, td.StartDate)) Date, 
    td.Name, Sum (td.Work) TotalWork 
From @Ds ds Left Join TableData td 
    On DateAdd(day, 0, DateDiff(day, 0, tD.StartDate)) = ds.aDate 
Group By Name, DateAdd(day, 0, DateDiff(day, 0, tD.StartDate)) 

BEARBEITEN, ich revidiere dies mit einer Lösung, die einen gemeinsamen Tabellenausdruck (CTE) verwendet. Dies erfordert NICHT die Verwendung einer Dattentabelle.

Declare @SD DateTime, @ED DateTime 
    Declare @count integer = datediff(day, @SD, @ED) 
    With Ints(i) As 
     (Select 0 Union All 
    Select i + 1 From Ints 
    Where i < @count) 
    Select DateAdd(day, 0, DateDiff(day, 0, td.StartDate)) Date, 
     td.Name, Sum (td.Work) TotalWork 
    From Ints i 
     Left Join TableData d 
      On DateDiff(day, @SD, d.StartDate) = i.i 
    Group By d.Name, DateAdd(day, 0, DateDiff(day, 0, d.StartDate)) 
+0

Ah ... das sieht wirklich gut aus, danke. Ich nehme es an, wenn ich die Parameter @SD und @ED verwenden möchte, unabhängig davon, ob ich die fehlenden Tage haben will, dann muss ich die temporäre Tabelle erstellen? – JonRed

+0

@JonRed, Nein, wenn Sie die fehlenden Daten nicht möchten, dann verwenden Sie die erste SQL. In diesem Fall benötigen Sie weder die T-Sql-Variablen noch die temporäre Tabelle. Wenn Sie die fehlenden Daten benötigen, benötigen Sie die temporäre Tabelle und die T-Sql-Variablen sind erforderlich, um sie zu füllen. –

+0

Wirklich guter Trick ... – Zafer

5

Die Art, wie Sie Zeilen in SQL durchlaufen, ist, dass Sie nicht. SQL ist eine Set-basierte Sprache, die eine ganz andere Denkweise als andere prozedurale Sprachen erfordert. Wenn Sie mit SQL arbeiten, müssen Sie wirklich in der Lage sein, diesen Denkwechsel zum Erfolg zu führen.

Hier ist, wie ich ein damit umgehen würde:

SELECT 
    CONVERT(VARCHAR(10), StartTime, 121) AS [date], 
    name, 
    SUM(work) 
FROM 
    My_Table 
WHERE 
    StartTime >= @start_date AND 
    StartTime < DATEADD(dy, 1, @finish_date) 
GROUP BY 
    CONVERT(VARCHAR(10), StartTime, 121), 
    name 

Auch Tisch Design sieht aus wie es normal, Datenbank-Design-Standards verstößt. Ihre Spalte "Arbeit" ist in Wirklichkeit nur eine Berechnung zwischen StartTime und FinishTime. Das macht eine Duplizierung der gleichen Daten, die alle möglichen Probleme verursachen können. Zum Beispiel, was machst du, wenn StartTime und FinishTime 4 Stunden auseinander liegen, aber die "Arbeit" sagt 5 Stunden?

Um Daten ohne Arbeit zuzuordnen, müssen Sie entweder das Frontend behandeln, oder Sie benötigen eine "Kalender" -Tabelle. Es würde alle Daten darin enthalten, und Sie würden einen LINKEN VERBINDEN dazu mit Ihrem Tisch machen. Zum Beispiel:

SELECT 
    CONVERT(VARCHAR(10), C.StartTime, 121) AS [date], 
    MT.name, 
    SUM(MT.work) 
FROM 
    Calendar C 
LEFT JOIN My_Table MT ON 
    MT.StartDate BETWEEN C.StartTime and C.FinishTime 
WHERE 
    C.StartTime >= @start_date AND 
    C.StartTime < DATEADD(dy, 1, @finish_date) 
GROUP BY 
    CONVERT(VARCHAR(10), C.StartTime, 121), 
    MT.name 

Der Kalender Tabelle können Sie auch weitere Informationen zu den Terminen, wie eine Flagge für den Urlaub zu können „Überstunden“ Tage (vielleicht gilt als Zeit arbeiten und eine Hälfte an Sonntagen) usw.

ANMERKUNG: Die Lösung von Charles Bretana ist wahrscheinlich ein wenig sauberer, da die Datentypen als Datumswerte beibehalten werden, anstatt sie in Zeichenfolgen umzuwandeln. Ich werde das hier für einige der anderen Kommentare hier lassen.

+0

Meintest du 'Convert'? –

+0

Arbeit ist leider nicht nur der Unterschied zwischen zwei Daten; Es gibt ein% -Ergebniselement, das in dieser Tabelle nicht erfasst wird. Der Einfachheit halber habe ich mehrere verknüpfte Tabellen als eine Tabelle dargestellt. Danke dafür. Während ich die Probleme durcharbeite, verändert sich mein Design und deine Kommentare sind sehr hilfreich. – JonRed

+1

@Charles - Nein, COVERT ist eine neue ANSI-Standardfunktion, wenn Sie streng geheime Spalten benötigen. ;) Danke für die Korrektur. –