O cenário: Eu tenho um processo ETL. Esse processo envolve ingerir dados de fontes distintas e fazer meu banco de dados sincronizar com o mais recente.
Digamos que eu tenha uma tabela chamada [catalog]. Ela contém o catálogo de produtos da minha empresa. A fonte da verdade para essa tabela me envia um CSV a cada quatro horas. Eu ingiro esse CSV e certifico-me de que minha tabela [catalog] esteja sincronizada com ele. Eu uso uma atualização como esta:
UPDATE mc
SET
[field1] = tc.[field1]
, [field2] = tc.[field2]
FROM [my].[catalog] as mc
INNER JOIN [their].[catalog] as tc
ON mc.id = tc.id
WHERE mc.[field1] <> tc.[field1]
OR mc.[field2] <> tc.[field2]
Este comando comparará cada correspondência e filtrará apenas aqueles que verão mudanças significativas e atualizará apenas essas linhas. Não quero tocar em nenhuma linha desnecessariamente--a tarefa perene dos desenvolvedores de ETL.
Nenhuma atualização trivial será executada (por atualização trivial, quero dizer uma atualização para uma linha que não altera nenhum dos campos naquela linha). As linhas sem alterações permanecem como estavam, sem serem molestadas.
Unmolested é uma boa palavra para usar aqui porque atualizar uma linha sem impactar valores tem um custo desnecessário. Na RAM e no disco, uma atualização de uma linha exclui fisicamente a linha com os valores antigos e insere uma nova linha com os novos valores. Todas essas atualizações precisam ser registradas! Então, no processo de não fazer nada, fragmentamos e dividimos as páginas de índice e dados que temos para a tabela na RAM e no disco. E estamos colocando tráfego desnecessário no log.
Se não estamos mudando nada, então uma atualização trivial ainda está causando rotatividade na RAM e no disco. Dar a uma tabela uma maneira automática de ignorar atualizações triviais seria uma grande vitória para muitos cenários.
Agora, digamos que algum desenvolvedor desajeitado faça uma atualização por meio de algum cliente ou em uma alteração de procedimento que faça algo semelhante a uma tabela grande, mas não inclua nenhuma medida para ignorar linhas que não precisam 'realmente' da atualização. Há algo que eu possa fazer na tabela para fazê-la agir como se soubesse passar atualizações triviais?
Você deve adicionar qual sistema de banco de dados, versão e edição você está usando...
...porque sua quilometragem pode variar em algumas das conclusões que você tirou, dependendo desses fatores.
Por exemplo:
O SQL Server não processa
UPDATE
instruções redundantes, em diferentes extensões, em cenários específicos.Então suas declarações...
...estão incorretas em alguns contextos.
Prova
Paul White escreveu um post de blog muito interessante sobre este assunto intitulado The Impact of Non-Updating Updates . É uma leitura curta, então recomendo fortemente que você o leia na íntegra. Mas alguns (não quero revelar todos os detalhes interessantes) dos pontos-chave que ele resume no final são:
...sério, não confie em mim para copiar e colar corretamente acima, e vá ler o artigo de Paul na íntegra.
Portanto
Pelo menos no SQL Server, atualizações redundantes, também conhecidas como atualizações sem atualização, nem sempre " causam rotatividade desnecessária na RAM e no disco ".
Mas é bom ser explícito
Porque nem todos os casos são garantidos e as coisas podem mudar no futuro, seja na forma como o mecanismo de banco de dados processa uma atualização não atualizada
UPDATE
ou na forma como seu código e dados evoluem ao longo do tempo.Existem algumas maneiras diferentes de tentar resolver:
A resposta de Akina é uma opção, embora ela ainda gere registros de log para exclusões das linhas que você removeu antecipadamente e exija uma passagem dupla para atingir seu objetivo.
Outra opção é fazer hash da linha e materializar o valor hash em uma coluna para que ela possa ser indexada. Então você pode comparar suas chaves
SourceTable
eTargetTable
nas chaves deles onde os valores hash não correspondem, de uma forma muito eficiente, por meio de uma consulta como:Uma passagem, comparação eficiente, sem necessidade de gerar registros de log extras excluindo as linhas que não serão usadas para a atualização porque seus valores são redundantes.
No passado, usei essa técnica para grandes conjuntos de dados para problemas semelhantes. Eu particularmente uso a
HASHBYTES()
função para calcular o hash da linha, que é determinístico e pode ser armazenado em uma coluna computada ou em uma visualização indexada (se você não quiser modificar o esquema da tabela). Mas, novamente, suas opções aqui dependerão do seu sistema de banco de dados, versão e edição.Nota lateral
Acho que " não molestado " é uma péssima escolha de palavras neste contexto devido às suas conotações usuais.
Você recebe seu CSV e o salva na
[their].[catalog]
tabela.Como primeiro passo, exclua as linhas desta tabela que causarão "atualizações triviais".
Esta consulta usará índices compostos de acordo com muita eficiência. Então, não esqueça de criar esses índices em sua tabela temporária que contém o estado real dos dados recebidos de sua "fonte da verdade".
Após essa exclusão, a tabela
[their].[catalog]
conterá apenas as linhas que causarão "atualizações práticas", e você poderá atualizar sem filtragem adicional:Esta consulta, novamente, pode usar índices (primários?) de acordo com eficiência.
Eu realmente aprecio as contribuições acima. Valeu, pessoal! O link para o artigo de Paul White é realmente útil. Muito obrigado.
A solução que tenho é adicionar uma coluna calculada persistente à tabela [row_hash]. É o resultado da função Hashbytes do SQL Server.
Um gatilho INSTEAD OF me permite agrupar as pseudotabelas inseridas e excluídas por suas chaves e agir somente em linhas que tenham uma hash_column alterada.
Com o gatilho em vigor, a tabela é mais tolerante para um desenvolvedor menos iniciado. E também posso cortar parte da sobrecarga que venho construindo com praticamente todas as instruções MERGE que escrevo.
Com o row_hash persistido e o gatilho INSTEAD OF agindo como o border collie, apenas as linhas com alterações reais são alteradas.
O código para configurar isso está neste documento: https://github.com/islandmonk/update_only_when_changed/blob/main/row_hash%20infrastructure.sql
Um exemplo de uso é em uma tabela com o seguinte esquema:
O código necessário para configurar isso para ignorar atualizações redundantes é este: