代码之家  ›  专栏  ›  技术社区  ›  Michel

sql server:在转换中的这种嵌套是否足以从数据库中获取唯一的数字?

  •  1
  • Michel  · 技术社区  · 14 年前

    我想从表中生成一个唯一的数字。 当然,它必须是线程安全的,所以当我检查最后一个数字并获取“3”,然后将“4”存储在数据库中时,我不希望其他任何人仅在这两个操作之间(获取数字并将其存储在数据库中较高的位置)也获取“3”,然后存储“4”

    所以我想,把它放在这样的交易中:

    begin transaction
            declare @maxNum int
            select @maxNum = MAX(SequenceNumber) from invoice
                where YEAR = @year
            if @maxNum is null
            begin
                set @maxNum = 0
            end
            set @maxNum = @maxNum + 1
            INSERT INTO [Invoice]
               ([Year]
               ,[SequenceNumber]
               ,[DateCreated])
         VALUES
               (@year
               ,@maxNum
               ,GETUTCDATE()
    )
    
        commit transaction
    
        return @maxNum
    

    但我想知道,这是否足以让它成为一笔交易? 我的第一个想法是:它锁定这个sp供其他人使用,但这是正确的吗?sql server如何知道在第一步锁定什么?

    这个建筑能保证没有人会做 select @maxnum 就在我离开的时候 updating the @maxnum 值,此时收到相同的@ maxnum 因为我这样做,我有麻烦了。

    我希望你能理解我想要实现的目标,也希望你知道我是否选择了正确的解决方案。

    编辑: 也被描述为“如何单线程存储过程”

    3 回复  |  直到 14 年前
        1
  •  5
  •   marc_s    14 年前

    如果要将年份和序列号存储在数据库中,并从中创建发票号,我将使用:

    • InvoiceYear 列(可以计算为 YEAR(InvoiceDate) )
    • 一个 InvoiceID INT IDENTITY 每年可以重置为1的列
    • 创建计算列 InvoiceNumber 作为:

      ALTER TABLE dbo.InvoiceTable
         ADD InvoiceNumber AS CAST(InvoiceYear AS VARCHAR(4)) + '-' +
                 RIGHT('000000' + CAST(InvoiceID AS VARCHAR(6)), 6) PERSISTED
      

    这样,您可以自动获取发票号码:

    2010-000001
    ......
    2010-001234
    ......
    2010-123456
    

    当然,如果您需要超过6位数(100万张发票),只需调整 RIGHT() CAST() 对…的陈述 InvoiceID 列。

    而且,因为这是一个 坚持 计算列,您可以为其编制索引以进行快速检索。

    这种方式: 不必担心并发性、存储过程、事务和诸如此类的事情—SQL Server将免费为您做到这一点!

        2
  •  2
  •   GSerg    14 年前

    不,这还不够。由select设置的共享锁不会阻止任何人同时读取相同的值。

    更改此:

    select @maxNum = MAX(SequenceNumber) from invoice where YEAR = @year
    

    对此:

    select @maxNum = MAX(SequenceNumber) from invoice with (updlock, holdlock) where YEAR = @year
    

    这样就可以用更新锁替换共享锁,并且两个更新锁不兼容。
    这个 holdlock 意味着锁将一直保持到事务结束。所以您仍然需要事务位。

    请注意,如果有其他过程也希望执行更新,则此操作将不会有帮助。如果另一个过程在不提供 updlock 提示,它仍然可以读取计数器的上一个值。这可能是一件好事,因为在其他读者不打算稍后进行更新的情况下,它可以提高并发性,但也可能不是您想要的,在这种情况下,可以更新所有要使用的过程 上钩 ,或使用 xlock 而是放置一个独占锁,与共享锁不兼容。

        3
  •  1
  •   Michel    14 年前

    结果,我不想锁定表,我只想一次执行一个存储过程。 在C代码中,我会在另一个对象上加一个锁,这就是这里讨论的 http://www.sqlservercentral.com/Forums/Topic357663-8-1.aspx

    所以我就用这个

    declare @Result int
    EXEC @Result =
    sp_getapplock @Resource = 'holdit1', @LockMode = 'Exclusive', @LockTimeout = 10000 --Time to wait for the lock
    IF @Result < 0
    BEGIN
    ROLLBACK TRAN
    RAISERROR('Procedure Already Running for holdit1 - Concurrent execution is not supported.',16,9)
    RETURN(-1)
    END
    

    其中“holdit1”只是锁的名称。 @result 如果成功获取锁,则返回0或1(其中一个是当它立即成功时,另一个是当您在等待时获取锁时)