2008-08-01 15 views
69

MySQL ma to niesamowicie użyteczne, ale poprawne polecenie SQL Command REPLACE INTO.SQL Server 2005 implementacja MySQL REPLACE INTO?

Czy można to łatwo emulować w SQL Server 2005?

Rozpoczynanie nowej transakcji, robi Select() a następnie albo UPDATE lub INSERT i COMMIT zawsze jest trochę uciążliwe, zwłaszcza gdy robi to we wniosku i dlatego zawsze mając 2 wersje instrukcji.

Zastanawiam się, czy istnieje prosty iuniwersalny sposób, aby zaimplementować taką funkcję w SQL Server 2005?

Odpowiedz

53

To jest coś, co denerwuje mnie na temat MSSQL (rant on my blog). Szkoda, że ​​MSSQL nie obsługuje upsert.

@ kodu Dillie-O jest dobrym sposobem, w starszych wersjach SQL (+1 głos), ale nadal jest w zasadzie dwie operacje IO (the exists a następnie update lub insert)

Jest nieco lepszy sposób na this post, zasadniczo:

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

zmniejsza to do jednej operacji IO, czy jest to zmiana, lub dwa jeśli wkładka.

MS Sql2008 wprowadza merge z SQL: standard 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, ...) 

Teraz to naprawdę tylko jedna operacja IO, ale kod straszne :-(

+0

Świetnie, dzięki! Zapisuje Select i często nawet nie wymaga teran akcji w sytuacjach, w których mogę być pewien, że pomiędzy Update a "my" insertem nie ma innej wstawki dla tego klucza. –

+1

@Michael Lepiej mieć unikalny indeks w tej tabeli i obsługę błędów powtarzających się kluczy, jeśli zamierzasz używać tego rozwiązania. –

+3

@Keith Instrukcja łączenia nie działa. "MERGE" nie obsługuje klauzuli "WHERE", musisz przepisać to używając 'USING' i' ON'. Ponadto, jeśli nie dodasz 'WITH (HOLDLOCK)', istnieje wyścig i jednoczesne 'INSERT's może się zdarzyć, a jeden z nich zawiedzie ze względu na kluczowe zderzenie. –

19

Funkcjonalność, której szukasz, jest tradycyjnie nazywana UPSERT. Najmniej wiedząc, jak się nazywa, może pomóc ci znaleźć to, czego szukasz.

Nie sądzę, że SQL Server 2005 ma wiele świetnych sposobów robienia tego. W 2008 roku wprowadzono instrukcję MERGE, której można użyć do wykonania tej czynności, jak pokazano w: http://www.databasejournal.com/features/mssql/article.php/3739131 lub http://blogs.conchango.com/davidportas/archive/2007/11/14/SQL-Server-2008-MERGE.aspx

Scalenie było dostępne w wersji beta z 2005 r., Ale usunięto je w wersji ostatecznej.

15

Co upsert/scalania robi coś do skutku ...

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

Więc mam nadzieję, że kombinacja tych artykułów, a ten pseudo kod może dostać rzeczy ruchomych.

9

Napisałem blog post about this issue

The. Najważniejsze jest to, że jeśli chcesz otrzymywać tanie aktualizacje ... i chcesz być bezpieczny dla równoczesnego użytkowania, spróbuj:

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 

W ten sposób masz 1 operację dla aktualizacji i maksymalnie 3 operacje dla wkładek. więc jeśli ogólnie aktualizujesz, jest to bezpieczna i tania opcja.

Byłbym również bardzo ostrożny, aby nie używać niczego, co nie jest bezpieczne w przypadku równoczesnego użytkowania. Naprawdę łatwo jest uzyskać podstawowe naruszenia klucza lub duplikaty wierszy w produkcji.

Powiązane problemy