sql >> Databasteknik >  >> RDS >> Sqlserver

Använda ett if-villkor i en insatt SQL Server

Mönstret är (utan felhantering):

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;

Du behöver inte göra en extra läsning av #temp-tabellen här. Du gör redan det genom att prova uppdateringen. För att skydda mot tävlingsförhållanden gör du samma sak som du skulle skydda ett block av två eller flera påståenden som du vill isolera:du skulle slå in det i en transaktion med en lämplig isoleringsnivå (kan troligtvis serialiseras här, även om det bara är är vettigt när vi inte pratar om en #temp-tabell, eftersom den per definition är serialiserad).

Du kommer inte längre fram genom att lägga till en IF EXISTS kontrollera (och du skulle behöva lägga till låstips för att göra det säkert / serialiserbart ändå), men du kan vara längre efter, beroende på hur många gånger du uppdaterar befintliga rader kontra infoga nya. Det kan lägga till en massa extra I/O.

Folk kommer förmodligen att säga åt dig att använda MERGE (vilket faktiskt är flera operationer bakom kulisserna, och som också måste skyddas med serialiserbar), jag uppmanar dig att inte göra det. Jag förklarar varför här:

  • Var försiktig med SQL Servers MERGE-uttalande

För ett mönster med flera rader (som en TVP) skulle jag hantera detta på ungefär samma sätt, men det finns inget praktiskt sätt att undvika den andra läsningen som du kan med ett enradsfodral. Och nej, MERGE undviker det inte heller.

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;

Tja, jag antar att det finns ett sätt att göra det, men jag har inte testat det här ordentligt:

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;

I båda fallen utför du uppdateringen först, annars uppdaterar du alla rader du precis infogade, vilket skulle vara slösaktigt.



  1. Leverantören är inte kompatibel med versionen av Oracle-klientfel när du använder Oracle.DataClient

  2. Hur man ansluter till MySQL med Python

  3. Infoga en bild i postgresql-databasen

  4. Vad är skillnaden mellan MyISAM och InnoDB?