[英]T-SQL If Statement issue
我有2個表: dbo.Videos
和dbo.Checkouts
。
dbo.Videos
表包含視頻列表,而dbo.Checkouts
表則跟蹤已簽出的視頻。
我的TSQL命令的目標是在dbo.Checkouts
表中插入新行,包括VideoId
, UserId
, CheckoutDate
。
一旦成功,我當時想更新dbo.Videos
和遞減TotalCopies
基於列值VideoID
,如果該值大於0的唯一選擇。
如果小於0,我想拋出一個異常。
兩個表中的VideoID
通過外鍵鏈接。
但是,我在下面的語句中包含的IF語句引發錯誤。
INSERT INTO dbo.Checkouts (VideoId, UserId, CheckoutDate)
VALUES (32, 'b0281f0d-8398-4a27-ba92-828bfaa9f90e', CURRENT_TIMESTAMP)
IF (SELECT TotalCopies FROM dbo.Videos WHERE VideoId = 32) > 0
UPDATE dbo.Videos SET
TotalCopies = TotalCopies - 1
WHERE VideoID = 32
你已經倒退了。
而不是增加一個記錄的Checkouts
,然后測試,如果你有在視頻Videos
,你需要首先檢查你有一個副本,你可以退房。
就像您要從任何商店購買商品時一樣-首先您將產品下架,然后才付款。
如果產品不在貨架上,則無需您付款。
您至少需要三個步驟才能正確執行:
首先,檢查是否有要簽出的副本。
如果沒有,則您什么也不做,只需返回一條消息,指出沒有免費副本可以結帳。
如果有副本,則需要更新Video
表(TotalCopies-= 1),最后-您需要將記錄插入到checkouts
。
這里最重要的是,如果這些步驟中的任何一個失敗,那么所有這些都將失敗-例如,如果由於某種原因您無法將行插入checkouts
,則必須還原對Video
表所做的更新,因為您可以無法完成該過程。
這是您需要將整個流程包裝在事務中的第一個原因。
您需要進行交易的第二個原因是要避免測試之間是否存在競爭情況(如果有要結帳的副本)和video
表的更新。 您可以在Dan Guzman的有關條件INSERT / UPDATE Race Condition的博客文章中閱讀有關它的更多信息。
這么說了之后,讓我們展示一些代碼:
CREATE PROCEDURE VideoCheckout
(
@VideoId int,
@UserId uniqueIdentifier,
@Success bit OUTPUT
)
AS
SET XACT_ABORT ON
SET @Success = 0
BEGIN TRANSACTION
BEGIN TRY
DECLARE @NumberOfCopies int
SET @NumberOfCopies = ISNULL(
(
SELECT TotalCopies
FROM dbo.Videos WITH (UPDLOCK, HOLDLOCK)
WHERE VideoId = @VideoId
)
, 0)
IF @NumberOfCopies > 0
BEGIN
UPDATE dbo.Videos
SET TotalCopies = TotalCopies - 1
WHERE VideoId = @VideoId;
INSERT INTO dbo.Checkouts (VideoId, UserId, CheckoutDate)
VALUES (@VideoId, @UserId, CURRENT_TIMESTAMP)
SET @Success = 1
END
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION
END CATCH
GO
@@rowcount
版本: SQL Server的@@Rowcount
全局變量返回生效的行數(通常,鏈接中記錄了一些例外情況)-使用它可以將測試部分與更新部分統一-讓SQL Server報告更新是否生效沒有的任何行。 這使您可以編寫更簡單的SQL,並可能具有更好的性能。
CREATE PROCEDURE VideoCheckout
(
@VideoId int,
@UserId uniqueIdentifier,
@Success bit OUTPUT
)
AS
SET XACT_ABORT ON
SET @Success = 0
BEGIN TRANSACTION
BEGIN TRY
UPDATE dbo.Videos
SET TotalCopies = TotalCopies - 1
WHERE VideoId = @VideoId
AND TotalCopies > 0;
IF @@ROWCOUNT > 0
BEGIN
INSERT INTO dbo.Checkouts (VideoId, UserId, CheckoutDate)
VALUES (@VideoId, @UserId, CURRENT_TIMESTAMP)
SET @Success = 1
END
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF @@TRANCOUNT > 0
ROLLBACK TRANSACTION
END CATCH
GO
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.