Sqlserver
 sql >> Baza danych >  >> RDS >> Sqlserver

Używanie warunku if we wstawianiu SQL Server

Wzór to (bez obsługi błędów):

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

BEGIN TRANSACTION;

UPDATE #TProductSales SET StockQty = @StockQty, ETA1 = @ETA1
  WHERE ProductID = @ProductID;

IF @@ROWCOUNT = 0
BEGIN
  INSERT #TProductSales(ProductID, StockQTY, ETA1) 
    VALUES(@ProductID, @StockQTY, @ETA1);
END

COMMIT TRANSACTION;

Nie musisz tutaj wykonywać dodatkowego odczytu tabeli #temp. Już to robisz, próbując zaktualizować. Aby chronić się przed warunkami wyścigu, robisz to samo, co zabezpieczasz dowolny blok dwóch lub więcej instrukcji, które chcesz wyizolować:owijasz go w transakcję z odpowiednim poziomem izolacji (prawdopodobnie możliwym do serializacji tutaj, chociaż to wszystko tylko ma sens, gdy nie mówimy o tabeli #temp, ponieważ jest ona z definicji serializowana).

Nie jesteś dalej do przodu, dodając IF EXISTS sprawdź (i będziesz musiał dodać wskazówki dotyczące blokowania, aby i tak uczynić to bezpiecznym / możliwym do serializacji), ale możesz być dalej w tyle, w zależności od tego, ile razy aktualizujesz istniejące wiersze, a ile razy wstawiasz nowe. To może dać dużo dodatkowych we/wy.

Ludzie prawdopodobnie powiedzą Ci, żebyś używał MERGE (co w rzeczywistości jest wieloma operacjami za kulisami, a także musi być chronione za pomocą serializacji), zachęcam, aby tego nie robić. Wyjaśniam, dlaczego tutaj:

  • Zachowaj ostrożność z instrukcją MERGE SQL Server

W przypadku wzorca wielowierszowego (takiego jak TVP) potraktowałbym to w ten sam sposób, ale nie ma praktycznego sposobu na uniknięcie drugiego odczytu, jak w przypadku jednowierszowego. I nie, MERGE też tego nie unika.

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

BEGIN TRANSACTION;

UPDATE t SET t.col = tvp.col
  FROM dbo.TargetTable AS t
  INNER JOIN @TVP AS tvp
  ON t.ProductID = tvp.ProductID;

INSERT dbo.TargetTable(ProductID, othercols)
  SELECT ProductID, othercols
  FROM @TVP AS tvp
  WHERE NOT EXISTS
  (
    SELECT 1 FROM dbo.TargetTable
    WHERE ProductID = tvp.ProductID
  );

COMMIT TRANSACTION;

Cóż, myślę, że jest na to sposób, ale nie przetestowałem tego dokładnie:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

BEGIN TRANSACTION;

DECLARE @exist TABLE(ProductID int PRIMARY KEY);

UPDATE t SET t.col = tvp.col
  OUTPUT deleted.ProductID INTO @exist
  FROM dbo.TargetTable AS t
  INNER JOIN @tvp AS tvp
  ON t.ProductID = tvp.ProductID;

INSERT dbo.TargetTable(ProductID, othercols) 
  SELECT ProductID, othercols 
  FROM @tvp AS t 
  WHERE NOT EXISTS 
  (
    SELECT 1 FROM @exist 
    WHERE ProductID = t.ProductID
  );

COMMIT TRANSACTION;

W obu przypadkach najpierw wykonujesz aktualizację, w przeciwnym razie zaktualizujesz wszystkie wstawione przed chwilą wiersze, co byłoby marnotrawstwem.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. izolowanie podciągu w ciągu przed symbolem w SQL Server 2008

  2. Konwertuj zapytanie SQL Server na MySQL

  3. Przełączanie partycji w SQL Server (T-SQL)

  4. Pobierz definicję kolumny dla zestawu wyników procedury składowanej

  5. Odpowiednik MySQL ON DUPLICATE KEY UPDATE w Sql Server