이 문제는 데이터 세트가 커질수록 TSQL로 해결할 수있는 솔루션이 확장되지 않는다는 점에서 문제가 있습니다. 아래에서는 문제를 해결하기 위해 임시로 작성된 일련의 임시 테이블을 사용합니다. 숫자 테이블을 사용하여 각 날짜 범위 항목을 각각의 요일로 나눕니다. 여기서 확장되지 않는 이유는 무한대로 보이는 열린 원거리 NULL 값 때문입니다. 따라서 변환 범위를 실현 가능한 기간으로 제한하는 미래의 고정 날짜로 스와핑해야합니다. 매일의 최적화 된 렌더링을 위해 적절한 인덱싱을 사용하여 일별 테이블 또는 달력 테이블을 구성함으로써 성능을 향상시킬 수 있습니다.
범위가 분할되면 XML PATH를 사용하여 설명이 병합되므로 범위 시리즈의 각 요일에 나열된 모든 설명이 나열됩니다. PersonID 및 Date에 의한 행 번호 매기기는 두 개의 NOT EXISTS 검사를 사용하여 일치하는 PersonID 및 설명 집합에 대해 이전 행이 존재하지 않는 인스턴스를 찾거나 다음 행이없는 경우를 찾는 각 범위의 첫 번째 행과 마지막 행을 허용합니다. 일치하는 PersonID W 설명 세트에 대해 존재하지 않습니다.
그런 다음이 결과 집합은 ROW_NUMBER를 사용하여 번호가 매겨 지므로 최종 결과를 빌드 할 수 있습니다.
/*
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
*/
위의 솔루션은 또한뿐만 아니라 추가 설명 사이의 격차를 처리합니다, 그래서 당신이 인 경우에 간격을두고 PersonID (18)에 대한 또 다른 설명을 추가 :
INSERT INTO Schedule VALUES (18, 'Smith', 'John', 'Gym', '10/02/2009', '28/02/2009')
그것은 적절 공백을 채울 것입니다. 주석에서 지적했듯이이 테이블에 이름 정보가 있으면 안되며 최종 결과에 JOIN 할 수있는 사람 테이블로 정규화되어야합니다. SELECT DISTINCT를 사용하여 JOIN을 생성하는 임시 테이블을 작성하여이 다른 테이블을 시뮬레이션했습니다.
귀하의 디자인은 동일한 이름이나 성을 가진 여러 멤버를 어떻게 처리합니까? 당신이 제공 한 샘플 데이터가 존 스미스 (John Smith)라는 세 명의 다른 사람들을 가리킬 가능성이 있습니다. –
이것이 유효한 이유입니다. 그 중 제가이 가능성을 반영하도록 제 질문을 수정했습니다. 나는 실제로 각 사람의 ID를 저장하고 있지만, 중복 이름을 생각하고 있지는 않은 질문을 썼을 때. 의견에 대한 환호. – user168369
PersonID가 있습니다 - 최종 출력까지 이름 비트를 완전히 무시합니다. 선택 – MartW