【问题标题】:Generating custom ID for my application为我的应用程序生成自定义 ID
【发布时间】:2016-07-13 04:07:53
【问题描述】:

我想为我的应用程序中的一项功能生成自定义 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,但不确定这是正确的方法还是还需要其他任何东西。

【问题讨论】:

  • startingvendorinvoiceno 会改变吗?如果没有,为什么不能有一个标识列呢?
  • 因为这是一个更简单的整数版本。我计划稍后使用 varchar 来生成发票,正如我在问题 Identity is not possible 中所建议的那样。
  • 为什么不能进行身份验证?如果您所做的只是获取先前的最大值并添加一个,那么您所做的正是身份以一种效率较低的方式所做的事情。
  • @ZLK:你愿意看我的评论吗?我说我打算在 varchar 上执行此操作,因此发票编号可能是 ZXCVA - 都是独一无二的。我希望你明白这在 Identity 中是不可能的。如果您只是回答我的问题,而不是提出在我的情况下不可能的不同方式,我将不胜感激。
  • 那么现实地说,你所做的远比它需要的复杂得多(因为你可以在任何你需要显示“ZXCVA”的应用程序中添加“ZXCVA”,但有 ACTUAL字段为整数)。如果您的字段将是 varchar,那么它不像 max+1 那样简单,您必须拆分列中的值,找到整数,然后将其加 1,然后插入它...但实际上,您应该将其保留为 int。

标签: sql sql-server tsql stored-procedures sql-server-2012


【解决方案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 易于理解和维护。

【讨论】:

  • 超级工作。非常感谢!只是为了让我了解我的 TABLOCK 不起作用?
  • @TimLiberty,查看扩展答案。
  • 非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-08-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-21
  • 1970-01-01
相关资源
最近更新 更多