sql >> Base de Datos >  >> RDS >> Sqlserver

Actualice todos menos uno de los registros duplicados en la tabla en SQL Server

Puede resolver este problema sin una combinación, lo que significa que debería tener un mejor rendimiento. La idea es agrupar los datos por su object_id, contando el número de fila de cada object_id. Esto es lo que hace "partición por". Luego puede actualizar donde el número de fila es> 1. ¡Esto actualizará todos los object_id duplicados excepto el primero!

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 

En una tabla de prueba de 82944 registros, el rendimiento fue tal (¡su kilometraje puede variar!):Tabla 'prueba'. Recuento de escaneo 5, lecturas lógicas 82283, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas lob 0, lecturas físicas lob 0, lecturas anticipadas lob 0. Tiempo de CPU =141 ms, tiempo transcurrido =150 ms.

Sin duda, también podemos resolver este problema mediante el uso de una combinación interna, sin embargo, en general, esto debería conducir a lecturas más lógicas y una CPU más alta:

Tabla 'prueba'. Recuento de escaneos 10, lecturas lógicas 83622, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas lob 0, lecturas físicas lob 0, lecturas anticipadas lob 0.Table 'Workfile'. Recuento de exploración 0, lecturas lógicas 0, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas lob 0, lecturas físicas lob 0, lecturas anticipadas lob 0.Table 'Worktable'. Recuento de escaneos 4, lecturas lógicas 167426, lecturas físicas 0, lecturas anticipadas 0, lecturas lógicas lob 0, lecturas físicas lob 0, lecturas anticipadas lob 0. Tiempo de CPU =342 ms, tiempo transcurrido =233 ms.

Para recorrer los resultados y actualizar en lotes más pequeños:

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

Esto ayudará a mantener el bloqueo si otras sesiones simultáneas intentan acceder a esta tabla.