Sqlserver
 sql >> Datenbank >  >> RDS >> Sqlserver

Aktualisieren Sie alle bis auf einen der doppelten Datensätze in der Tabelle in SQL Server

Sie können dieses Problem ohne einen Join lösen, was bedeutet, dass es eine bessere Leistung haben sollte. Die Idee ist, die Daten nach Ihrer Objekt-ID zu gruppieren und die Zeilennummer jeder Objekt-ID zu zählen. Das macht "partition by". Dann können Sie aktualisieren, wo row_num> 1 ist. Dadurch werden alle duplizierten object_id außer der ersten aktualisiert!

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 

Auf einer Testtabelle mit 82944 Datensätzen war die Leistung so (Ihre Laufleistung kann variieren!):Tabelle 'test'. Scan-Zähler 5, logische Lesevorgänge 82283, physische Lesevorgänge 0, Read-Ahead-Lesevorgänge 0, Lob-Logik-Reads 0, Lob-Physische Lesevorgänge 0, Lob-Read-Ahead-Lesevorgänge 0.CPU-Zeit =141 ms, verstrichene Zeit =150 ms.

Wir können dieses Problem sicherlich auch lösen, indem wir einen Inner Join verwenden, aber im Allgemeinen sollte dies zu mehr logischen Lesevorgängen und einer höheren CPU führen:

Tabelle 'Test'. Anzahl der Scans 10, logische Lesevorgänge 83622, physische Lesevorgänge 0, Read-Ahead-Lesevorgänge 0, logische Lob-Reads 0, physische Lob-Reads 0, Lob-Read-Ahead-Lesevorgänge 0.Tabelle „Arbeitsdatei“. Scan-Anzahl 0, logische Lesevorgänge 0, physische Lesevorgänge 0, Read-Ahead-Lesevorgänge 0, Lob-Logik-Reads 0, Lob-Physical-Reads 0, Lob-Read-Ahead-Reads 0. Tabelle „Arbeitstabelle“. Scan-Anzahl 4, logische Lesevorgänge 167426, physische Lesevorgänge 0, Read-Ahead-Lesevorgänge 0, Lob-Logik-Reads 0, Lob-Physische Lesevorgänge 0, Lob-Read-Ahead-Lesevorgänge 0.CPU-Zeit =342 ms, verstrichene Zeit =233 ms.

Um die Ergebnisse zu durchlaufen und in kleineren Stapeln zu aktualisieren:

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

Dies hilft dabei, die Sperrung aufrechtzuerhalten, wenn andere gleichzeitige Sitzungen versuchen, auf diese Tabelle zuzugreifen.