【问题标题】:How to change identity column values programmatically?如何以编程方式更改标识列值?
【发布时间】:2009-04-15 12:43:47
【问题描述】:

我有一个 MS SQL 2005 数据库,其中有一个表 Test 和列 IDID 是一个标识列。

我在这个表中有行,它们都有对应的 ID 自动递增值。

现在我想像这样更改此表中的每个 ID:

ID = ID + 1

但是当我这样做时,我得到一个错误:

无法更新身份列“ID”。

我试过了:

    ALTER TABLE Test NOCHECK CONSTRAINT ALL 
    set identity_insert ID ON

但这并不能解决问题。

我需要为此列设置身份,但我也需要不时更改值。所以我的问题是如何完成这项任务。

【问题讨论】:

    标签: sql-server sql-server-2005 tsql identity sql-server-2005-express


    【解决方案1】:

    你需要

    set identity_insert YourTable ON
    

    然后删除您的行并以不同的身份重新插入。

    完成插入后,别忘了关闭 identity_insert

    set identity_insert YourTable OFF
    

    【讨论】:

    • 设置此项仅在插入数据时有效,在更新时无效。 UPDATE 语句仍然会失败。
    • 对于更新,您需要删除并重新插入。没有其他办法。
    • @MartinSmith 您的解决方案似乎太长了。能够分两步完成(关闭身份、删除/插入、打开身份)会更有效。
    • @ashes999 那是 4 个步骤而不是 2 个。我的答案只是多了一个(创建表、切换、更新、切换回、删除) 想必您对这些步骤更熟悉,所以看起来更少令人费解。当涉及许多行时,更新比删除插入更有效。另外,您在哪里保存已删除的行?如果您随后删除了 #temp 表,那是您未在此处计算的另一个步骤。
    【解决方案2】:

    IDENTITY 列值是不可变的。

    但是可以切换表元数据以删除 IDENTITY 属性,进行更新,然后切换回来。

    假设如下结构

    CREATE TABLE Test
    (
    ID INT IDENTITY(1,1) PRIMARY KEY,
    X VARCHAR(10)
    )
    
    INSERT INTO Test 
    OUTPUT INSERTED.*
    SELECT 'Foo' UNION ALL
    SELECT 'Bar' UNION ALL
    SELECT 'Baz'
    

    那你就可以了

    /*Define table with same structure but no IDENTITY*/
    CREATE TABLE Temp
    (
    ID INT PRIMARY KEY,
    X VARCHAR(10)
    )
    
    /*Switch table metadata to new structure*/
    ALTER TABLE Test SWITCH TO Temp;
    
    /*Do the update*/
    UPDATE Temp SET ID = ID + 1;
    
    /*Switch table metadata back*/
    ALTER TABLE Temp SWITCH TO Test;
    
    /*ID values have been updated*/
    SELECT *
    FROM Test
    
    /*Safety check in case error in preceding step*/
    IF NOT EXISTS(SELECT * FROM Temp)
        DROP TABLE Temp /*Drop obsolete table*/
    

    在 SQL Server 2012 中,可以有一个自动递增列,也可以使用 SEQUENCES 更直接地更新该列

    CREATE SEQUENCE Seq
        AS INT
        START WITH 1
        INCREMENT BY 1
    
    CREATE TABLE Test2
    (
    ID INT DEFAULT NEXT VALUE FOR Seq NOT NULL PRIMARY KEY,
    X VARCHAR(10)
    )
    
    INSERT INTO Test2(X)
    SELECT 'Foo' UNION ALL
    SELECT 'Bar' UNION ALL
    SELECT 'Baz'
    
    UPDATE Test2 SET ID+=1
    

    【讨论】:

    • 非常漂亮。每天都学习一些新东西(顺便说一句,我在 SQLExpress 上对此进行了测试;尽管 SWITCH TO 它显然不使用分区)。请注意,表必须非常精确地匹配(索引、FK 等)
    • 当您在原始表上构建 PK、FK、索引和视图时,切换方法是否有效?用这种方法会坏掉吗?
    • @t.j 在创建包含所有索引和约束的源表的临时表脚本时。然后更改表和约束的名称以避免冲突。除非索引或模式绑定,否则视图不会导致问题。
    • 如果表中有全文检索索引则不起作用。我想可以删除它并在之后重新创建它,但我还没有测试过。
    【解决方案3】:

    首先,IDENTITY_INSERT 的设置打开或关闭不适用于您的要求(它用于插入新值,例如堵塞间隙)。

    通过 GUI 执行操作只是创建一个临时表,将所有数据复制到一个没有标识字段的新表中,然后重命名该表。

    【讨论】:

      【解决方案4】:

      通过 SQL Server 2005 管理器中的 UI,更改列删除列的自动编号(标识)属性(通过右键单击选择表并选择“设计”)。

      然后运行您的查询:

      UPDATE table SET Id = Id + 1
      

      然后将自动编号属性添加回列。

      【讨论】:

      • 如果您手动进行更改,您可以要求经理为更改生成SQL脚本(表格设计器菜单,生成更改脚本)。对于此更改,它会创建一个新表并复制数据,然后删除原始表。
      • @tomaszs - 一个更高效的代码示例,因为它根本不物理重建表(这将这样做两次)在我的最新答案here
      • 您无法重新添加身份。你? stackoverflow.com/questions/1049210/…
      • 谢谢。这是完美的。我的表中有 1 行我不知道,因此我的表的引导脚本将 Id Auto Identity 值减 1。
      【解决方案5】:

      这可以使用临时表来完成。

      想法

      • 禁用约束(以防您的 id 被外键引用)
      • 使用新 ID 创建临时表
      • 删除表格内容
      • 将复制表中的数据复制回原始表中
      • 启用以前禁用的约束

      SQL 查询

      假设您的 test 表有两个附加列(column2column3),并且有 2 个表具有引用 test 的外键,称为 foreign_table1foreign_table2(因为现实生活中的问题是绝不简单)。

      alter table test nocheck constraint all;
      alter table foreign_table1 nocheck constraint all;
      alter table foreign_table2 nocheck constraint all;
      set identity_insert test on;
      
      select id + 1 as id, column2, column3 into test_copy from test v;
      delete from test;
      insert into test(id, column2, column3)
      select id, column2, column3 from test_copy
      
      alter table test check constraint all;
      alter table foreign_table1 check constraint all;
      alter table foreign_table2 check constraint all;
      set identity_insert test off;
      drop table test_copy;
      

      就是这样。

      【讨论】:

        【解决方案6】:

        首先保存所有 ID 并以编程方式将它们更改为您不需要的值,然后将它们从数据库中删除,然后使用类似的方法再次插入它们:

        use [Name.Database]
        go
        set identity_insert [Test] ON
        insert into [dbo].[Test]
                   ([Id])
             VALUES
                   (2)
        set identity_insert [Test] OFF
        

        用于批量插入:

        use [Name.Database]
        go
        set identity_insert [Test] ON
        BULK INSERT [Test]
        FROM 'C:\Users\Oscar\file.csv'
        WITH (FIELDTERMINATOR = ';',
              ROWTERMINATOR = '\n',
              KEEPIDENTITY)
        set identity_insert [Test] OFF
        

        file.csv 中的样本数据:

        2;
        3;
        4;
        5;
        6;
        

        如果您不将identity_insert 设置为关闭,您将收到以下错误:

        无法在表“测试”中为标识列插入显式值 IDENTITY_INSERT 设置为 OFF。

        【讨论】:

          【解决方案7】:

          如果该列不是 PK,您始终可以在表中创建一个带有递增数字的新列,删除原始列,然后将新列更改为旧列。

          很好奇你为什么需要这样做...我曾经对身份列最烦的事情是回填数字,我最终使用 DBCC CHECKIDENT (tablename,RESEED,newnextnumber)

          祝你好运!

          【讨论】:

            【解决方案8】:

            DBCC CHECKIDENT ('databasename.dbo.orders',RESEED, 999) 您可以使用此命令更改任何标识列编号,也可以从您想要的每个数字开始该字段编号。例如在我的命令中,我要求从 1000 (999+1) 开始 希望这就足够了……祝你好运

            【讨论】:

              【解决方案9】:

              身份修改可能会失败,具体取决于多种因素,主要是围绕与 id 列相关联的对象/关系。似乎 db 设计在这里是一个问题,因为 id 应该很少改变(我相信你有你的理由并且正在级联更改)。如果您确实需要不时更改 id,我建议您创建一个新的虚拟 id 列,该列不是您可以自己管理并从当前值生成的主键/自动编号。或者,如果您在允许插入身份时遇到问题,我的另一个建议是上述 Chrisotphers 的想法。

              祝你好运

              PS 它没有失败,因为它运行的顺序是试图将列表中的值更新为 id 列表中已经存在的项目?抓住稻草,也许加上行数+1,然后如果可行,减去行数:-S

              【讨论】:

                【解决方案10】:

                如果您需要偶尔更改 ID,最好不要使用标识列。过去,我们使用“计数器”表手动实现自动编号字段,该表跟踪每个表的下一个 ID。 IIRC 我们这样做是因为标识列导致 SQL2000 中的数据库损坏,但能够更改 ID 有时对测试很有用。

                【讨论】:

                  【解决方案11】:

                  您可以插入具有修改值的新行,然后删除旧行。以下示例将 ID 更改为与外键 PersonId

                  相同
                  SET IDENTITY_INSERT [PersonApiLogin] ON
                  
                  INSERT INTO [PersonApiLogin](
                         [Id]
                        ,[PersonId]
                        ,[ApiId]
                        ,[Hash]
                        ,[Password]
                        ,[SoftwareKey]
                        ,[LoggedIn]
                        ,[LastAccess])
                  SELECT [PersonId]
                        ,[PersonId]
                        ,[ApiId]
                        ,[Hash]
                        ,[Password]
                        ,[SoftwareKey]
                        ,[LoggedIn]
                        ,[LastAccess]
                  FROM [db304].[dbo].[PersonApiLogin]
                  GO
                  
                  DELETE FROM [PersonApiLogin]
                  WHERE [PersonId] <> ID
                  GO
                  SET IDENTITY_INSERT [PersonApiLogin] OFF
                  GO
                  

                  【讨论】:

                    【解决方案12】:

                    我看到了一篇好文章,它在最后一刻帮助了我。我试图在一个有标识列的表中插入几行,但做错了,不得不删除回来。一旦我删除了这些行,我的身份列就会改变。我试图找到一种方法来更新插入的列,但是 - 没有运气。所以,在谷歌上搜索时发现了一个链接..

                    1. 删除了错误插入的列
                    2. 使用身份打开/关闭强制插入(如下所述)

                    http://beyondrelational.com/modules/2/blogs/28/posts/10337/sql-server-how-do-i-insert-an-explicit-value-into-an-identity-column-how-do-i-update-the-value-of-an.aspx

                    【讨论】:

                    • 不过,不确定您是否真的回答了这里的问题。
                    【解决方案13】:

                    非常好的问题,首先我们需要开启特定表的 IDENTITY_INSERT,然后运行插入查询(必须指定列名)。

                    注意:编辑身份列后,不要忘记关闭 IDENTITY_INSERT。如果未完成,则无法编辑任何其他表的标识列。

                    SET IDENTITY_INSERT Emp_tb_gb_Menu ON
                         INSERT Emp_tb_gb_Menu(MenuID) VALUES (68)
                    SET IDENTITY_INSERT Emp_tb_gb_Menu OFF
                    

                    http://allinworld99.blogspot.com/2016/07/how-to-edit-identity-field-in-sql.html

                    【讨论】:

                      猜你喜欢
                      • 2013-12-23
                      • 2016-12-10
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 1970-01-01
                      • 2015-10-15
                      • 2017-08-07
                      • 2012-04-09
                      相关资源
                      最近更新 更多