Sqlserver
 sql >> база данни >  >> RDS >> Sqlserver

Използване на условие if във вмъкнат SQL Server

Моделът е (без обработка на грешки):

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;

Тук не е необходимо да извършвате допълнително четене на таблицата #temp. Вече правите това, като изпробвате актуализацията. За да се предпазите от условия на състезание, вие правите същото, както бихте защитили всеки блок от две или повече изрази, които искате да изолирате:ще го обвиете в транзакция с подходящо ниво на изолация (вероятно да се сериализира тук, макар че всичко това е само има смисъл, когато не говорим за таблица #temp, тъй като тя по дефиниция е сериализирана).

Не сте по-напред, като добавите IF EXISTS проверете (и ще трябва да добавите заключващи подсказки, за да направите това безопасно / сериализиращо се така или иначе), но може да сте по-назад, в зависимост от това колко пъти актуализирате съществуващи редове спрямо вмъкване на нови. Това може да доведе до много допълнителни I/O.

Хората вероятно ще ви кажат да използвате MERGE (което всъщност е множество операции зад кулисите и също така трябва да бъде защитено с възможност за сериализиране), призовавам ви да не го правите. Излагам защо тук:

  • Внимавайте с изявлението MERGE на SQL Server

За многоредов модел (като TVP) бих се справил с това по същия начин, но няма практичен начин да избегнете второто четене, както можете с едноредовия случай. И не, MERGE също не го избягва.

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;

Е, предполагам, че има начин да го направя, но не съм го тествал обстойно:

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;

И в двата случая първо извършвате актуализацията, в противен случай ще актуализирате всички редове, които току-що вмъкнахте, което би било разточително.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Типове данни VARCHAR и NVARCHAR в SQL Server

  2. Научете как да използвате SQL Server Management Studio

  3. Как да вмъкна байт[] в колона VARBINARY на SQL сървър

  4. EXEC sp_executesql с множество параметри

  5. Как работи APPROX_COUNT_DISTINCT() в SQL Server