sql >> Databáze >  >> RDS >> Sqlserver

Použití podmínky if ve vložení SQL Server

Vzor je (bez zpracování chyb):

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;

Zde nemusíte provádět další čtení tabulky #temp. Už to děláte tak, že zkoušíte aktualizaci. Abyste se chránili před rasovými podmínkami, uděláte totéž, co byste chránili jakýkoli blok dvou nebo více příkazů, které chcete izolovat:zabalíte jej do transakce s vhodnou úrovní izolace (zde pravděpodobně serializovatelnou, i když všechny pouze dává smysl, když nehovoříme o #temp tabulce, protože ta je z definice serializovaná).

Přidáním IF EXISTS už nebudete dále napřed check (a museli byste přidat uzamykací rady, aby to bylo bezpečné / serializovatelné tak jako tak), ale můžete být dále pozadu, v závislosti na tom, kolikrát aktualizujete existující řádky vs. vkládání nových. To by mohlo přidat spoustu dalších I/O.

Lidé vám pravděpodobně řeknou, abyste použili MERGE (což je ve skutečnosti několik operací v zákulisí a také je třeba je chránit pomocí serializovatelných), vyzývám vás, abyste to nedělali. Zde uvádím proč:

  • Používejte opatrně s příkazem MERGE serveru SQL Server

U víceřadého vzoru (jako TVP) bych to řešil úplně stejně, ale neexistuje praktický způsob, jak se vyhnout druhému čtení, jako u jednořadého pouzdra. A ne, MERGE nevyhýbá se ani tomu.

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;

No, myslím, že existuje způsob, jak to udělat, ale důkladně jsem to netestoval:

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;

V obou případech nejprve provedete aktualizaci, jinak aktualizujete všechny řádky, které jste právě vložili, což by bylo zbytečné.



  1. jak vypočítat zůstatky v účetním softwaru pomocí postgres window funkce

  2. Jak spočítat všechny řádky při použití SELECT s LIMIT v dotazu MySQL?

  3. Najděte nejbližší zeměpisnou šířku/délku pomocí SQL dotazu

  4. PL/pgSQL kontroluje, zda řádek existuje