2008-08-01 7 views
69

MySQL a cette commande SQL incroyablement utile encore properitary.L'implémentation SQL Server 2005 de MySQL REPLACE INTO?

Peut-il être facilement émulé dans SQL Server 2005?

Démarrage d'une nouvelle transaction, faire un Select() puis soit UPDATE ou INSERT et COMMIT est toujours un peu d'une douleur, surtout quand le faire dans l'application et donc en gardant toujours 2 versions de la déclaration.

Je me demande s'il existe un moyen facile et universel d'implémenter une telle fonction dans SQL Server 2005?

Répondre

53

Ceci est quelque chose qui m'ennuie à propos de MSSQL (rant on my blog). Je souhaite que MSSQL pris en charge upsert.

@ code de Darlie-O est une bonne façon dans les anciennes versions SQL (1 vote), mais il est encore essentiellement deux opérations d'entrées-sorties (le exists, puis le update ou insert)

Il y a une façon un peu mieux sur this post, essentiellement:

--try an update 
update tablename 
set field1 = 'new value', 
    field2 = 'different value', 
    ... 
where idfield = 7 

--insert if failed 
if @@rowcount = 0 and @@error = 0 
    insert into tablename 
      (idfield, field1, field2, ...) 
    values (7, 'value one', 'another value', ...) 

Cela réduit à une des opérations IO si elle est une mise à jour, ou deux si un insert.

MS SQL2008 introduit merge de SQL: 2003 standard:

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, ...) 

Maintenant, il est vraiment une seule opération IO, mais le code terrible :-(

+0

Super, merci! Enregistre le Select et n'a souvent même pas besoin d'une teransaction dans des situations où je peux être sûr qu'entre Update et "my" insert, il n'y a pas d'autre insert pour cette clé. –

+1

@Michael Il vaut mieux avoir un index unique sur cette table et gérer les erreurs de clé en double si vous allez utiliser cette solution. –

+3

@Keith Votre instruction de fusion ne fonctionne pas. 'MERGE' ne supporte pas la clause' WHERE', vous devez réécrire cela en utilisant 'USING' et' ON'. De plus, à moins d'ajouter 'WITH (HOLDLOCK)', il y a une course et des 'INSERT' simultanés peuvent se produire, l'un d'entre eux échouant en raison du conflit de clé. –

19

La fonctionnalité que vous recherchez est traditionnellement appelée UPSERT. Au moins savoir comment ça s'appelle peut vous aider à trouver ce que vous cherchez. Je ne pense pas que SQL Server 2005 ait de bonnes façons de le faire. 2008 introduit l'instruction MERGE qui peut être utilisée pour accomplir ceci comme indiqué dans: http://www.databasejournal.com/features/mssql/article.php/3739131 ou http://blogs.conchango.com/davidportas/archive/2007/11/14/SQL-Server-2008-MERGE.aspx

Fusion était disponible dans la version bêta de 2005, mais ils l'ont supprimé dans la version finale.

15

Ce que le upsert/fusion fait est quelque chose à l'effet de ...

IF EXISTS (SELECT * FROM [Table] WHERE Id = X) 
    UPDATE [Table] SET... 
ELSE 
    INSERT INTO [Table] 

Alors, espérons-la combinaison de ces articles et ce code pseudo peut faire bouger les choses.

9

J'ai écrit un blog post about this issue

Le. ligne de fond est que si vous voulez des mises à jour à bas prix ... et vous voulez être en sécurité pour une utilisation simultanée.essayez:

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 

De cette façon, vous avez 1 opération pour les mises à jour et 3 opérations maximum pour les insertions. Donc, si vous mettez généralement à jour c'est une option sûre et bon marché.

Je ferais également très attention à ne pas utiliser quoi que ce soit dangereux pour une utilisation simultanée. Il est vraiment facile d'obtenir des violations de clés primaires ou des lignes en double dans la production.