Sqlserver
 sql >> Base de données >  >> RDS >> Sqlserver

Mettre à jour tous les enregistrements en double sauf un dans la table dans SQL Server

Vous pouvez résoudre ce problème sans jointure, ce qui signifie qu'il devrait avoir de meilleures performances. L'idée est de regrouper les données par votre object_id, en comptant le numéro de ligne de chaque object_id. C'est ce que fait "partitionner par". Ensuite, vous pouvez mettre à jour où le row_num est> 1. Cela mettra à jour tous les object_id dupliqués sauf le premier!

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 

Sur une table de test de 82944 enregistrements, les performances étaient telles (votre kilométrage peut varier !) :Table 'test'. Nombre de balayages 5, lectures logiques 82283, lectures physiques 0, lectures anticipées 0, lectures logiques lob 0, lectures physiques lob 0, lectures anticipées lob 0. Temps CPU =141 ms, temps écoulé =150 ms.

Nous pouvons certainement également résoudre ce problème en utilisant une jointure interne, cependant, en général, cela devrait conduire à des lectures plus logiques et à un CPU plus élevé :

Tableau 'essai'. Nombre de balayages 10, lectures logiques 83622, lectures physiques 0, lectures anticipées 0, lectures logiques lob 0, lectures physiques lob 0, lectures anticipées lob 0.Table 'Workfile'. Nombre de balayages 0, lectures logiques 0, lectures physiques 0, lectures anticipées 0, lectures logiques lob 0, lectures physiques lob 0, lectures anticipées lob 0.Table 'Table de travail'. Nombre d'analyses 4, lectures logiques 167426, lectures physiques 0, lectures anticipées 0, lectures logiques lob 0, lectures physiques lob 0, lectures anticipées lob 0. Temps CPU =342 ms, temps écoulé =233 ms.

Pour boucler sur les résultats et mettre à jour par lots plus petits :

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

Cela aidera à maintenir le verrouillage si d'autres sessions simultanées tentent d'accéder à cette table.