使用Self Join Sql Server进行更新

时间:2015-10-15 16:00:21

标签: sql sql-server sql-update self-join

我有大量数据,表格样本如下所示

+-----------+------------+-----------+-----------+
| Unique_ID |    Date    | RowNumber | Flag_Date |
+-----------+------------+-----------+-----------+
|         1 | 6/3/2014   |         1 | 6/3/2014  |
|         1 | 5/22/2015  |         2 | NULL      |
|         1 | 6/3/2015   |         3 | NULL      |
|         1 | 11/20/2015 |         4 | NULL      |
|         2 | 2/25/2014  |         1 | 2/25/2014 |
|         2 | 7/31/2014  |         2 | NULL      |
|         2 | 8/26/2014  |         3 | NULL      |
+-----------+------------+-----------+-----------+

现在我需要检查第二行中的Date和第一行中的Flag_date之间的区别。如果差异大于180,那么第二行Flag_date应该使用第二行中的日期进行更新,否则需要通过第一行中的Flag_date进行更新。对于具有相同unique_ID

的所有行,遵循相同的规则
update a
set a.Flag_Date=case when DATEDIFF(dd,b.Flag_Date,a.[Date])>180 then a.[Date] else b.Flag_Date end
from Table1 a
inner join Table1 b
on a.RowNumber=b.RowNumber+1 and a.Unique_ID=b.Unique_ID

上面的更新查询执行一次,只有每个Unique_ID下的第二行得到更新,结果如下所示

+-----------+------------+-----------+------------+
| Unique_ID |    Date    | RowNumber | Flag_Date  |
+-----------+------------+-----------+------------+
|         1 | 2014-06-03 |         1 | 2014-06-03 |
|         1 | 2015-05-22 |         2 | 2015-05-22 |
|         1 | 2015-06-03 |         3 | NULL       |
|         1 | 2015-11-20 |         4 | NULL       |
|         2 | 2014-02-25 |         1 | 2014-02-25 |
|         2 | 2014-07-31 |         2 | 2014-02-25 |
|         2 | 2014-08-26 |         3 | NULL       |
+-----------+------------+-----------+------------+

我需要跑四次才能达到理想的效果

+-----------+------------+-----------+------------+
| Unique_ID |    Date    | RowNumber | Flag_Date  |
+-----------+------------+-----------+------------+
|         1 | 2014-06-03 |         1 | 2014-06-03 |
|         1 | 2015-05-22 |         2 | 2015-05-22 |
|         1 | 2015-06-03 |         3 | 2015-05-22 |
|         1 | 2015-11-20 |         4 | 2015-11-20 |
|         2 | 2014-02-25 |         1 | 2014-02-25 |
|         2 | 2014-07-31 |         2 | 2014-02-25 |
|         2 | 2014-08-26 |         3 | 2014-08-26 |
+-----------+------------+-----------+------------+

有没有办法只能运行一次更新,所有行都会更新。

谢谢!

1 个答案:

答案 0 :(得分:1)

如果您使用的是SQL Server 2012+,则可以使用lag()

with toupdate as (
      select t1.*,
             lag(flag_date) over (partition by unique_id order by rownumber) as prev_flag_date
      from table1 t1
     )
update toupdate
    set Flag_Date = (case when DATEDIFF(day, prev_Flag_Date, toupdate.[Date]) > 180
                          then toupdate.[Date] else prev_Flag_Date
                     end);

此版本和您的版本都可以利用table1(unique_id, rownumber)上的索引,或者更好的是table1(unique_id, rownumber, flag_date)

编辑:

在早期版本中,这可能会有更好的性能:

with toupdate as (
      select t1.*, t2.flag_date as prev_flag_date
      from table1 t1 outer apply
           (select top 1 t2.flag_date
            from table1 t2
            where t2.unique_id = t1.unique_id and
                  t2.rownumber < t1.rownumber
            order by t2.rownumber desc
           ) t2
     )
update toupdate
    set Flag_Date = (case when DATEDIFF(day, prev_Flag_Date, toupdate.[Date]) > 180
                          then toupdate.[Date] else prev_Flag_Date
                     end);

CTE可以使用相同的索引 - 拥有索引很重要。性能更好的原因是因为row_number()上的联接无法在该字段上使用索引。