Sqlserver
 sql >> Database >  >> RDS >> Sqlserver

Aggiorna tutti i record duplicati tranne uno nella tabella in SQL Server

Puoi risolvere questo problema senza un join, il che significa che dovrebbe avere prestazioni migliori. L'idea è di raggruppare i dati in base al tuo object_id, contando il numero di riga di ogni object_id. Questo è ciò che fa "partizione per". Quindi puoi aggiornare dove row_num è> 1. Questo aggiornerà tutti gli object_id duplicati tranne il primo!

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 

Su una tabella di prova di 82944 record, le prestazioni erano tali (il tuo chilometraggio può variare!):Tabella 'test'. Conteggio scansioni 5, letture logiche 82283, letture fisiche 0, letture read-ahead 0, letture logiche lob 0, letture fisiche lob 0, letture read-ahead lob 0. Tempo CPU =141 ms, tempo trascorso =150 ms.

Possiamo certamente risolvere questo problema anche utilizzando un inner join, tuttavia, in generale questo dovrebbe portare a letture più logiche e CPU più elevate:

Tabella 'test'. Conteggio scansioni 10, letture logiche 83622, letture fisiche 0, letture read-ahead 0, letture logiche lob 0, letture fisiche lob 0, letture read-ahead lob 0.Tabella 'Workfile'. Conteggio scansioni 0, letture logiche 0, letture fisiche 0, letture read-ahead 0, letture logiche lob 0, letture fisiche lob 0, letture read-ahead lob 0.Tabella 'Worktable'. Conteggio scansioni 4, letture logiche 167426, letture fisiche 0, letture read-ahead 0, letture logiche lob 0, letture fisiche lob 0, letture read-ahead lob 0. Tempo CPU =342 ms, tempo trascorso =233 ms.

Per scorrere i risultati e aggiornarli in batch più piccoli:

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

Ciò contribuirà a mantenere il blocco se altre sessioni simultanee stanno tentando di accedere a questa tabella.