【问题标题】:Sequential grouping of start and end date ranges开始和结束日期范围的顺序分组
【发布时间】:2014-05-05 22:55:21
【问题描述】:

我有一个我无法弄清楚的问题。我创建了一个视图,当它运行时,它会为我提供旅行工人的安置数据。重要的部分是PlacementIDStartDateEndDate

TravelerID  FirstName   LastName    PlacementID StartDate   EndDate
--------------------------------------------------------------------
65648       Lori        Williams    106593      09/22/01    02/08/03
65648       Lori        Williams    392605      02/24/03    05/24/03
65648       Lori        Williams    477950      05/26/03    11/22/03
65648       Lori        Williams    600089      12/01/03    05/29/04
65648       Lori        Williams    717424      05/30/04    12/04/04
65648       Lori        Williams    832842      12/05/04    02/04/05
65648       Lori        Williams    867492      02/06/05    07/30/05
65648       Lori        Williams    979375      08/15/05    11/12/05
65648       Lori        Williams    1030555     11/14/05    05/13/06
65648       Lori        Williams    1155937     05/15/06    01/06/07
65648       Lori        Williams    1341007     01/07/07    01/05/08
65648       Lori        Williams    1709959     01/06/08    05/31/08
65648       Lori        Williams    1878735     06/01/08    07/19/08
65648       Lori        Williams    1937168     07/20/08    01/31/09

他们希望将连续的展示位置计为一个,并使用第一个展示位置的PlacementID。例如,请注意PlacementIDs 600089,717424 和 832842。请注意,下一个的开始日期是前一个结束日期的一天后。在列表的下方还有其他连续的展示位置。所以期望的输出是:

TravelerID  FirstName   LastName    PlacementID StartDate   EndDate
--------------------------------------------------------------------
65648       Lori        Williams    106593      09/22/01    02/08/03
65648       Lori        Williams    392605      02/24/03    05/24/03
65648       Lori        Williams    477950      05/26/03    11/22/03
65648       Lori        Williams    600089      12/01/03    02/04/05
65648       Lori        Williams    867492      02/06/05    07/30/05
65648       Lori        Williams    979375      08/15/05    11/12/05
65648       Lori        Williams    1030555     11/14/05    05/13/06
65648       Lori        Williams    1155937     05/15/06    01/31/09

下面是一些生成示例数据的代码:

CREATE TABLE [dbo].vw_PlacementData(
 TravelerID int
,FirstName varchar(255)
,LastName varchar(255)
,PlacementID int
,StartDate datetime
,EndDate datetime
) ON [PRIMARY]

INSERT INTO vw_PlacementData VALUES (65648,'Lori','Williams',106593,'9/22/01','2/8/03')
INSERT INTO vw_PlacementData VALUES (65648,'Lori','Williams',392605,'2/24/03','5/24/03')
INSERT INTO vw_PlacementData VALUES (65648,'Lori','Williams',477950,'5/26/03','11/22/03')
INSERT INTO vw_PlacementData VALUES (65648,'Lori','Williams',600089,'12/1/03','5/29/04')
INSERT INTO vw_PlacementData VALUES (65648,'Lori','Williams',717424,'5/30/04','12/4/04')
INSERT INTO vw_PlacementData VALUES (65648,'Lori','Williams',832842,'12/5/04','2/4/05')
INSERT INTO vw_PlacementData VALUES (65648,'Lori','Williams',867492,'2/6/05','7/30/05')
INSERT INTO vw_PlacementData VALUES (65648,'Lori','Williams',979375,'8/15/05','11/12/05')
INSERT INTO vw_PlacementData VALUES (65648,'Lori','Williams',1030555,'11/14/05','5/13/06')
INSERT INTO vw_PlacementData VALUES (65648,'Lori','Williams',1155937,'5/15/06','1/6/07')
INSERT INTO vw_PlacementData VALUES (65648,'Lori','Williams',1341007,'1/7/07','1/5/08')
INSERT INTO vw_PlacementData VALUES (65648,'Lori','Williams',1709959,'1/6/08','5/31/08')
INSERT INTO vw_PlacementData VALUES (65648,'Lori','Williams',1878735,'6/1/08','7/19/08')
INSERT INTO vw_PlacementData VALUES (65648,'Lori','Williams',1937168,'7/20/08','1/31/09')

` 我试图将表格加入到自身中,但它并没有考虑超过一个连续的位置。从示例中可以看出,可能需要计算 2 或 20 个连续的展示位置。

非常感谢任何帮助!!!

【问题讨论】:

  • 这似乎是overlapping date range 问题的变体(因为它是第二天而不是实际重叠)。这些解决方案中的任何一个对您有用吗,或者在类似的问题上有用吗?如果他们不这样做,您尝试过什么不起作用?
  • 谢谢!我现在正在查看链接,看看该解决方案是否适合我。
  • 此外,为此返回原始数据而不是视图可能会很方便。特别是因为该 CTE 的使用似乎正在超时......

标签: sql sql-server sql-server-2005 grouping sequential


【解决方案1】:

有人在 msdn 论坛上提供了一个方便的解决方案。语法如下:

SELECT *
    ,CAST(NULL AS datetime) AS FinalStartDate
    ,CAST(NULL AS datetime) AS FinalEndDate 
INTO #temp
FROM vw_PlacementData

CREATE CLUSTERED INDEX IDX_Clust_temp ON #temp (TravelerID,PlacementID,StartDate,EndDate)

DECLARE 
     @TravelerID int
    ,@PlacementID int
    ,@StartDate datetime
    ,@EndDate datetime

SELECT TOP 1 
     @TravelerID =TravelerID 
    ,@PlacementID = PlacementID
    ,@StartDate  = StartDate
    ,@EndDate = EndDate
FROM vw_PlacementData
ORDER BY TravelerID,StartDate,EndDate

--SELECT TOP 1 @TravelerID,@PlacementID  ,@StartDate  ,@EndDate

UPDATE t
SET @StartDate = FinalStartDate = CASE 
                                    WHEN TravelerID = @TravelerID 
                                    AND PlacementID > @PlacementID 
                                    AND  StartDate = @EndDate + 1
                                  THEN @StartDate
                                  ELSE StartDate
                             END,
    @TravelerID =TravelerID
   ,@PlacementID = PlacementID 
 --,@StartDate  = FinalStartDate
   ,@EndDate = EndDate
 -- OUTPUT INSERTED.StartDate,INSERTED.EndDate
 FROM #temp t (TABLOCKX)
 OPTION (MAXDOP 1)

SELECT 
     TravelerID
    ,FirstName
    ,LastName
    ,MIN(PlacementID) AS PlacementID
    ,FinalStartDate AS StartDate
    ,MAX(EndDate) AS EndDate
FROM #temp
GROUP BY TravelerID,FirstName,LastName,FinalStartDate

DROP TABLE  #Temp

【讨论】:

    【解决方案2】:

    您可以使用递归 CTE 根据开始/结束日期将展示位置段连接在一起。

    ;WITH cteConsecutivePlacements AS(
        SELECT
             TravelerID
            ,FirstName
            ,LastName
            ,PlacementID
            ,StartDate
            ,EndDate
        FROM dbo.vw_PlacementData
    
        UNION ALL
    
        SELECT
             cte.TravelerID
            ,cte.FirstName
            ,cte.LastName
            ,cte.PlacementID
            ,cte.StartDate
            ,vpd.EndDate
        FROM cteConsecutivePlacements cte
        JOIN dbo.vw_PlacementData vpd
            ON cte.TravelerID = vpd.TravelerID
        WHERE DATEADD(DAY,1,cte.EndDate) = vpd.StartDate
    )
    

    内部 select 语句使用 ROW_NUMBER 函数来标识后续放置,外部 select 语句中的聚合将结果减少到每个连续放置 1 行,并具有正确的结束日期。

    SELECT
         TravelerID
        ,FirstName
        ,LastName
        ,PlacementID
        ,StartDate
        ,MAX(EndDate) AS EndDate
    FROM( 
        SELECT 
             TravelerID
            ,FirstName
            ,LastName
            ,PlacementID
            ,StartDate
            ,EndDate
            ,ROW_NUMBER() OVER(PARTITION BY TravelerID, FirstName, LastName, EndDate ORDER BY StartDate ASC) AS row_num
        FROM cteConsecutivePlacements   
    )q
    WHERE q.row_num = 1
    GROUP BY TravelerID, FirstName, LastName, PlacementID, StartDate
    ORDER BY PlacementID, EndDate
    

    【讨论】:

    • 非常感谢您的回复!完全披露的是,我所说的是这里的一个表(vw_PlacementData)实际上是一个给我结果的视图。它有大约 167,000 行,没有重复。当我在上面运行您的语法时,我收到“100 的最大递归已用尽”错误,因此我将其设置为 MAXRECURSION 0。运行时间非常长。我在 5 分钟后停止了它。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多