C# T-SQL If语句问题
我有两张表:C# T-SQL If语句问题,c#,sql-server,tsql,if-statement,ado.net,C#,Sql Server,Tsql,If Statement,Ado.net,我有两张表:dbo.Videos和dbo.checkout dbo.Videos表包含视频列表,dbo.checkout表跟踪已签出的视频。 我的TSQL命令的目标是在dbo.checkout表中插入一个新行,包括VideoId,UserId,CheckoutDate 成功后,我想更新dbo.Videos,并仅当值大于0时,根据所选VideoID减小TotalCopies列值。 如果小于0,我想抛出异常。 两个表中的VideoID都通过外键链接。 但是,我在下面的语句中包含的IF语句抛出了一个错
dbo.Videos
和dbo.checkout
dbo.Videos
表包含视频列表,dbo.checkout
表跟踪已签出的视频。我的TSQL命令的目标是在
dbo.checkout
表中插入一个新行,包括VideoId
,UserId
,CheckoutDate
成功后,我想更新
dbo.Videos
,并仅当值大于0时,根据所选VideoID
减小TotalCopies
列值。如果小于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
你把它倒过来了。
您不需要将记录添加到
签出
,然后测试视频是否在视频
中,而需要先检查您是否有一份可以签出的副本。这就像你去任何一家商店买东西一样——首先你把商品从货架上拿下来,然后你才付钱。
如果产品不在货架上,你就没有必要付钱 第一版 您至少需要三个步骤才能正确操作:
首先,您要检查是否有副本要签出。
如果没有,您什么也不做,只需返回一条消息,说明没有免费副本可供签出。
如果有副本,则需要更新
视频
表(TotalCopies-=1)
最后-您需要将记录插入到签出
这里最重要的一点是,如果这些步骤中的任何一个都失败了,那么所有步骤都会失败——例如,如果由于某种原因您未能将行插入到签出
,您必须恢复您在视频
表上所做的更新,因为您无法完成该过程
这是您需要将整个流程包装到事务中的第一个原因。需要事务的第二个原因是,如果有副本要签出,则要避免测试与更新
视频
表之间的竞争条件。你可以在丹·古兹曼的博客文章中了解更多
说到这里,让我们展示一些代码:
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的全局变量返回受影响的行数(通常。链接中记录了一些例外情况)-使用该变量可以将测试部分与更新部分统一起来-如果更新未影响任何行,则让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
下面的
抛出一个错误。
什么错误删除IF
并使用更新dbo。视频设置TotalCopies=TotalCopies-1,其中VideoID=32,TotalCopies>0
。在插入签出之前是否也要检查TotalCopies
值?@mjwills好的,我理解。是的,这可能行得通,而且可能比我们两个答案都快。作为一个类比,为什么这个方法值得考虑@Nisarg——让我们想象一下,我妻子让我去买一些鸡蛋。一个选择是让她让我去商店,看看是否有鸡蛋(选择
),然后如果有鸡蛋,让店主把鸡蛋放在一边(锁
提示),然后回家。然后,我告诉她这里有鸡蛋,所以她让我去买一些(UPDATE
)。这确实有效。但如果她只是让我去买一些有库存的鸡蛋(更新
),并让她知道我是否成功(@@ROWCOUNT
)。