【问题标题】:SQL Server - Auto-incrementation that allows UPDATE statementsSQL Server - 允许 UPDATE 语句的自动增量
【发布时间】:2011-03-28 01:46:44
【问题描述】:

在我的数据库中添加一个项目时,我需要它来自动确定字段 DisplayOrder 的值。 Identity(自动增量)将是一个理想的解决方案,但我需要能够以编程方式更改(更新)DisplayOrder 列的值,而 Identity 似乎不允许这样做。目前,我使用以下代码:

CREATE PROCEDURE [dbo].[AddItem]

AS

DECLARE @DisplayOrder INT

SET @DisplayOrder = (SELECT MAX(DisplayOrder) FROM [dbo].[MyTable]) + 1

INSERT INTO [dbo].[MyTable] ( DisplayOrder ) VALUES ( @DisplayOrder )

这是做这件事的好方法还是有更好/更简单的方法?

【问题讨论】:

  • 我刚刚遇到了这个方法的一个问题: 如果表中没有行,MAX 返回 NULL。因此,此过程无法添加第一行,因为它会产生错误!
  • 在这里找到了修复:stackoverflow.com/questions/1688715/…

标签: sql-server tsql identity-column


【解决方案1】:

“Microsoft SQL Server 2008 内部:T-SQL 查询”中对此问题的解决方案

CREATE TABLE dbo.Sequence(
 val int IDENTITY (10000, 1) /*Seed this at whatever your current max value is*/
 )

GO

CREATE PROC dbo.GetSequence
@val AS int OUTPUT
AS
BEGIN TRAN
    SAVE TRAN S1
    INSERT INTO dbo.Sequence DEFAULT VALUES
    SET @val=SCOPE_IDENTITY()
    ROLLBACK TRAN S1 /*Rolls back just as far as the save point to prevent the 
                       sequence table filling up. The id allocated won't be reused*/
COMMIT TRAN

或者同一本书中的另一种更容易分配范围的替代方法。 (您需要考虑是从事务内部还是外部调用它——内部会阻塞其他并发事务,直到第一个事务提交)

CREATE TABLE dbo.Sequence2(
 val int 
 )

GO

INSERT INTO dbo.Sequence2 VALUES(10000);

GO

CREATE PROC dbo.GetSequence2
@val AS int OUTPUT,
@n as int =1
AS
UPDATE dbo.Sequence2 
SET @val = val = val + @n;

SET @val = @val - @n + 1; 

【讨论】:

    【解决方案2】:

    您可以将递增列设置为使用标识属性。然后,在需要向列中插入值的进程中,您可以在批处理中使用SET IDENITY_INSERT 命令。

    对于要使用标识属性的插入,您从插入语句的列列表中排除标识列:

    INSERT INTO [dbo].[MyTable] ( MyData ) VALUES ( @MyData )
    

    如果要在为标识列提供值的位置插入行,请使用以下命令:

    SET IDENTITY_INSERT MyTable ON
    
    INSERT INTO [dbo].[MyTable] ( DisplayOrder, MyData )
    VALUES ( @DisplayOrder, @MyData )
    
    SET IDENTITY_INSERT MyTable OFF
    

    您应该能够在没有任何其他步骤的情况下更新该列。

    您可能还想查看DBCC CHECKIDENT 命令。此命令将设置您的下一个身份值。如果您要插入下一个标识值可能不合适的行,则可以使用该命令设置新值。

    DECLARE @DisplayOrder INT
    
    SET @DisplayOrder = (SELECT MAX(DisplayOrder) FROM [dbo].[MyTable]) + 1
    
    DBCC CHECKIDENT (MyTable, RESEED, @DisplayOrder)
    

    【讨论】:

    • (顺便说一句,SET IDENITY_INSERT 中有错字)
    • 很高兴能提供帮助。我会为未来的读者更正错字。祝你好运。
    • 我刚刚注意到 IDENTITY_INSERT 似乎不允许身份列上的 UPDATE 语句(它只允许 INSERT 语句)。我需要另一个解决方案。
    【解决方案3】:

    这是我保留的解决方案:

    CREATE PROCEDURE [dbo].[AddItem]
    
    AS
    
    DECLARE @DisplayOrder INT
    
    BEGIN TRANSACTION
    
    SET @DisplayOrder = (SELECT ISNULL(MAX(DisplayOrder), 0) FROM [dbo].[MyTable]) + 1
    
    INSERT INTO [dbo].[MyTable] ( DisplayOrder ) VALUES ( @DisplayOrder )
    
    COMMIT TRANSACTION
    

    【讨论】:

    • 如果您同时执行AddItem,这可能会导致输入重复的DisplayOrders
    • 但是 SELECT 和 INSERT 语句包含在事务中。它不应该修复重复项吗?
    • 我认为这是一个想法,但如果没有任何隔离级别下的额外锁定提示,它就无法工作。由于两个并发选择不会相互阻塞并且都会读取相同的值。然后插入会成功,除非在我认为你会陷入死锁的可序列化级别。
    【解决方案4】:

    您应该做的一件事是添加命令,以便您的过程作为事务运行,否则同时运行的两个插入可能会在 DisplayOrder 中产生具有相同值的两行。

    这很容易实现:添加

    begin transaction
    

    在您的程序开始时,并且

    commit transaction
    

    在最后。

    【讨论】:

    • 这个答案似乎没有回答问题。
    • 它回答了关于建议的方法是否可以改进的问题。
    • 在事务中包装SELECTINSERT 不会阻止2个并发选择读取最大值
    【解决方案5】:

    你的方法很好(稍加修改)并且很简单。我会将它包装在@David Knell 所说的交易中。这将导致如下代码:

    CREATE PROCEDURE [dbo].[AddItem]
    
    AS
    
    DECLARE @DisplayOrder INT
    
    BEGIN TRANSACTION
    
    SET @DisplayOrder = (SELECT MAX(DisplayOrder) FROM [dbo].[MyTable]) + 1
    
    INSERT INTO [dbo].[MyTable] ( DisplayOrder ) VALUES ( @DisplayOrder )
    
    COMMIT TRANSACTION
    

    将您的 SELECT 和 INSERT 包装在事务中可确保您的 DisplayOrder 值不会被 AddItem 复制。如果您同时进行大量添加(每秒很多),MyTable 上可能会出现争用,但对于偶尔的插入,这不会有问题。

    【讨论】:

    • 在事务中包装SELECTINSERT 根本不能保证这一点。
    • 你是对的,我认为这个答案实际上有一个更清洁的解决方案:stackoverflow.com/questions/193257/…
    • 是的。我的回答中引用的书中也有一个版本。它处理数字块的分配比我选择的解决方案容易得多。该解决方案的问题是,如果从同一事务内部分配序列号,它将阻止任何并发插入,直到第一个事务完成,这可能是可取的,也可能不是可取的。身份表解决方案是非阻塞的。编辑:再看一遍,完全不一样。我会用另一个答案更新我的答案。
    猜你喜欢
    • 2011-10-20
    • 2021-01-07
    • 1970-01-01
    • 1970-01-01
    • 2013-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-26
    相关资源
    最近更新 更多