Napisałem przechowywany proces, który dokona aktualizacji, jeśli istnieje rekord, w przeciwnym razie dokona wstawienia. Wygląda mniej więcej tak:
update myTable set Col1=@col1, Col2=@col2 where ID=@ID
if @@rowcount = 0
insert into myTable (Col1, Col2) values (@col1, @col2)
Moja logika stojąca za pisaniem tego w ten sposób jest taka, że aktualizacja przeprowadzi niejawną selekcję przy użyciu klauzuli where i jeśli zwróci ona 0, nastąpi wstawienie.
Alternatywą dla zrobienia tego w ten sposób byłoby dokonanie wyboru, a następnie na podstawie liczby zwróconych wierszy albo zaktualizuj, albo wstaw. Uznałem to za nieefektywne, ponieważ jeśli masz wykonać aktualizację, spowoduje to 2 wybory (pierwsze jawne wywołanie wyboru i drugie niejawne w miejscu aktualizacji). Gdyby proc miał wykonać wstawkę, nie byłoby różnicy w wydajności.
Czy moja logika brzmi tutaj? Czy w ten sposób można połączyć wstawkę i aktualizację w przechowywany proces?
źródło
Przeczytaj post na moim blogu, aby uzyskać dobry, bezpieczny wzór, którego możesz użyć. Istnieje wiele rozważań, a przyjęta odpowiedź na to pytanie jest daleka od bezpiecznej.
Aby uzyskać szybką odpowiedź, wypróbuj następujący wzór. Będzie działać dobrze na SQL 2000 i nowszych. SQL 2005 zapewnia obsługę błędów, co otwiera inne opcje, a SQL 2008 udostępnia polecenie MERGE.
źródło
Jeśli ma być używany z SQL Server 2000/2005, oryginalny kod musi być dołączony do transakcji, aby zapewnić spójność danych w scenariuszu współbieżnym.
Spowoduje to dodatkowe koszty wydajności, ale zapewni integralność danych.
Dodaj, jak już zasugerowano, należy użyć MERGE, jeśli jest dostępny.
źródło
Nawiasem mówiąc, MERGE to jedna z nowych funkcji SQL Server 2008.
źródło
Nie tylko musisz uruchamiać go w transakcji, ale także wymaga wysokiego poziomu izolacji. W rzeczywistości domyślny poziom izolacji to Read Commited i ten kod wymaga serializacji.
Może dodanie również sprawdzania błędów @@ i cofania zmian mogłoby być dobrym pomysłem.
źródło
Jeśli nie wykonujesz scalania w SQL 2008, musisz zmienić to na:
if @@ rowcount = 0 i @@ error = 0
w przeciwnym razie, jeśli aktualizacja nie powiedzie się z jakiegoś powodu, spróbuje później wstawić, ponieważ liczba wierszy w nieudanej instrukcji wynosi 0
źródło
Wielki fan UPSERT, naprawdę ogranicza kod do zarządzania. Oto inny sposób, w jaki to robię: jeden z parametrów wejściowych to ID, jeśli ID ma wartość NULL lub 0, wiesz, że to INSERT, w przeciwnym razie jest to aktualizacja. Zakłada, że aplikacja wie, czy istnieje identyfikator, więc nie będzie działać we wszystkich sytuacjach, ale jeśli to zrobisz, skróci wykonywanie o połowę.
źródło
Zmodyfikowany post Dimy Malenko:
Możesz przechwycić błąd i wysłać rekord do uszkodzonej tabeli wstawiania.
Musiałem to zrobić, ponieważ pobieramy wszelkie dane wysyłane przez WSDL i, jeśli to możliwe, naprawiamy je wewnętrznie.
źródło
Twoja logika wydaje się rozsądna, ale możesz rozważyć dodanie kodu, aby zapobiec wstawianiu, jeśli przekazałeś określony klucz podstawowy.
W przeciwnym razie, jeśli zawsze robisz wstawianie, jeśli aktualizacja nie wpłynęła na żadne rekordy, co się stanie, gdy ktoś usunie rekord przed uruchomieniem „UPSERT”? Teraz rekord, który próbujesz zaktualizować, nie istnieje, więc zamiast tego utworzy rekord. Prawdopodobnie nie jest to zachowanie, którego szukałeś.
źródło