AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 343049
Accepted
Doug Hills
Doug Hills
Asked: 2024-10-17 06:55:03 +0800 CST2024-10-17 06:55:03 +0800 CST 2024-10-17 06:55:03 +0800 CST

Faça uma tabela Ignore atualizações triviais

  • 772

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?

trigger
  • 3 3 respostas
  • 72 Views

3 respostas

  • Voted
  1. J.D.
    2024-10-17T11:34:14+08:002024-10-17T11:34:14+08:00

    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 UPDATEinstruções redundantes, em diferentes extensões, em cenários específicos.

    Então suas declarações...

    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 mudarmos nada, uma atualização trivial ainda estará causando rotatividade na RAM e no disco.

    ...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:

    • Atualizações não atualizadas em uma tabela agrupada geralmente evitam registro extra e limpeza de página, a menos que uma coluna que forma (parte) da chave de agrupamento seja afetada pela operação de atualização.
    • As tabelas de heap se comportam da mesma forma que as tabelas em cluster, exceto que elas não têm uma chave de cluster para causar qualquer registro extra ou limpeza de página.

      Isso continua sendo o caso mesmo quando uma chave primária não clusterizada existe no heap. Atualizações não atualizadas em um heap, portanto, geralmente evitam o registro e a descarga extras (mas veja abaixo)

    • Tanto os heaps quanto as tabelas agrupadas sofrerão registro e liberação extras para qualquer linha em que uma coluna LOB contendo mais de 8000 bytes de dados for atualizada para o mesmo valor usando qualquer sintaxe diferente de SET column_name = column_name.

    ...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 UPDATEou na forma como seu código e dados evoluem ao longo do tempo.

    Existem algumas maneiras diferentes de tentar resolver:

    Existe algo que eu possa fazer na tabela para que ela aja como se soubesse passar atualizações triviais?

    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 SourceTablee TargetTablenas chaves deles onde os valores hash não correspondem, de uma forma muito eficiente, por meio de uma consulta como:

    UPDATE T
    SET T.SomeField1 = S.SomeField1
    FROM SourceTable AS S
    INNER JOIN TargetTable AS T
        ON S.KeyField = T.KeyField -- Same row by key
        AND S.RowHash <> T.RowHash; -- But has changes
    

    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

    Imperturbável é uma boa palavra para usar aqui...

    Acho que " não molestado " é uma péssima escolha de palavras neste contexto devido às suas conotações usuais.

    • 1
  2. Akina
    2024-10-17T13:37:58+08:002024-10-17T13:37:58+08:00

    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".

    DELETE FROM [their].[catalog] AS tc
    WHERE EXISTS (
        SELECT NULL
        FROM [my].[catalog] AS mc
        WHERE mc.id = tc.id 
          AND mc.[field1] = tc.[field1] 
          AND mc.[field2] = tc.[field2]
        );
    

    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:

    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;
    

    Esta consulta, novamente, pode usar índices (primários?) de acordo com eficiência.

    • 0
  3. Best Answer
    Doug Hills
    2024-10-18T01:47:34+08:002024-10-18T01:47:34+08:00

    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:

        CREATE TABLE [dbo].[note](
              [note_id] [int] IDENTITY(1,1) NOT NULL PRIMARY KEY 
            , [object_id] [int] NULL 
            , [column_id] [int] NULL 
            , [parent_note_id] [int] NULL 
            , [note] [nvarchar](max) NULL 
            , [created] [datetime] NOT NULL default(getdate())
        )  
    

    O código necessário para configurar isso para ignorar atualizações redundantes é este:

    -- Add row_hash column to table [dbo].[note]
    --
    ALTER TABLE [dbo].[note] ADD row_hash as HASHBYTES('sha2_512', CONCAT(
          [object_id]
        , [column_id]
        , [parent_note_id]
        , [note]
        , [created])
    ) PERSISTED; 
    GO
    
    
    
    --------
    -- INSTEAD OF UPDATE trigger for [dbo].[note]
    CREATE OR ALTER TRIGGER dbo_note__instead_of_IUD ON [dbo].[note]
    INSTEAD OF UPDATE, INSERT, DELETE 
    AS
        /*
        This is the definition of an INSTEAD OF trigger. Its initial purpose is to reduce churn on tables
        mostly for the sake of performance. There is nothing stopping you from altering this trigger to
        add other functionality. Keep in mind, also, that you can have AFTER UPDATE triggers on the same
        table as one with an INSTEAD OF UPDATE trigger. So that is still available to you even if you
        go with this approach.
        */
    
        UPDATE d
        SET 
              [object_id] = i.[object_id]
            , [column_id] = i.[column_id]
            , [parent_note_id] = i.[parent_note_id]
            , [note] = i.[note]
        FROM [dbo].[note] as d -- deleted
        INNER JOIN inserted as i 
            ON d.[note_id] = i.[note_id] 
    
        -- rows where there is no difference between inserted and deleted are redundant and ignored
        WHERE d.[row_hash] <> i.[row_hash] 
    
    
        -- Inserts proceed as usual
    
        INSERT [dbo].[note] (
              [object_id]
            , [column_id]
            , [parent_note_id]
            , [note]
            , [created]
        )
        SELECT 
              [object_id]
            , [column_id]
            , [parent_note_id]
            , [note]
            , [created]
        FROM inserted as i
        WHERE NOT EXISTS (
            SELECT TOP 1 1
            FROM deleted as d
            WHERE d.[note_id] = i.[note_id]     
        )
    
        -- DELETES proceed as usual
    
        DELETE t 
        FROM [dbo].[note] as t -- target 
        INNER JOIN deleted as d
            ON d.[note_id] = i.[note_id] 
        WHERE NOT EXISTS (
            SELECT TOP 1 1
            FROM inserted as i
            WHERE d.[note_id] = i.[note_id]     
        )
    GO
    --------------
    
    
    
    • 0

relate perguntas

  • É sábio usar o gatilho para atualizar outra tabela?

  • armazenar conjunto de resultados em tabela temporária, variável ou variáveis ​​separadas dentro de um gatilho

  • Gatilho: mova as linhas excluídas para a tabela de arquivo

  • Erro de impasse do SQL Server 2000 para o SQL Server 2008

  • Em um gatilho, posso determinar se uma coluna foi explicitamente definida como um valor ou não foi mencionada na instrução de atualização?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve