sql – 对具有日期范围的行进行分组

前端之家收集整理的这篇文章主要介绍了sql – 对具有日期范围的行进行分组前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在使用sql Server 2008,需要创建一个显示日期范围内的行的查询.

我的表格如下:

  1. ADM_ID WH_PID WH_IN_DATETIME WH_OUT_DATETIME

我的规则是:

>如果WH_OUT_DATETIME在具有相同WH_P_ID的另一个ADM_ID的WH_IN_DATETIME的24小时内或之后24小时内

我想在结果中添加另一列,如果可能的话,将列表值标识为EP_ID.

例如

  1. ADM_ID WH_PID WH_IN_DATETIME WH_OUT_DATETIME
  2. ------ ------ -------------- ---------------
  3. 1 9 2014-10-12 00:00:00 2014-10-13 15:00:00
  4. 2 9 2014-10-14 14:00:00 2014-10-15 15:00:00
  5. 3 9 2014-10-16 14:00:00 2014-10-17 15:00:00
  6. 4 9 2014-11-20 00:00:00 2014-11-21 00:00:00
  7. 5 5 2014-10-17 00:00:00 2014-10-18 00:00:00

将返回行:

  1. ADM_ID WH_PID EP_ID EP_IN_DATETIME EP_OUT_DATETIME WH_IN_DATETIME WH_OUT_DATETIME
  2. ------ ------ ----- ------------------- ------------------- ------------------- -------------------
  3. 1 9 1 2014-10-12 00:00:00 2014-10-17 15:00:00 2014-10-12 00:00:00 2014-10-13 15:00:00
  4. 2 9 1 2014-10-12 00:00:00 2014-10-17 15:00:00 2014-10-14 14:00:00 2014-10-15 15:00:00
  5. 3 9 1 2014-10-12 00:00:00 2014-10-17 15:00:00 2014-10-16 14:00:00 2014-10-17 15:00:00
  6. 4 9 2 2014-11-20 00:00:00 2014-11-20 00:00:00 2014-10-16 14:00:00 2014-11-21 00:00:00
  7. 5 5 1 2014-10-17 00:00:00 2014-10-18 00:00:00 2014-10-17 00:00:00 2014-10-18 00:00:00

EP_OUT_DATETIME将始终是组中的最新日期.希望这有点澄清.
通过这种方式,我可以按EP_ID进行分组,找到EP_OUT_DATETIME以及任何属于其中的ADM_ID / PID的开始时间.

每个都应该滚动到下一个,这意味着如果另一行的WH_IN_DATETIME在另一行的WH_OUT_DATETIME之后对于相同的WH_PID,则该行的WH_OUT_DATETIME变为该EP_ID内所有WH_PID的EP_OUT_DATETIME.

我希望这是有道理的.

谢谢,
先生

解决方法

由于问题没有指明解决方案是“单一”查询;-),这是另一种方法:使用dealy的“quirky update”功能,即在更新列的同时更新变量.打破这个操作的复杂性,我创建了一个临时表来保存最难计算的部分:EP_ID.完成后,它将加入一个简单的查询,并提供用于计算EP_IN_DATETIME和EP_OUT_DATETIME字段的窗口.

步骤是:

>创建临时表
>使用所有ADM_ID值对临时表进行种子处理 – 这使我们可以执行更新,因为所有行都已存在.
>更新临时表
>最后,简单的选择将临时表连接到主表

测试设置

  1. SET ANSI_NULLS ON;
  2. SET NOCOUNT ON;
  3.  
  4. CREATE TABLE #Table
  5. (
  6. ADM_ID INT NOT NULL PRIMARY KEY,WH_PID INT NOT NULL,WH_IN_DATETIME DATETIME NOT NULL,WH_OUT_DATETIME DATETIME NOT NULL
  7. );
  8.  
  9. INSERT INTO #Table VALUES (1,9,'2014-10-12 00:00:00','2014-10-13 15:00:00');
  10. INSERT INTO #Table VALUES (2,'2014-10-14 14:00:00','2014-10-15 15:00:00');
  11. INSERT INTO #Table VALUES (3,'2014-10-16 14:00:00','2014-10-17 15:00:00');
  12. INSERT INTO #Table VALUES (4,'2014-11-20 00:00:00','2014-11-21 00:00:00');
  13. INSERT INTO #Table VALUES (5,5,'2014-10-17 00:00:00','2014-10-18 00:00:00');

第1步:创建并填充临时表

  1. CREATE TABLE #Scratch
  2. (
  3. ADM_ID INT NOT NULL PRIMARY KEY,EP_ID INT NOT NULL
  4. -- Might need WH_PID and WH_IN_DATETIME fields to guarantee proper UPDATE ordering
  5. );
  6.  
  7. INSERT INTO #Scratch (ADM_ID,EP_ID)
  8. SELECT ADM_ID,0
  9. FROM #Table;

备用临时表结构以确保正确的更新顺序(因为“古怪更新”使用聚集索引的顺序,如本答案底部所述):

  1. CREATE TABLE #Scratch
  2. (
  3. WH_PID INT NOT NULL,ADM_ID INT NOT NULL,EP_ID INT NOT NULL
  4. );
  5.  
  6. INSERT INTO #Scratch (WH_PID,WH_IN_DATETIME,ADM_ID,EP_ID)
  7. SELECT WH_PID,0
  8. FROM #Table;
  9.  
  10. CREATE UNIQUE CLUSTERED INDEX [CIX_Scratch]
  11. ON #Scratch (WH_PID,ADM_ID);

步骤2:使用局部变量更新Scratch Table以跟踪先前值

  1. DECLARE @EP_ID INT; -- this is used in the UPDATE
  2.  
  3. ;WITH cte AS
  4. (
  5. SELECT TOP (100) PERCENT
  6. t1.*,t2.WH_OUT_DATETIME AS [PriorOut],t2.ADM_ID AS [PriorID],ROW_NUMBER() OVER (PARTITION BY t1.WH_PID ORDER BY t1.WH_IN_DATETIME)
  7. AS [RowNum]
  8. FROM #Table t1
  9. LEFT JOIN #Table t2
  10. ON t2.WH_PID = t1.WH_PID
  11. AND t2.ADM_ID <> t1.ADM_ID
  12. AND t2.WH_OUT_DATETIME >= (t1.WH_IN_DATETIME - 1)
  13. AND t2.WH_OUT_DATETIME < t1.WH_IN_DATETIME
  14. ORDER BY t1.WH_PID,t1.WH_IN_DATETIME
  15. )
  16. UPDATE sc
  17. SET @EP_ID = sc.EP_ID = CASE
  18. WHEN cte.RowNum = 1 THEN 1
  19. WHEN cte.[PriorOut] IS NULL THEN (@EP_ID + 1)
  20. ELSE @EP_ID
  21. END
  22. FROM #Scratch sc
  23. INNER JOIN cte
  24. ON cte.ADM_ID = sc.ADM_ID

第3步:选择加入划痕表

  1. SELECT tab.ADM_ID,tab.WH_PID,sc.EP_ID,MIN(tab.WH_IN_DATETIME) OVER (PARTITION BY tab.WH_PID,sc.EP_ID)
  2. AS [EP_IN_DATETIME],MAX(tab.WH_OUT_DATETIME) OVER (PARTITION BY tab.WH_PID,sc.EP_ID)
  3. AS [EP_OUT_DATETIME],tab.WH_IN_DATETIME,tab.WH_OUT_DATETIME
  4. FROM #Table tab
  5. INNER JOIN #Scratch sc
  6. ON sc.ADM_ID = tab.ADM_ID
  7. ORDER BY tab.ADM_ID;

资源

> UPDATE的MSDN页面

寻找“@variable = column = expression”
> Performance Analysis of doing Running Totals(与此不完全相同,但不太远)

这篇博文确实提到:

> PRO:这种方法通常很快> CON:“UPDATE的顺序由聚集索引的顺序控制”.此行为可能会根据具体情况排除使用此方法.但在这种特殊情况下,如果WH_PID值至少不是通过聚簇索引的排序自然地组合在一起并由WH_IN_DATETIME排序,那么这两个字段只会被添加到临时表和PK(带有隐含的聚簇索引)上临时表变为(WH_PID,ADM_ID).

猜你在找的MsSQL相关文章