Sie brauchen ein paar Dinge, und dann fällt diese Abfrage einfach zusammen.
Erstens, vorausgesetzt, Sie benötigen mehrere Daten, werden Sie wollen, was als Calendar Table
(Hands-down, wahrscheinlich die nützlichste Analysetabelle) bekannt ist.
Als nächstes wirst du entweder eine vorhandene Numbers
Tabelle wollen, wenn Sie eine haben, oder erzeugen nur die erste on the fly:
WITH Halfs AS (SELECT CAST(0 AS INT) m
UNION ALL
SELECT m + 1
FROM Halfs
WHERE m < 24 * 2)
SELECT m
FROM Halfs
(rekursive CTE - erzeugt eine Tabelle mit einer Liste von Zahlen beginnend bei 0).
Diese beiden Tabellen bieten die Grundlage für eine Bereichsabfrage basierend auf den Zeitstempeln in Ihrer Haupttabelle. Dadurch wird es für den Optimierer sehr einfach, Bucket-Zeilen für jede Aggregation, die Sie gerade ausführen, zu erstellen. Das durch CROSS JOIN
Sie die zwei Tabellen zusammen in einer Unterabfrage gemacht wird, sowie das Hinzufügen ein paar andere abgeleitete Spalten:
WITH Halfs AS (SELECT CAST(0 AS INT) m
UNION ALL
SELECT m + 1
FROM Halfs
WHERE m < 24 * 2)
SELECT calendarDate, m, rangeStart, rangeEnd
FROM (SELECT Calendar.calendarDate, Halfs.m rangeGroup,
DATEADD(minutes, m * 30, CAST(Calendar.calendarDate AS DATETIME2) rangeStart,
DATEADD(minutes, (m + 1) * 30, CAST(Calendar.calendarDate AS DATETIME2) rangeEnd
FROM Calendar
CROSS JOIN Halfs
WHERE Calendar.calendarDate >= CAST('20160823' AS DATE)
AND Calendar.calendarDate < CAST('20160830' AS DATE)
-- OR whatever your date range actually is.
) Range
ORDER BY rangeStart
(beachten Sie, dass, wenn der Datumsbereich ausreichend groß ist, kann es vorteilhaft sein, zu speichern Dies ist eine temporäre Tabelle mit Indizes. Bei kleinen Tabellen und Datasets ist die Leistungssteigerung wahrscheinlich nicht spürbar.
Jetzt, da wir unsere Bereiche haben, ist es trivial, unsere Gruppen zu erhalten und die Tabelle zu drehen.
Oh, und SQL Server hat einen spezifischen Operator für PIVOT
ing.
WITH Halfs AS (SELECT CAST(0 AS INT) m
UNION ALL
SELECT m + 1
FROM Halfs
WHERE m < 3 * 2)
-- Intentionally limiting range for example only
SELECT calendarDate AS day, [0], [1], [2], [3], [4], [5], [6]
-- If you're displaying "nice" names,
-- do it at this point, or in the reporting application
FROM (SELECT Range.calendarDate, Range.rangeGroup
FROM (SELECT Calendar.calendarDate, Halfs.m rangeGroup,
DATEADD(minutes, m * 30, CAST(Calendar.calendarDate AS DATETIME2) rangeStart,
DATEADD(minutes, (m + 1) * 30, CAST(Calendar.calendarDate AS DATETIME2) rangeEnd
FROM Calendar
CROSS JOIN Halfs
WHERE Calendar.calendarDate >= CAST('20160823' AS DATE)
AND Calendar.calendarDate < CAST('20160830' AS DATE)
-- OR whatever your date range actually is.
) Range
LEFT JOIN Test
ON Test.user_id IS NOT NULL
AND Test.start_date >= Range.rangeStart
AND Test.start_date < Range.rangeEnd
) AS DataTable
PIVOT (COUNT(*)
FOR Range.rangeGroup IN ([0], [1], [2], [3], [4], [5], [6])) AS PT
-- Only covers the first 6 groups,
-- or the first three hours.
ORDER BY day
Der Schwenk soll die immer einzelnen Spalten kümmern, und COUNT
automatisch null Zeilen lösen. Sollte alles sein, was Sie brauchen.
iff wird nicht richtig verwendet –