Se eu tiver uma UPDATE
declaração que realmente não altere nenhum dado (porque os dados já estão no estado atualizado). Existe algum benefício de desempenho ao colocar uma verificação na WHERE
cláusula para impedir a atualização?
Por exemplo, haveria alguma diferença na velocidade de execução entre UPDATE 1 e UPDATE 2 no seguinte:
CREATE TABLE MyTable (ID int PRIMARY KEY, Value int);
INSERT INTO MyTable (ID, Value)
VALUES
(1, 1),
(2, 2),
(3, 3);
-- UPDATE 1
UPDATE MyTable
SET
Value = 2
WHERE
ID = 2
AND Value <> 2;
SELECT @@ROWCOUNT;
-- UPDATE 2
UPDATE MyTable
SET
Value = 2
WHERE
ID = 2;
SELECT @@ROWCOUNT;
DROP TABLE MyTable;
O motivo pelo qual pergunto é que preciso que a contagem de linhas inclua a linha inalterada, para que eu saiba se deve fazer uma inserção se o ID não existir. Como tal, usei o formulário UPDATE 2. Se houver um benefício de desempenho ao usar o formulário UPDATE 1, é possível obter a contagem de linhas necessária de alguma forma?
sql-server
query-performance
update
Martin Brown
fonte
fonte
Respostas:
Certamente, pode haver uma pequena diferença de desempenho devido à ATUALIZAÇÃO 1 :
No entanto, qual a diferença existente precisaria ser medida por você em seu sistema com seu esquema, dados e carga do sistema. Existem vários fatores que influenciam o impacto de uma atualização não atualizada:
UPDATE TableName SET Field1 = Field1
, um acionador de atualização será acionado e indicará que o campo foi atualizado (se você marcar usando as funções UPDATE () ou COLUMNS_UPDATED ) e se o campo nas tabelasINSERTED
eDELETED
no mesmo é o mesmo valor.Além disso, a seção de resumo a seguir é encontrada no artigo de Paul White, O impacto de atualizações não atualizáveis (conforme observado por @spaghettidba em um comentário sobre sua resposta):
Lembre-se (especialmente se você não seguir o link para ver o artigo completo de Paulo), os dois itens a seguir:
As atualizações não atualizadas ainda possuem alguma atividade de log, mostrando que uma transação está começando e terminando. Acontece que nenhuma modificação de dados acontece (o que ainda é uma boa economia).
Como afirmei acima, você precisa testar no seu sistema. Use as mesmas consultas de pesquisa que Paul está usando e veja se você obtém os mesmos resultados. Estou vendo resultados ligeiramente diferentes no meu sistema do que o mostrado no artigo. Ainda não há páginas sujas a serem gravadas, mas um pouco mais de atividade de log.
Simplificando, se você está apenas lidando com uma única linha, pode fazer o seguinte:
Para várias linhas, você pode obter as informações necessárias para tomar essa decisão usando a
OUTPUT
cláusula Ao capturar exatamente quais linhas foram atualizadas, é possível restringir os itens para procurar a diferença entre não atualizar linhas que não existem, em vez de não atualizar linhas que existem, mas que não precisam da atualização.Eu mostro a implementação básica na seguinte resposta:
Como evitar o uso de consulta de mesclagem ao converter vários dados usando o parâmetro xml?
O método mostrado nessa resposta não filtra as linhas existentes, mas não precisa ser atualizado. Essa parte pode ser adicionada, mas você primeiro precisa mostrar exatamente onde está obtendo seu conjunto de dados no qual está se mesclando
MyTable
. Eles vêm de uma mesa temporária? Um parâmetro com valor de tabela (TVP)?ATUALIZAÇÃO 1:
Finalmente pude fazer alguns testes e eis o que encontrei em relação ao bloqueio e ao log de transações. Primeiro, o esquema da tabela:
Em seguida, o teste atualizando o campo para o valor que ele já possui:
Resultados:
Por fim, o teste que filtra a atualização devido ao valor não mudar:
Resultados:
Como você pode ver, nada é gravado no log de transações ao filtrar a linha, em oposição às duas entradas que marcam o início e o final da transação. E embora seja verdade que essas duas entradas são quase nada, elas ainda são alguma coisa.
Além disso, o bloqueio dos recursos PAGE e KEY é menos restritivo ao filtrar as linhas que não foram alteradas. Se nenhum outro processo estiver interagindo com esta tabela, provavelmente não é um problema (mas qual é a probabilidade disso, realmente?). Lembre-se de que o teste mostrado em qualquer um dos blogs vinculados (e até mesmo no meu teste) pressupõe implicitamente que não há contenção na tabela, pois ela nunca faz parte dos testes. Dizer que as atualizações não atualizadas são tão leves que não vale a pena fazer a filtragem precisa ser feita com um pouco de sal, já que os testes foram feitos, mais ou menos, no vácuo. Mas em Produção, essa tabela provavelmente não está isolada. Obviamente, pode muito bem ser que o pouco de registro e bloqueios mais restritivos não se traduzam em menos eficiência. Então, a fonte mais confiável de informações para responder a essa pergunta? Servidor SQL. Especificamente:seu SQL Server. Ele mostrará qual método é melhor para o seu sistema :-).
ATUALIZAÇÃO 2:
Se as operações nas quais o novo valor é igual ao valor atual (ou seja, sem atualização) numeram as operações nas quais o novo valor é diferente e a atualização é necessária, o padrão a seguir pode ser ainda melhor, especialmente se há muita disputa na mesa. A idéia é fazer um simples
SELECT
primeiro para obter o valor atual. Se você não obtiver um valor, terá sua resposta sobre oINSERT
. Se você tiver um valor, poderá fazer um simplesIF
e emitir oUPDATE
somente se for necessário.Resultados:
Portanto, existem apenas dois bloqueios adquiridos em vez de 3, e ambos são Intent Shared, não Intent eXclusive ou Intent Update ( Compatibilidade de Bloqueios ). Tendo em mente que cada bloqueio adquirido também será liberado, cada bloqueio é realmente 2 operações, portanto, este novo método é um total de 4 operações em vez das 6 operações no método proposto originalmente. Considerando que esta operação está sendo executada uma vez a cada 15 ms (aproximadamente, conforme declarado pelo OP), ou seja, cerca de 66 vezes por segundo. Portanto, a proposta original equivale a 396 operações de bloqueio / desbloqueio por segundo, enquanto esse novo método equivale a apenas 264 operações de bloqueio / desbloqueio por segundo de bloqueios ainda mais leves. Isso não é garantia de um desempenho incrível, mas certamente vale a pena testar :-).
fonte
Diminua um pouco o zoom e pense na imagem maior. No mundo real, sua declaração de atualização será realmente assim:
Ou será mais parecido com isto:
Porque no mundo real, as tabelas têm muitas colunas. Isso significa que você terá que gerar muita lógica complexa de aplicativo dinâmico para criar seqüências dinâmicas, OU precisará especificar sempre o conteúdo antes e depois de cada campo.
Se você criar essas instruções de atualização dinamicamente para todas as tabelas, passando apenas nos campos que estão sendo atualizados, poderá encontrar rapidamente um problema de poluição do cache do plano semelhante ao problema de tamanhos de parâmetro do NHibernate, alguns anos atrás. Pior ainda, se você criar as instruções de atualização no SQL Server (como nos procedimentos armazenados), queimará preciosos ciclos da CPU porque o SQL Server não é muito eficiente na concatenação de seqüências de caracteres em escala.
Devido a essas complexidades, geralmente não faz sentido fazer esse tipo de comparação linha por linha, campo a campo, enquanto você faz as atualizações. Pense em operações baseadas em conjuntos.
fonte
Você pode ver um ganho de desempenho ao ignorar linhas que não precisam ser atualizadas apenas quando o número de linhas é grande (menos log, menos páginas sujas para gravar no disco).
Ao lidar com atualizações de linha única, como no seu caso, a diferença de desempenho é completamente insignificante. Se a atualização das linhas em todos os casos facilitar para você, faça-o.
Para obter mais informações sobre o tópico, consulte Atualizações sem atualização por Paul White
fonte
Você pode combinar a atualização e inserir em uma instrução. No SQL Server, você pode usar uma instrução MERGE para fazer a atualização e a inserção, se não for encontrada. Para o MySQL, você pode usar INSERT ON DUPLICATE KEY UPDATE .
fonte
Em vez de verificar os valores de todos os campos, você não pode obter um valor de hash usando as colunas nas quais está interessado e comparar com o hash armazenado na linha da tabela?
fonte