【问题标题】:Implement Incrementing Counter in SQL在 SQL 中实现递增计数器
【发布时间】:2021-01-08 19:09:14
【问题描述】:

所以我需要实现一个自增列。我在一个表中有一个列,它有一个将充当计数器的 int,并且会有另一个表具有相应的行数,每一行都有它自己的计数器。我认为这实现起来相当简单,但是在做了一些研究之后,我发现了一些论坛,他们提到如果多个事务大致同时运行,即使通过运行选择、更新、并插入一笔交易

在我看来,如果我使用 OUTPUT 子句来获取第一个表正在更新的新值,我可以完全避免这个问题。

这就是我想到的 T-SQL。我的情况应该可以吗?

BEGIN TRANSACTION;

DECLARE @tblCtrs table (Ctr int)
DECLARE @CtrVal INT

UPDATE Table1
SET Ctr = Ctr+1
OUTPUT inserted.Ctr INTO @CtrVals
WHERE id=<whatever id value>

SET @CtrVal = (SELECT TOP(1) Ctr FROM @tblCtrs)

INSERT INTO TABLE2 (Ctr, ....)
VALUES( @CtrVal, ....)

COMMIT;

【问题讨论】:

    标签: sql-server tsql transactions increment


    【解决方案1】:

    如果我正确理解您的问题,那么担心并发事务可能会导致计数器值出现意外结果。您知道如何很好地管理计数器值,这只是其他事务是否可以同时读取/修改的问题。如果这种理解是正确的,那就是隔离级别的用途。取决于数据的访问/存储方式(例如,是否有多个记录具有相同的 或者是唯一的?)和应用程序要求(例如,如果 不是唯一的并且有插入由于时间原因未更新的新记录)您应该相应地SET TRANSACTION ISOLATION LEVEL

    【讨论】:

    • 谢谢,我想就是这样。看起来 REPEATABLE READ 是我需要的。非常感谢!
    • 好吧,我可能只是猜测,但在这种情况下这仍然是必要的吗?如果我有两个这样的事务同时运行,第一个将获取锁,然后读取原始值,递增它,存储它,然后将插入的表输出到表参数中。现在我想起来了,插入语句甚至不需要与更新在同一个事务中,只要我能够更新表值并获取新值而没有另一个更新干扰它,或者被受第一个更新事务的影响。应该可以吧?
    • 在同时插入行时,这是所需行为的问题。简单示例: BEGIN TRAN T1 SELECT * FROM MyTable WHERE ID='x' ... ... COMMIT 现在与 INSERT INTO MyTable (ID, ...) VALUES 同时('x'...) 根据隔离级别,插入可能会或可能不会被阻止,直到 T1 被提交或回滚。有必要吗? IE。 是否需要在该事务的时间范围内对 ID='x' 的 所有 记录进行操作,如果不需要,则可以?相同 ID 具有不同值的 ctr 可以吗?
    【解决方案2】:

    如果您能够使用 Identity 列,则可以使用 SCOPE_IDENTITY() 来查找最后插入的记录的 Identity 值,如下所示:

    DECLARE 
    @ParentData varchar(20) = 'ParentData1',
    @ChildData varchar(20) = 'ChildData1',
    @ParentID int
    
    DECLARE @ParentTable table (ParentId int IDENTITY(1, 1), ParentData Varchar(20))
    DECLARE @ChildTable table (ChildId int IDENTITY(1,1), ParentId int, ChildData varchar(20))
    
    INSERT INTO @ParentTable 
        (ParentData)
    VALUES
        (@ParentData)
    
    SET @ParentId = SCOPE_IDENTITY()
    
    INSERT INTO @ChildTable
        (ParentId, ChildData)
    VALUES
        (@ParentID, @ChildData)
    
    SET @ChildData = 'ChildData1,1'
    INSERT INTO @ChildTable
        (ParentId, ChildData)
    VALUES
        (@ParentID, @ChildData)
    
    SET @ParentData = 'ParentData2'
    SET @ChildData = 'ChildData2'
    
    INSERT INTO @ParentTable 
        (ParentData)
    VALUES
        (@ParentData)
    
    INSERT INTO @ChildTable
        (ParentId, ChildData)
    VALUES
        (SCOPE_IDENTITY(), @ChildData)
    
    
    SELECT * FROM @ParentTable
    SELECT * FROM @ChildTable
    

    【讨论】:

    • 很遗憾没有。我确实考虑了标识列,但问题是 Table1 会有一行,每个都有自己的计数器,所以 Table2 每个都有自己的序列。不过谢谢。我总是忘记 SCOPE_IDENTITY()
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-29
    • 2016-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    相关资源
    最近更新 更多