2013-07-10 5 views
2

Я пытаюсь сгенерировать элемент, сводку диапазона дат из списка элементов, даты (ей) для sql-2008.tsql список дат к датам диапазонов

DECLARE @RoomDays AS TABLE (
    [RoomID] [int] NOT NULL, 
    [DateOf] [date] NOT NULL, 
    [Segment] [char](1) NOT NULL 
) 

INSERT INTO @RoomDays 
VALUES 
('1','2013-07-03','1'), 
('1','2013-07-04','1'), 
('1','2013-07-05','6'), 
('1','2013-07-15','6'), 
('1','2013-07-16','6'), 
('2','2013-07-08','1'), 
('2','2013-07-09','1'), 
('2','2013-07-10','6'), 
('2','2013-07-11','6'), 
('2','2013-07-12','1'), 
('2','2013-07-13','1'), 
('3','2013-07-19','6') 

Результаты, которые я пытаюсь получить:

RoomID DateFrom DateThru Segments  NumDays 
1  2013-07-03 2013-07-05 1,1,6  3 
1  2013-07-15 2013-07-16 6,6   2 
2  2013-07-08 2013-07-13 1,1,6,6,1,1 6 
3  2013-07-19 2013-07-19 6   1 

Я просто не могу получить мою голову вокруг, как это сделать ...

..maybe это для столбца Сегменты:

( 
SELECT CAST(ltrim(rtrim(SegmentID)) + ', ' AS VARCHAR(MAX)) 
from @RoomDays where roomid = rd.roomid 
FOR XML PATH ('') 
)+' ' as Segments, 
-- 

Любая помощь или руководство будут оценены.

+0

Попытайтесь найти информацию о «Gaps and Islands». – GilM

+0

Как отличить 1-ю и 2-ю строки от результатов, так как id одинаковый? – Tomasito

+0

Я сделал blogpost в мае об этом. Статья находится на шведском языке: http://blogs.solidq.com/SQL-Server-pa-svenska/Post.aspx?ID=24&title=Hur+konkatenering+med+FOR+XML+PATH+fungerar. Я запустил его через google translate, и получилось ok-ish (слово предупреждения, перекрывающиеся слова на английском и шведском языках, возможно, были переведены внутри кода). Проверьте это здесь: http://translate.google.com/translate?sl=sv&tl=ru&js=n&prev=_t&hl=ru&ie=UTF-8&u=http%3A%2F%2Fblogs.solidq.com%2FSQL-Server-pa -svenska% 2FPost.aspx% 3FID% 3D24% 26title% 3DHur% 2Bkonkatenering% 2Bmed% 2BFOR% 2BXML% 2BPATH% 2Bfungerar. –

ответ

0

Получение диапазонов дат - проблема старых проблем и островов. Конкатенация ваших сегментов выполняется с помощью трюка XML, как вы описали.

Вот полное решение с SQL скрипкой: http://sqlfiddle.com/#!3/d4a961/11

Использование кода в вопросе и табличную переменную код должен выглядеть следующим образом:

WITH groupedDays AS (
SELECT 
RoomID 
,DateOf 
,Segment 
,DATEADD(day,-row_number() OVER (partition by RoomID order by DateOf),DateOf) AS Island 
FROM @RoomDays 
) 

SELECT 
outerTable.RoomID 
,MIN(outerTable.DateOf) AS DateFrom 
,MAX(outerTable.DateOf) AS DateThru 
,STUFF((
    SELECT ', ' + innerTable.Segment 
    FROM groupedDays AS innerTable 
    WHERE innerTable.RoomID = outerTable.RoomID 
    AND innerTable.Island = outerTable.Island 
    ORDER BY innerTable.DateOf 
    FOR XML PATH('') 
),1,1,'') 
,COUNT(*) AS NumDays 

FROM groupedDays AS outerTable 

GROUP BY 
outerTable.RoomID 
,outerTable.Island 

ORDER BY 
outerTable.RoomID 
,outerTable.Island 
0

Код ниже полный рабочий пример с вашими данными выборки и здесь некоторые полезные ссылки:

T-SQL CSV from column values

Recursive CTE

SET NOCOUNT ON 
GO 

    DECLARE @RoomDays TABLE 
    (
     [RoomID] INT NOT NULL 
     ,[DateOf] DATE NOT NULL 
     ,[Segment] CHAR(1) NOT NULL 
    ) 

    INSERT INTO @RoomDays ([RoomID], [DateOf], [Segment]) 
    VALUES ('1','2013-07-03','1') 
      ,('1','2013-07-04','1') 
      ,('1','2013-07-05','6') 
      ,('1','2013-07-15','6') 
      ,('1','2013-07-16','6') 
      ,('2','2013-07-08','1') 
      ,('2','2013-07-09','1') 
      ,('2','2013-07-10','6') 
      ,('2','2013-07-11','6') 
      ,('2','2013-07-12','1') 
      ,('2','2013-07-13','1') 
      ,('3','2013-07-19','6') 

    ;WITH DataSource ([RoomID], [DateOf], [Segment], [Level]) AS 
    (
     SELECT [RoomID], [DateOf], [Segment], 0 AS [Level] 
     FROM @RoomDays AS RD 
     UNION ALL 
     SELECT RD1.[RoomID], RD1.[DateOf], RD1.[Segment], [Level] + 1 
     FROM @RoomDays AS RD1 
     INNER JOIN DataSource DS 
      ON RD1.RoomID = DS.RoomID 
     WHERE DATEADD (DAY , 1 , DS.DateOf) = RD1.DateOf 
    ), 
    StartDates AS 
    (
     -- Retriving the "Start" records 
     SELECT [RoomID], [DateOf], [Segment] 
     FROM DataSource 
     WHERE [Level] = 0 
     EXCEPT 
     SELECT [RoomID], [DateOf], [Segment] 
     FROM DataSource 
     WHERE [Level] = 1 
    ), 
    TempResult AS 
    (
     -- Mathing each "Start" record with its values 
     SELECT SD.[DateOf] AS [StartDate] 
       ,RD.[RoomId] 
       ,RD.[DateOf] 
       ,RD.[Segment] 
     FROM StartDates SD 
     INNER JOIN @RoomDays RD 
      ON SD.RoomID = RD.RoomID 
      AND SD.DateOf <= RD.DateOf  
      AND NOT EXISTS (SELECT 1 FROM StartDates WHERE DateOf <= RD.DateOf AND RoomID = RD.RoomID AND SD.DateOf < DateOf) 
    ) 
    SELECT TR.[RoomID] AS [RoomID] 
      ,MIN(TR.[DateOf]) AS [DateFrom] 
      ,MAX(TR.[DateOf]) AS [DateThru] 
      ,SUBSTRING((SELECT ',' + [Segment] FROM TempResult WHERE [StartDate] = TR.[StartDate] FOR XML PATH('')),2,4000) AS CSV 
      ,COUNT(TR.[DateOf]) AS [NumDays] 
    FROM TempResult TR 
    GROUP BY TR.[RoomID],TR.[StartDate] 

SET NOCOUNT OFF 
GO 

ВЫВОД:

1 2013-07-03 2013-07-05 1,1,6 3 
1 2013-07-15 2013-07-16 6,6 2 
2 2013-07-08 2013-07-13 1,1,6,6,1,1 6 
3 2013-07-19 2013-07-19 6 1 

Пожалуйста, обратите внимание, что приведенный выше код может быть введен в поле зрения или функцию, или вы можете «вырезать» и сохранить результаты от каждого КТРА при темпе или таблицах переменные таблицы.