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

Actualizar si es diferente/cambiado

Durante la compilación y ejecución de consultas, SQL Server no se toma el tiempo de determinar si una instrucción UPDATE realmente cambiará algún valor o no. Simplemente realiza las escrituras como se espera, incluso si son innecesarias.

En el escenario como

update table1 set col1 = 'hello'

puede pensar que SQL no hará nada, pero lo hará:realizará todas las escrituras necesarias como si realmente hubiera cambiado el valor. Esto ocurre tanto para la tabla física (o índice agrupado) como para cualquier índice no agrupado definido en esa columna. Esto provoca escrituras en las tablas/índices físicos, recálculo de índices y escrituras en el registro de transacciones. Cuando se trabaja con grandes conjuntos de datos, hay enormes beneficios de rendimiento al actualizar solo las filas que recibirán un cambio.

Si queremos evitar la sobrecarga de estas escrituras cuando no son necesarias, tenemos que idear una forma de comprobar si es necesario actualizarlas. Una forma de verificar la necesidad de actualizar sería agregar algo como "where col <> 'hola'.

update table1 set col1 = 'hello' where col1 <> 'hello'

Pero esto no funcionaría bien en algunos casos, por ejemplo, si estuviera actualizando varias columnas en una tabla con muchas filas y solo un pequeño subconjunto de esas filas cambiaría sus valores. Esto se debe a la necesidad de filtrar en todas esas columnas, y los predicados de no igualdad generalmente no pueden usar búsquedas de índice, y la sobrecarga de las escrituras de tabla e índice y las entradas del registro de transacciones como se mencionó anteriormente.

Pero hay una alternativa mucho mejor usando una combinación de una cláusula EXISTS con una cláusula EXCEPT. La idea es comparar los valores de la fila de destino con los valores de la fila de origen coincidente para determinar si realmente se necesita una actualización. Mire la consulta modificada a continuación y examine el filtro de consulta adicional que comienza con EXISTS. Tenga en cuenta cómo dentro de la cláusula EXISTS las declaraciones SELECT no tienen cláusula FROM. Esa parte es particularmente importante porque esto solo agrega un escaneo constante adicional y una operación de filtro en el plan de consulta (el costo de ambos es trivial). Entonces, lo que obtiene es un método muy liviano para determinar si se necesita una ACTUALIZACIÓN en primer lugar, evitando una sobrecarga de escritura innecesaria.

update table1 set col1 = 'hello'
/* AVOID NET ZERO CHANGES */
where exists 
    (
    /* DESTINATION */
    select table1.col1
    except
    /* SOURCE */
    select col1 = 'hello'
    )

Esto parece demasiado complicado en comparación con la búsqueda de actualizaciones en una cláusula WHERE simple para el escenario simple en la pregunta original cuando está actualizando un valor para todas las filas en una tabla con un valor literal. Sin embargo, esta técnica funciona muy bien si está actualizando varias columnas en una tabla, y la fuente de su actualización es otra consulta y desea minimizar las escrituras y las entradas de registros de transacciones. También funciona mejor que probar cada campo con <>.

Un ejemplo más completo podría ser

update table1
   set col1 = 'hello',
       col2 = 'hello',
       col3 = 'hello'
/* Only update rows from CustomerId 100, 101, 102 & 103 */
where table1.CustomerId IN (100, 101, 102, 103)
/* AVOID NET ZERO CHANGES */
  and exists 
    (
    /* DESTINATION */
    select table1.col1
           table1.col2
           table1.col3
    except
    /* SOURCE */
    select z.col1,
           z.col2,
           z.col3
      from #anytemptableorsubquery z
     where z.CustomerId = table1.CustomerId
    )