我有一个SQL Server表,其中一列(object_id)中有重复的条目,例如:
+----+-----------+------------+
| id | object_id | status_val |
+----+-----------+------------+
| 1 | 1 | 0 |
| 2 | 1 | 0 |
| 3 | 1 | 0 |
| 4 | 2 | 0 |
| 5 | 3 | 0 |
| 6 | 3 | 0 |
+----+-----------+------------+
Run Code Online (Sandbox Code Playgroud)
我需要更新他们的所有状态,除了object_id列中有重复的状态.所以在上表中,object_id1和3是重复的.所以我想将它们更改status_val为2,除了其中一个条目.结果如下:
| id | object_id | status_val |
+----+-----------+------------+
| 1 | 1 | 0 |
| 2 | 1 | 2 |
| 3 | 1 | 2 |
| 4 | 2 | 0 |
| 5 | 3 | 0 |
| 6 | 3 | 2 |
+----+-----------+------------+
Run Code Online (Sandbox Code Playgroud)
哪个重复行的状态更新无关紧要.
任何帮助,将不胜感激.
您可以在没有连接的情况下解决此问题,这意味着它应该具有更好的性能.我们的想法是通过object_id对数据进行分组,计算每个object_id的行号.这就是"分区依据"的作用.然后你可以更新row_num> 1的位置.这将更新除第一个之外的所有重复的object_id!
update t set t.status_val = 'some_status'
from (
select *, row_number() over(partition by object_id order by (select null)) row_num
from foo
) t
where row_num > 1
Run Code Online (Sandbox Code Playgroud)
在82944条记录的测试表上,表现如此(您的里程可能会有所不同!):表'测试'.扫描计数5,逻辑读取82283,物理读取0,预读取读取0,lob逻辑读取0,lob物理读取0,lob预读取读取0. CPU时间= 141 ms,经过时间= 150 ms.
我们当然也可以通过使用内部连接来解决这个问题,但是,通常这会导致更多的逻辑读取和更高的CPU:
表'测试'.扫描计数10,逻辑读取83622,物理读取0,预读读取0,lob逻辑读取0,lob物理读取0,lob预读读取0.表'Workfile'.扫描计数0,逻辑读取0,物理读取0,预读取读取0,lob逻辑读取0,lob物理读取0,lob预读读取0.表'工作表'.扫描计数4,逻辑读取167426,物理读取0,预读取读取0,lob逻辑读取0,lob物理读取0,lob预读取读取0. CPU时间= 342 ms,经过时间= 233 ms.
循环结果并以较小批量更新:
declare @rowcount int = 1;
declare @batch_size int = 1000;
while @rowcount > 0
begin
update top(@batch_size) t set t.status_val = 'already updated'
from (
select *, row_number() over(partition by object_id order by (select null)) row_num
from foo
where status_val <> 'already updated'
) t
where row_num > 1
set @rowcount = @@rowcount;
end
Run Code Online (Sandbox Code Playgroud)
如果其他并发会话尝试访问此表,这将有助于保持锁定.