Использование условия if в SQL Server для вставки - PullRequest
0 голосов
/ 12 октября 2018

В моем коде есть следующее утверждение

INSERT INTO #TProductSales (ProductID, StockQTY, ETA1) 
VALUES (@ProductID, @StockQTY, @ETA1)

Я хочу сделать что-то вроде:

IF @ProductID exists THEN 
   UPDATE #TProductSales 
ELSE 
   INSERT INTO #TProductSales

Есть ли способ, которым я могу это сделать?

Ответы [ 3 ]

0 голосов
/ 12 октября 2018

Шаблон (без обработки ошибок):

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, вам потребуетсядобавить подсказки о блокировке, чтобы сделать это безопасным / сериализуемым в любом случае ), но вы можете отстать, в зависимости от того, сколько раз вы обновляете существующие строки и вставляете новые.Это может привести к большому количеству дополнительных операций ввода-вывода.

Люди, вероятно, скажут вам использовать MERGE (что на самом деле является множеством скрытых операций, и также необходимо защитить с помощью сериализуемых), я призываю вас не делать этого.Выкладываю, почему здесь:

Для многорядного шаблона (например, 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;

В любом случае вы сначала выполняете обновление, в противном случае вы обновите все только что вставленные строки, что было бы бесполезно.

0 голосов
/ 12 октября 2018

Мне лично нравится создавать переменную таблицы или временную таблицу для хранения значений, а затем выполнять обновление / вставку, но обычно я выполняю массовую вставку / обновления.Что хорошо в этом шаблоне, так это то, что он работает для нескольких записей без избыточности во вставках / обновлениях.

DECLARE @Tbl TABLE (
    StockQty INT,
    ETA1 DATETIME,
    ProductID INT
)

INSERT INTO @Tbl (StockQty,ETA1,ProductID)
    SELECT @StockQty AS StockQty ,@ETA1 AS ETA1,@ProductID AS ProductID

UPDATE tps
SET   StockQty = tmp.StockQty
    , tmp.ETA1 = tmp.ETA1
FROM #TProductSales tps
INNER JOIN @Tbl tmp ON tmp.ProductID=tps.ProductID

INSERT INTO #TProductSales(StockQty,ETA1,ProductID)
    SELECT
        tmp.StockQty,tmp.ETA1,tmp.ProductID
    FROM @Tbl tmp
    LEFT JOIN #TProductSales tps ON tps.ProductID=tmp.ProductID
    WHERE tps.ProductID IS NULL
0 голосов
/ 12 октября 2018

Вы можете использовать что-то вроде:

IF EXISTS( SELECT NULL FROM  #TProductSales WHERE ProductID = @ProductID)
     UPDATE #TProductSales SET StockQTY = @StockQTY, ETA1 = @ETA1 WHERE ProductID = @ProductID
ELSE
     INSERT INTO #TProductSales(ProductID,StockQTY,ETA1) VALUES(@ProductID,@StockQTY,@ETA1)
...