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 / 166833
Accepted
krystah
krystah
Asked: 2017-03-11 05:56:25 +0800 CST2017-03-11 05:56:25 +0800 CST 2017-03-11 05:56:25 +0800 CST

As atualizações "vazias" criam quantidades iguais de log de transações? [duplicado]

  • 772
Essa pergunta já tem respostas aqui :
Os mecanismos de banco de dados são atualizados quando o valor atualizado é o mesmo? [fechado] (3 respostas)
Fechado há 5 anos .

Pergunta : No SQL Server 2016, atualizar uma coluna com o mesmo valor (por exemplo, atualizar uma coluna de 'john'to 'john') produz a mesma quantidade de log de transações que ao atualizar uma coluna com um valor diferente? Leia abaixo para mais detalhes.

Temos vários trabalhos do SQL Agent em execução em uma programação. Esses trabalhos selecionam dados de uma tabela de origem (dados replicados, servidores vinculados), transformam-nos e inserem/atualizam/excluem as linhas da tabela de destino local de acordo.

Passamos por várias estratégias enquanto tentamos encontrar a melhor maneira de conseguir isso. Nós tentamos

  • Atualizando destino da origem usando MERGE
  • Atualizando o destino da origem usando UPDATE para atualizar todas as colunas
  • Atualizando o destino da origem usando uma única instrução UPDATE por coluna de destino

Agora, sou apenas um DBD júnior e meu entendimento de como o log de transações funciona é muito limitado. Dito isto, meus superiores concluíram que não podemos usar as instruções MERGE ou UPDATE onde todas as colunas são processadas na mesma instrução, pois isso cria um log excessivo. O argumento para isso é que quando você executa uma UPDATE-instrução no SQL Server, quando você define um valor de coluna e o novo valor é igual ao valor antigo, ele ainda é marcado como uma atualização no log de transações. Isso aparentemente se torna caro quando você executa muitas e muitas operações SET inúteis.

No exemplo a seguir, atualizamos o first_nameand last_nameda tabela de destino usando valores da tabela de origem, unidos por id.

-- create source- and target-table
CREATE TABLE [#tgt] (
    [id] Int PRIMARY KEY,
    [first_name] NVarchar(10),
    [last_name] NVarchar(10)
)
CREATE TABLE [#src] (
    [id] Int PRIMARY KEY,
    [first_name] NVarchar(10),
    [last_name] NVarchar(10)
)

