Das Problem, das Sie bei diesem Problem haben werden, besteht darin, dass die Lösungen zur Lösung mit TSQL nicht gut skalieren, wenn der Datensatz wächst. Im Folgenden wird eine Reihe von temporären Tabellen verwendet, die spontan erstellt werden, um das Problem zu lösen. Es teilt jeden Datumsbereichseintrag mithilfe einer Zahlentabelle in seine jeweiligen Tage auf. Hier wird es nicht skaliert, hauptsächlich aufgrund Ihrer offenen NULL-Werte, die scheinbar unendlich sind, sodass Sie ein festes Datum weit in der Zukunft eintauschen müssen, das den Konvertierungsbereich auf eine machbare Zeitdauer begrenzt. Sie könnten wahrscheinlich eine bessere Leistung erzielen, indem Sie eine Tagestabelle oder eine Kalendertabelle mit geeigneter Indizierung für eine optimierte Wiedergabe jedes Tages erstellen.
Sobald die Bereiche aufgeteilt sind, werden die Beschreibungen mit XML PATH zusammengeführt, sodass für jeden Tag in der Bereichsserie alle Beschreibungen aufgelistet sind. Die Zeilennummerierung nach Personen-ID und Datum ermöglicht es, die erste und letzte Zeile jedes Bereichs zu finden, indem zwei NOT EXISTS-Prüfungen verwendet werden, um Fälle zu finden, in denen eine vorherige Zeile für einen übereinstimmenden Personen-ID- und Beschreibungssatz nicht vorhanden ist, oder wo die nächste Zeile vorhanden ist. t existiert für einen übereinstimmenden Personen-ID- und Beschreibungssatz.
Diese Ergebnismenge wird dann mit ROW_NUMBER neu nummeriert, sodass sie zum Erstellen der endgültigen Ergebnisse gekoppelt werden können.
/*
SET DATEFORMAT dmy
USE tempdb;
GO
CREATE TABLE Schedule
( PersonID int,
Surname nvarchar(30),
FirstName nvarchar(30),
Description nvarchar(100),
StartDate datetime,
EndDate datetime)
GO
INSERT INTO Schedule VALUES (18, 'Smith', 'John', 'Poker Club', '01/01/2009', NULL)
INSERT INTO Schedule VALUES (18, 'Smith', 'John', 'Library', '05/01/2009', '18/01/2009')
INSERT INTO Schedule VALUES (18, 'Smith', 'John', 'Gym', '10/01/2009', '28/01/2009')
INSERT INTO Schedule VALUES (26, 'Adams', 'Jane', 'Pilates', '03/01/2009', '16/02/2009')
GO
*/
SELECT
PersonID,
Description,
theDate
INTO #SplitRanges
FROM Schedule, (SELECT DATEADD(dd, number, '01/01/2008') AS theDate
FROM master..spt_values
WHERE type = N'P') AS DayTab
WHERE theDate >= StartDate
AND theDate <= isnull(EndDate, '31/12/2012')
SELECT
ROW_NUMBER() OVER (ORDER BY PersonID, theDate) AS rowid,
PersonID,
theDate,
STUFF((
SELECT '/' + Description
FROM #SplitRanges AS s
WHERE s.PersonID = sr.PersonID
AND s.theDate = sr.theDate
FOR XML PATH('')
), 1, 1,'') AS Descriptions
INTO #MergedDescriptions
FROM #SplitRanges AS sr
GROUP BY PersonID, theDate
SELECT
ROW_NUMBER() OVER (ORDER BY PersonID, theDate) AS ID,
*
INTO #InterimResults
FROM
(
SELECT *
FROM #MergedDescriptions AS t1
WHERE NOT EXISTS
(SELECT 1
FROM #MergedDescriptions AS t2
WHERE t1.PersonID = t2.PersonID
AND t1.RowID - 1 = t2.RowID
AND t1.Descriptions = t2.Descriptions)
UNION ALL
SELECT *
FROM #MergedDescriptions AS t1
WHERE NOT EXISTS
(SELECT 1
FROM #MergedDescriptions AS t2
WHERE t1.PersonID = t2.PersonID
AND t1.RowID = t2.RowID - 1
AND t1.Descriptions = t2.Descriptions)
) AS t
SELECT DISTINCT
PersonID,
Surname,
FirstName
INTO #DistinctPerson
FROM Schedule
SELECT
t1.PersonID,
dp.Surname,
dp.FirstName,
t1.Descriptions,
t1.theDate AS StartDate,
CASE
WHEN t2.theDate = '31/12/2012' THEN NULL
ELSE t2.theDate
END AS EndDate
FROM #DistinctPerson AS dp
JOIN #InterimResults AS t1
ON t1.PersonID = dp.PersonID
JOIN #InterimResults AS t2
ON t2.PersonID = t1.PersonID
AND t1.ID + 1 = t2.ID
AND t1.Descriptions = t2.Descriptions
DROP TABLE #SplitRanges
DROP TABLE #MergedDescriptions
DROP TABLE #DistinctPerson
DROP TABLE #InterimResults
/*
DROP TABLE Schedule
*/
Die obige Lösung behandelt auch Lücken zwischen zusätzlichen Beschreibungen. Wenn Sie also eine weitere Beschreibung für PersonID 18 hinzufügen und eine Lücke hinterlassen:
INSERT INTO Schedule VALUES (18, 'Smith', 'John', 'Gym', '10/02/2009', '28/02/2009')
Es wird die Lücke angemessen füllen. Wie in den Kommentaren erwähnt, sollten Sie in dieser Tabelle keine Namensinformationen haben, sie sollten auf eine Personentabelle normalisiert werden, mit der im Endergebnis JOIN verbunden werden kann. Ich habe diese andere Tabelle simuliert, indem ich ein SELECT DISTINCT verwendet habe, um eine temporäre Tabelle zu erstellen, um diesen JOIN zu erstellen.