Wdrożenie MySQL w SQL Server 2005 WYMIENIĆ NA?

86

MySQL ma to niezwykle przydatne, ale zastrzeżone REPLACE INTOpolecenie SQL.

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

Rozpoczęcie nowej transakcji, wykonanie Select()a potem albo UPDATEalbo INSERTi COMMITzawsze jest trochę uciążliwe, szczególnie gdy robisz to w aplikacji i dlatego zawsze zachowujesz 2 wersje wyciągu.

Zastanawiam się, czy istnieje łatwy i uniwersalny sposób zaimplementowania takiej funkcji w SQL Server 2005?

Michael Stum
źródło

Odpowiedzi:

60

To jest coś, co mnie denerwuje w MSSQL ( rant na moim blogu ). Chciałbym, żeby obsługiwany był MSSQL upsert.

Kod @ Dillie-O jest dobry w starszych wersjach SQL (+1 głos), ale nadal jest to w zasadzie dwie operacje IO ( existsa następnie updateor insert)

W tym poście jest nieco lepszy sposób :

--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 we / wy, jeśli jest to aktualizacja, lub dwóch, jeśli jest wstawiana.

MS Sql2008 wprowadza mergeze standardu 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, ... )

Teraz to tylko jedna operacja IO, ale okropny kod :-(

Keith
źródło
Wielkie dzięki! Zapisuje Select i często nie wymaga nawet teransakcji w sytuacjach, w których mogę być pewien, że pomiędzy Aktualizacją a „moją” wstawką nie ma innej wkładki dla tego klucza.
Michael Stum
2
@Michael Lepiej mieć unikalny indeks w tej tabeli i obsługę błędów zduplikowanych kluczy, jeśli zamierzasz używać tego rozwiązania.
Sam Saffron,
3
@Keith Twoja instrukcja łączenia nie działa. MERGEnie obsługuje WHEREklauzuli, musisz to przepisać, używając USINGi ON. Ponadto, jeśli nie dodasz WITH (HOLDLOCK), jest wyścig i INSERTmogą się zdarzyć równoczesne s, a jeden z nich zawiedzie z powodu zderzenia kluczy.
Evgeniy Berezovsky,
Tak, jak wskazano tutaj: weblogs.sqlteam.com/dang/archive/2009/01/31/ ... MERGE nie jest atomowa. Usuwa niejawną blokadę aktualizacji, ale zwalnia ją przed wykonaniem wstawiania, co powoduje sytuację wyścigu, która może skutkować naruszeniem klucza podstawowego. Aby operacja była niepodzielna, musisz użyć jawnego HOLDLOCK oprócz niejawnego UPDLOCK. W obecnym kształcie nie jest atomowy, mimo że wydaje się być pojedynczym stwierdzeniem.
Triynko
1
Składnia MERGE jest nieprawidłowa i została poprawiona w nowszej odpowiedzi tego samego autora: stackoverflow.com/a/243670/24472
Larry,
21

Funkcjonalność, której szukasz, jest tradycyjnie nazywana UPSERT. Przynajmniej wiedza o tym, jak to się nazywa, może pomóc Ci znaleźć to, czego szukasz.

Nie sądzę, aby SQL Server 2005 miał na to świetne sposoby. 2008 wprowadza oświadczenie MERGE, które można wykorzystać do osiągnięcia tego celu, jak pokazano na: http://www.databasejournal.com/features/mssql/article.php/3739131 lub http://blogs.conchango.com/davidportas/archive/ 2007/11/14 / SQL-Server-2008-MERGE.aspx

Merge było dostępne w wersji beta 2005, ale usunęli je w ostatecznej wersji.

Karl Seguin
źródło
18

To, co robi upsert / merge, ma wpływ na ...

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

Miejmy nadzieję, że połączenie tych artykułów i tego pseudo kodu może ożywić sprawę.

Dillie-O
źródło
10

Napisałem wpis na blogu dotyczący tego problemu.

Najważniejsze jest to, że jeśli chcesz tanie aktualizacje i chcesz być bezpieczny przy jednoczesnym użyciu, 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ę aktualizacji i maksymalnie 3 operacje wstawiania. Tak więc, jeśli generalnie aktualizujesz, jest to bezpieczna i tania opcja.

Byłbym również bardzo ostrożny, aby nie używać niczego, co jest niebezpieczne przy jednoczesnym użyciu. Uzyskanie naruszeń klucza podstawowego lub zduplikowanych wierszy w środowisku produkcyjnym jest naprawdę łatwe.

Sam Saffron
źródło