Isso é algo que me irrita no MSSQL ( discurso retórico no meu blog ). Desejo suporte a MSSQLupsert
.
O código de @Dillie-O é uma boa maneira em versões mais antigas do SQL (+1 voto), mas ainda é basicamente duas operações de IO (o exists
e depois o update
ouinsert
)
Há uma maneira um pouco melhor neste post , basicamente:
update tablename
set field1 = 'new value',
field2 = 'different value',
...
where idfield = 7
if @@rowcount = 0 and @@error = 0
insert into tablename
( idfield, field1, field2, ... )
values ( 7, 'value one', 'another value', ... )
Isso reduz a uma operação de IO se for uma atualização, ou duas se for uma inserção.
MS Sql2008 apresenta a merge
partir do padrão SQL: 2003:
merge tablename as target
using (values ('new value', 'different value'))
as source (field1, field2)
on target.idfield = 7
when matched then
update
set field1 = source.field1,
field2 = source.field2,
...
when not matched then
insert ( idfield, field1, field2, ... )
values ( 7, source.field1, source.field2, ... )
Agora é realmente apenas uma operação de IO, mas um código horrível :-(
MERGE
não oferece suporte àWHERE
cláusula, você deve reescrever usandoUSING
eON
. Além disso, a menos que você adicioneWITH (HOLDLOCK)
, há uma corrida eINSERT
s concorrentes podem acontecer, com um deles falhando devido ao conflito de chave.A funcionalidade que você está procurando é tradicionalmente chamada de UPSERT. Pelo menos saber como é chamado pode ajudá-lo a encontrar o que procura.
Não acho que o SQL Server 2005 tenha boas maneiras de fazer isso. 2008 apresenta a instrução MERGE que pode ser usada para fazer isso, conforme mostrado em: http://www.databasejournal.com/features/mssql/article.php/3739131 ou http://blogs.conchango.com/davidportas/archive/ 14/11/2007 / SQL-Server-2008-MERGE.aspx
O Merge estava disponível na versão beta de 2005, mas foi removido na versão final.
fonte
O que o upsert / merge está fazendo é algo no sentido de ...
IF EXISTS (SELECT * FROM [Table] WHERE Id = X) UPDATE [Table] SET... ELSE INSERT INTO [Table]
Portanto, esperançosamente, a combinação desses artigos e este pseudo código pode fazer as coisas andarem.
fonte
Eu escrevi uma postagem no blog sobre esse problema.
O resultado final é que se você deseja atualizações baratas e deseja estar seguro para uso simultâneo, tente:
update t set hitCount = hitCount + 1 where pk = @id if @@rowcount < 1 begin begin tran update t with (serializable) set hitCount = hitCount + 1 where pk = @id if @@rowcount = 0 begin insert t (pk, hitCount) values (@id,1) end commit tran end
Desta forma, você tem 1 operação para atualizações e um máximo de 3 operações para inserções. Portanto, se você costuma atualizar, esta é uma opção segura e barata.
Eu também teria muito cuidado para não usar nada que não seja seguro para uso simultâneo. É realmente fácil obter violações de chave primária ou linhas duplicadas na produção.
fonte