【问题标题】:tsql list of dates to date rangestsql 日期到日期范围的列表
【发布时间】:2013-07-10 03:46:27
【问题描述】:

我正在尝试从 sql-2008 的项目列表和日期生成项目、日期范围摘要。

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

我只是想不通如何做到这一点......

..也许这适用于 Segments 列:

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

任何帮助或指导将不胜感激。

【问题讨论】:

  • 尝试查找有关“差距和岛屿”的信息。
  • 如何区分结果中的第 1 行和第 2 行,因为 id 相同?
  • 我在 5 月发表了一篇关于这些东西的博文。这篇文章是瑞典文的:blogs.solidq.com/SQL-Server-pa-svenska/…。我通过谷歌翻译运行它,结果很好(警告词,英语和瑞典语中的重叠词可能已在代码中翻译)。在这里查看:translate.google.com/….

标签: tsql pivot-table common-table-expression group-by


【解决方案1】:

获取日期范围是一个古老的差距和岛屿问题。 像您描述的那样使用 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

【讨论】:

    【解决方案2】:

    下面的代码是您的示例数据的完整工作示例,这里有一些有用的链接:

    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
    

    请注意,上面的代码可以放入视图或函数中,或者您可以“剪切”它并将每个 CTE 的结果保存在临时表或表变量表中。

    【讨论】:

      猜你喜欢
      • 2020-06-12
      • 1970-01-01
      • 2013-03-02
      • 1970-01-01
      • 2014-01-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多