-- fill some dummy-data
INSERT INTO [#tgt]([id], [first_name], [last_name])VALUES(1, 'john', 'lennon')
INSERT INTO [#src]([id], [first_name], [last_name])VALUES(1, 'john', 'cena')

-- update target-table with values from source-table
UPDATE 
    [T]
SET 
    [T].[first_name] = [S].[first_name],
    [T].[last_name] = [S].[last_name]
FROM
    [#tgt] AS [T]
    JOIN [#src] AS [S] ON [S].[id] = [T].[id]

DROP TABLE [#tgt]
DROP TABLE [#src]

Este exemplo não verifica se algum valor foi realmente alterado. Se ignorarmos NULL-checking e fallbacks sãos por um momento, isso pode ser verificado de uma das seguintes maneiras:

-- Example #1: updates all rows where first-name or last-name has changed
UPDATE ..
SET ..
FROM ..
WHERE [T].[first_name] <> [S].[first_name] OR [T].[last_name] <> [S].[last_name]

-- Example #2: updates all rows, sets target-value to source-value if value has changed
UPDATE ..
SET 
    ISNULL(NULLIF([S].[first_name], [T].[first_name]), [T].[first_name]),
    ISNULL(NULLIF([S].[last_name], [T].[last_name]), [T].[last_name])
FROM ..

No Exemplo #1, a operação SET atualizará todas as colunas, mesmo que apenas 1 coluna tenha sido alterada.

No Exemplo #2, a operação SET atualizará todas as colunas de todas as linhas, retornando ao valor antigo se o valor não for alterado.

Em ambos os exemplos, todas as colunas são atingidas pela operação SET e, de acordo com meus superiores, isso cria uma quantidade desnecessária/problemática de log de transações quando feito com frequência.

O mesmo se aplica para a MERGEinstrução -. Mesmo se você verificar uma linha correspondente para alterações, todas as colunas serão atingidas pela atualização.

MERGE [#tgt] AS tgt
USING [#src] AS src
ON (tgt.id = src.id)  
WHEN MATCHED AND ([tgt].[first_name] <> [src].[first_name] OR [tgt].[last_name] <> [src].[last_name])
THEN UPDATE SET 
        [tgt].[first_name] = [src].[first_name], 
        [tgt].[last_name] = [src].[last_name];

Então, o que fazemos? Use uma única instrução UPDATE para cada coluna que desejamos atualizar. Nesse caso:

-- first_name
UPDATE [T]
SET [T].[first_name] = [S].[first_name]
FROM
    [#tgt] AS [T]
    JOIN [#src] AS [S] ON [S].[id] = [T].[id]
    WHERE [T].[first_name] <> [S].[first_name]

-- last_name
UPDATE [T]
SET [T].[last_name] = [S].[last_name]
FROM
    [#tgt] AS [T]
    JOIN [#src] AS [S] ON [S].[id] = [T].[id]
    WHERE [T].[last_name] <> [S].[last_name]

Agora, existem alguns contras nessa abordagem:

  • Todas as instruções de atualização devem ser executadas na mesma transação para garantir que uma linha não seja deixada pela metade.
  • É realmente uma merda escrever todo o código (imagine tabelas com mais de 50 colunas)

Parece que deve haver uma maneira mais inteligente de contornar isso, e eu apreciaria qualquer esclarecimento e correção nas declarações feitas neste post. Como mencionado anteriormente, estou apenas tentando o meu melhor para entender por que tem que ser assim.

Peço desculpas pelo post longo e obrigado desde já.

sql-server transaction-log
  • 2 2 respostas
  • 379 Views

2 respostas

  • Voted
  1. Best Answer
    Solomon Rutzky
    2017-03-11T09:08:19+08:002017-03-11T09:08:19+08:00

    meus veteranos concluíram que não podemos usar as instruções MERGE ou UPDATE onde todas as colunas são processadas na mesma instrução, pois isso cria um log excessivo.

    Bem, é legal da parte deles concluir isso. Mas, eles forneceram alguma evidência, ou seus scripts de teste, mostrando esse comportamento? Eu estaria interessado em ver tal teste ;-)

    O argumento para isso é que quando você executa uma instrução UPDATE no SQL Server, quando você define um valor de coluna e o novo valor é igual ao valor antigo, ele ainda é marcado como uma atualização no log de transações.

    Este é um daqueles casos em que um pouco de conhecimento é enganoso. Sim, atualizar uma coluna para o mesmo valor exato é considerado uma atualização, assim como o teste de colunas sendo atualizadas por meio da UPDATE()função retornará 1enquanto a coluna estiver na instrução SET, independentemente do valor ser alterado ou não.

    MAS, as peças que faltam são:

    1. Se nenhuma das colunas estiver mudando de valor, essa linha não será realmente atualizada. E se nenhuma linha for atualizada, a única atividade do log de transações serão dois registros: um LOP_BEGIN_XACTpara marcar o início da transação e um LOP_COMMIT_XACTpara marcar o fim da transação. Mas nenhuma página de dados real ou página de índice é modificada. Isso pressupõe que "Linha(s) afetada(s)" > 0, mas nada realmente mudou.

    2. Se todas as linhas forem filtradas de forma que nenhuma linha seja atualizada (ou seja, "Linha(s) afetadas" = 0), não haverá atividade de Log de Tran.

    3. Se qualquer uma das colunas estiver mudando de valor, as colunas adicionais que estão sendo configuradas para seu valor existente terão a mesma aparência no log de transações como não especificando as colunas que não estão mudando de valor.

    4. Cada consulta (a menos que agrupada com outras em uma transação explícita) é sua própria transação, e cada transação no log de transações tem, no mínimo, as 2 entradas: uma para o início e outra para COMMIT ou ABORT.

    Logo:

    • Suas duas opções de "Exemplo 1" e "Exemplo 2" são quase as mesmas no que diz respeito ao Tran Log. Se houver pelo menos uma linha para atualizar, elas devem ser as mesmas. Mas se não houver linhas em que as colunas estejam mudando de valor, o "Exemplo 1" (com a WHEREcláusula) resultará em menos atividade de Tran Log, pois não haverá entradas enquanto no "Exemplo 2" (todas as linhas "atualizadas") haverá as entradas BEGIN e COMMIT. Portanto, eu recomendaria usar a WHEREcláusula, pois está sendo explícita em suas intenções e resultará em um pouco menos de atividade de Tran Log.

    • Seguir o conselho de seus "idosos" é garantido para resultar em mais atividade de Tran Log, sem mencionar a diminuição do desempenho. Por quê? Porque:

      • Em alguns casos, a mesma linha será sinalizada para atualização se o nome e o sobrenome forem alterados. Mesmo que você envolva ambas as UPDATEinstruções em uma única Transação Explícita para reduzir a inconsistência, bem como entradas de log BEGIN / END extras, você ainda estará atualizando a linha várias vezes em alguns casos, e cada modificação é registrada.
      • Mesmo que as linhas de dados já estejam na memória, ainda leva mais tempo para redigitalizá-las por cada UPDATEinstrução.
    • É sempre melhor saber com certeza e ver por si mesmo, em vez de confiar em conjecturas ou no que outra pessoa afirma. Para isso, você deve testar suas várias opções, incluindo as duas atualizações separadas sugeridas por seus idosos, e após cada teste, verifique através de:

      SELECT   *
      FROM     sys.fn_dblog(NULL, NULL) tl
      ORDER BY tl.[Transaction ID] DESC; -- most recent first
      

    PS Eu fiz meu teste inicial no SQL Server 2012 (SP3) Developer Edition. Em seguida, testei novamente no SQL Server 2016 (RTM) Express Edition e o comportamento foi o mesmo.

    PPS Logicamente, [T].[first_name] = ISNULL(NULLIF([S].[first_name], [T].[first_name]), [T].[first_name])não é diferente de [T].[first_name] = [S].[first_name], é apenas envolvido em mais funções. Mas se ambas as colunas forem 'A', atualizar isso com um 'A'da mesma tabela em oposição a um 'A'da outra tabela é exatamente a mesma operação.

    PPPS Ao verificar quaisquer diferenças nos campos de string, você realmente precisa usar um Collation binário, caso contrário, pode haver alterações em maiúsculas e minúsculas (ou largura ou combinação de caracteres, etc.) . Eu percebo que você mencionou que esses foram exemplos simplificados, mas estou apenas me certificando de que esse aspecto não seja esquecido :-). Por isso:

    WHERE [T].[first_name] <> [S].[first_name] OR [T].[last_name] <> [S].[last_name]
    

    torna-se:

    WHERE [T].[first_name] <> [S].[first_name] COLLATE Latin1_General_100_BIN2
    OR    [T].[last_name] <> [S].[last_name] COLLATE Latin1_General_100_BIN2
    

    E:

    ISNULL(NULLIF([S].[first_name], [T].[first_name]), [T].[first_name]),
    ISNULL(NULLIF([S].[last_name], [T].[last_name]), [T].[last_name])
    

    torna-se:

    ISNULL(NULLIF([S].[first_name] COLLATE Latin1_General_100_BIN2, [T].[first_name]), [T].[first_name]),
    ISNULL(NULLIF([S].[last_name] COLLATE Latin1_General_100_BIN2, [T].[last_name]), [T].[last_name])
    
    • 3
  2. paparazzo
    2017-03-11T12:32:15+08:002017-03-11T12:32:15+08:00

    Qual é o seu nível de registro? Você sabe que o simples limpará o log de transações entre as instruções?

    Se você deseja minimizar o registro, várias atualizações

    UPDATE [T]
       SET [T].[first_name] = [S].[first_name]
      FROM [#tgt] AS [T]
      JOIN [#src] AS [S] 
        ON [T].[id] = [S].[id]
       AND [T].[first_name] != [S].[first_name];
    
    UPDATE [T]
       SET [T].[first_name] = [S].[first_name]
      FROM [#tgt] AS [T]
      JOIN [#src] AS [S] 
        ON [T].[id] = [S].[id]
       AND [T].[first_name] is null and [S].[first_name] is not null;
    
    UPDATE [T]
       SET [T].[first_name] = null
      FROM [#tgt] AS [T]
      JOIN [#src] AS [S] 
        ON [T].[id] = [S].[id]
       AND [T].[first_name] is not null and [S].[first_name] is null;
    

    Então a mesma coisa para last_name

    Eu sei que parece longo mas irá minimizar o registo
    As múltiplas actualizações são normalmente mais eficientes do que OR

    Uh, oh, acabei de ler a mesma transação

    Um ou outro vai ser muito diferente

    Que tal uma vista

    Basta usar a visualização durante a atualização - essa visualização deve ser muito eficiente

    select case when [S].[ID] == null 
                     then [T].[first_name]
                     else [S].[first_name]
                end as [first_name]
         , case when [S].[ID] == null 
                     then [T].[last_name]
                     else [S].[last_name]
                end as [last_name]
    from      [#tgt] [T] 
    left join [#src] [S]
      on [t].[ID] = [S].[ID]
    
    • 0

relate perguntas

  • SQL Server - Como as páginas de dados são armazenadas ao usar um índice clusterizado

  • Preciso de índices separados para cada tipo de consulta ou um índice de várias colunas funcionará?

  • Quando devo usar uma restrição exclusiva em vez de um índice exclusivo?

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

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