使用 T-SQL 根据先前行的值逐行更新表

Posted

技术标签:

【中文标题】使用 T-SQL 根据先前行的值逐行更新表【英文标题】:Using T-SQL to update a table row by row based on previous rows value 【发布时间】:2019-11-13 09:50:15 【问题描述】:

我有一个表,我正在尝试根据当前开始日期和前几行结束日期之间的日期差异创建一个 ID 字段

表格看起来像这样:

Table ID     |     Person ID    |     Start Date    |     End Date   
   1         |        1         |     01/01/2019    |    03/01/2019         
   2         |        1         |     03/01/2019    |    05/01/2019         
   3         |        1         |     07/01/2019    |    10/01/2019         
   4         |        1         |     10/01/2019    |    16/01/2019         
   4         |        1         |     16/01/2019    |    16/01/2019         
   5         |        1         |     18/01/2019    |    20/01/2019     

我想要它做的是将当前行的开始日期与前一行的结束日期进行比较,如果差值大于 1,那么我们希望将 New ID 字段增加 1。如果它小于 1我们希望保持这个新字段与上一行相同(参见下面的示例)

Table ID     |     Person ID   |      Start Date    |     End Date     |  NEW ID Field
   1         |        1        |      01/01/2019    |    03/01/2019    |     1
   2         |        1        |      03/01/2019    |    05/01/2019    |     1
   3         |        1        |      06/01/2019    |    10/01/2019    |     2
   4         |        1        |      10/01/2019    |    16/01/2019    |     2
   4         |        1        |      16/01/2019    |    16/01/2019    |     2
   5         |        1        |      18/01/2019    |    20/01/2019    |     3

我目前必须这样做的代码一次更新整个表,而不是逐行更新,我想尽可能避免使用游标,因为这是一个相当大的表,会减慢我们的进程相当大的一块。

我现在的代码是:

DECLARE @rown INT
DECLARE @MAX INT
DECLARE @Val INT
SET @rown = 1
SET @max = (select count(*) from dbo.table)
SET @Val = 1


WHILE @rown <> @max


Update dbo.table
  SET New_id_field = 
  (select Case WHEN LT_Flag = 0 then NULL
        WHEN 
        DateDiffFromPrev *-1 >1 
        THEN @Val + 1
        ELSE @Val
        END)

 FROM dbo.table t1
 INNER JOIN
 (
      SELECT 
      table_ID
      ,person_ID
      ,start_date
      ,end_date
      ,LT_Flag
      ,ROW_NUMBER() OVER (PARTITION BY person_id ORDER BY start_date) AS rownumber 
      ,DATEDIFF(day,  start_date, coalesce(lag(end_date) over (partition by Person_ID, LT_Flag order by start_date), start_date)) as DateDiffFromPrev  
FROM ) t2  
     ON t1.table_id = t2.table_id

SET @rown = @rown + 1

END 

对帖子中的任何格式表示歉意,因为我是堆栈溢出的新手! 非常感谢提前

【问题讨论】:

对于描述 " 如果差值大于 1,那么我们希望将 New ID 字段增加 1。如果它小于 1" 那么当差是 1? 【参考方案1】:

WHILE 是一个非常糟糕的解决方案。使用分析函数LAG 然后使用窗口聚合会好得多。请注意,当差异为 1 天时(根据我的评论),我不知道逻辑是什么,因此您必须更正:

WITH Prev AS(
    SELECT V.[Table ID],
           V.[Person ID],
           V.[Start Date],
           V.[End Date],
           LAG(V.[End Date],1,V.[End Date]) OVER (PARTITION BY V.[Person ID] ORDER BY V.[Start Date]) AS PrevEndDate --Not ordered on [Table ID] as it is not unique in the OP's data.
    FROM (VALUES(1,1,CONVERT(date,'01/01/2019',103),CONVERT(date,'03/01/2019',103)),         
                (2,1,CONVERT(date,'03/01/2019',103),CONVERT(date,'05/01/2019',103)),         
                (3,1,CONVERT(date,'07/01/2019',103),CONVERT(date,'10/01/2019',103)),         
                (4,1,CONVERT(date,'10/01/2019',103),CONVERT(date,'16/01/2019',103)),         
                (4,1,CONVERT(date,'16/01/2019',103),CONVERT(date,'16/01/2019',103)),         
                (5,1,CONVERT(date,'18/01/2019',103),CONVERT(date,'20/01/2019',103)))V([Table ID],[Person ID],[Start Date],[End Date]))
SELECT P.[Table ID],
       P.[Person ID],
       P.[Start Date],
       P.[End Date],
       SUM(CASE WHEN DATEDIFF(DAY,P.PrevEndDate,P.[Start Date]) < 1 THEN 0 
                WHEN DATEDIFF(DAY,P.PrevEndDate,P.[Start Date]) > 1 THEN 1
                --WHEN DATEDIFF(DAY,P.PrevEndDate,P.[Start Date]) = 1 THEN ???
           END) OVER (PARTITION BY P.[Person ID] ORDER BY P.[Start Date]
                      ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) +1 AS [New ID]
FROM Prev P;

【讨论】:

哇,这太棒了,像梦一样工作,非常感谢!!!

以上是关于使用 T-SQL 根据先前行的值逐行更新表的主要内容,如果未能解决你的问题,请参考以下文章

根据同一行中的行值逐行更新列值

根据组合框中的是/否值逐行更新表格列

根据给定日期的条件及其先前行操作列检索最新行

更新查询 - 前行 x 和当前行 y 的总和

如何根据字典中的键值逐行向熊猫数据框添加值?

mysql过程在更新前行时更新前行中的数字引用