Sqlserver
 sql >> Base de Dados >  >> RDS >> Sqlserver

Atualizar se diferente/alterado


Durante a compilação e execução da consulta, o SQL Server não leva tempo para descobrir se uma instrução UPDATE realmente alterará algum valor ou não. Ele apenas executa as gravações conforme o esperado, mesmo que desnecessário.

No cenário como
update table1 set col1 = 'hello'

você pode pensar que o SQL não fará nada, mas fará – ele executará todas as gravações necessárias como se você realmente tivesse alterado o valor. Isso ocorre tanto para a tabela física (ou índice clusterizado) quanto para quaisquer índices não clusterizados definidos nessa coluna. Isso causa gravações nas tabelas/índices físicos, recálculo de índices e gravações de log de transações. Ao trabalhar com grandes conjuntos de dados, há enormes benefícios de desempenho ao atualizar apenas as linhas que receberão uma alteração.

Se quisermos evitar a sobrecarga dessas gravações quando não forem necessárias, temos que criar uma maneira de verificar a necessidade de atualização. Uma maneira de verificar a necessidade de atualização seria adicionar algo como “where col <> 'hello'.
update table1 set col1 = 'hello' where col1 <> 'hello'

Mas isso não funcionaria bem em alguns casos, por exemplo, se você estivesse atualizando várias colunas em uma tabela com muitas linhas e apenas um pequeno subconjunto dessas linhas realmente tivesse seus valores alterados. Isso ocorre devido à necessidade de filtrar todas essas colunas, e os predicados de não igualdade geralmente não são capazes de usar buscas de índice e a sobrecarga de gravações de tabela e índice e entradas de log de transações, conforme mencionado acima.

Mas há uma alternativa muito melhor usando uma combinação de uma cláusula EXISTS com uma cláusula EXCEPT. A ideia é comparar os valores na linha de destino com os valores na linha de origem correspondente para determinar se uma atualização é realmente necessária. Observe a consulta modificada abaixo e examine o filtro de consulta adicional começando com EXISTS. Observe como dentro da cláusula EXISTS as instruções SELECT não possuem cláusula FROM. Essa parte é particularmente importante porque isso adiciona apenas uma verificação constante adicional e uma operação de filtro no plano de consulta (o custo de ambos é trivial). Então, o que você obtém é um método muito leve para determinar se um UPDATE é necessário em primeiro lugar, evitando sobrecarga de gravação desnecessária.
update table1 set col1 = 'hello'
/* AVOID NET ZERO CHANGES */
where exists 
    (
    /* DESTINATION */
    select table1.col1
    except
    /* SOURCE */
    select col1 = 'hello'
    )

Isso parece muito complicado em relação à verificação de atualizações em uma cláusula WHERE simples para o cenário simples na pergunta original quando você está atualizando um valor para todas as linhas em uma tabela com um valor literal. No entanto, essa técnica funciona muito bem se você estiver atualizando várias colunas em uma tabela e a origem de sua atualização for outra consulta e você desejar minimizar as gravações e entradas de logs de transações. Ele também tem um desempenho melhor do que testar todos os campos com <>.

Um exemplo mais completo pode 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
    )