我想为我的应用程序中的某个功能生成自定义ID。这是执行此操作的过程:
CREATE PROCEDURE [dbo].[GetNextVendorInvoiceNo]
AS
BEGIN
Declare @StartingVendorInvoiceNo int = 0
Select @StartingVendorInvoiceNo = MAX(StartingVendorInvoiceNo) + 1
From SystemSettings WITH (TABLOCK)
Update SystemSettings
Set StartingVendorInvoiceNo = @StartingVendorInvoiceNo
Select @StartingVendorInvoiceNo
END
如果多个用户最终调用此过程,是否会出现任何问题。显然,我不希望多个用户拥有相同的ID。我正在使用TABLOCK,但不确定这是否正确,或者其他任何东西都是必需的。
答案 0 :(得分:1)
SQL Server 2012具有SEQUENCE
功能,对于多用户环境来说绝对安全。但它基于整数类型。
如果您有一个生成“下一个”ID的复杂过程,并且您希望确保在任何时刻只运行该过程的一个实例(以吞吐量为代价),我将使用`sp_getapplock' 。它易于使用和理解,您无需担心放置正确的查询提示。
您的程序如下:
CREATE PROCEDURE [dbo].[GetNextVendorInvoiceNo]
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
BEGIN TRANSACTION;
BEGIN TRY
DECLARE @VarLockResult int;
EXEC @VarLockResult = sp_getapplock
@Resource = 'GetNextVendorInvoiceNo_app_lock',
@LockMode = 'Exclusive',
@LockOwner = 'Transaction',
@LockTimeout = 60000,
@DbPrincipal = 'public';
Declare @StartingVendorInvoiceNo int = 0;
IF @VarLockResult >= 0
BEGIN
-- Acquired the lock, generate the "next" ID
Select @StartingVendorInvoiceNo = MAX(StartingVendorInvoiceNo) + 1
From SystemSettings;
Update SystemSettings
Set StartingVendorInvoiceNo = @StartingVendorInvoiceNo;
END ELSE BEGIN
-- TODO: handle the case when it takes too long to acquire the lock,
-- i.e. return some error code
-- For example, return 0
SET @StartingVendorInvoiceNo = 0;
END;
Select @StartingVendorInvoiceNo;
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION;
-- TODO: handle the error
END CATCH;
END
你写的简单TABLOCK
绝对不够。您需要将所有内容包装到事务中。然后确保锁定一直持续到事务结束,请参阅HOLDLOCK
。然后确保你获得的锁是正确的。您可能需要TABLOCKX
。因此,总体而言,您需要非常好地理解所有这些提示以及锁定的工作原理。使用这些提示肯定可以达到相同的效果。但是,如果程序中的逻辑比简化示例更复杂,那么很容易变得非常难看。
在我看来,sp_getapplock
很容易理解和维护。