如何以编程方式更改标识列的值?


160

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

我在此表中有行,并且所有行都有其对应的ID自动递增值。

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

ID = ID + 1

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

无法更新标识列“ ID”。

我已经试过了:

    ALTER TABLE Test NOCHECK CONSTRAINT ALL 
    set identity_insert ID ON

但这不能解决问题。

我需要在此列设置标识,但是我还需要不时更改值。所以我的问题是如何完成这项任务。

Answers:


220

你需要

set identity_insert YourTable ON

然后删除您的行,然后以其他身份重新插入。

完成插入操作后,不要忘记关闭identity_insert

set identity_insert YourTable OFF

133
设置此项仅在插入数据时有效,而在更新时无效。UPDATE语句仍将失败。
侯赛因·朗塞维奇2011年

31
要进行更新,您需要删除并重新插入。没有别的办法了。
ashes999

1
@MartinSmith您的解决方案似乎太长了。能够分两个步骤进行操作(身份关闭,删除/插入,身份打开)更加有效。
ashes999 2013年

3
@ ashes999这是4个步骤,而不是2个步骤。我的答案仅是一个(创建表,切换,更新,切回,放下)。大概是您对这些步骤更加熟悉,因此看起来不太复杂。当涉及到许多行时,更新可能比删除插入更为有效。您还在哪里保存已删除的行?如果#temp要删除的表又是另一个步骤,则这里没有算在内。
马丁·史密斯

40

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

1
非常光滑 每天学习一些新知识(顺便说一句,我在SQLExpress上进行了测试;尽管SWITCH TO显然不使用分区)。请注意,表格必须完全匹配(索引,FK等)
Mark Sowul 2014年

1
当您在原始表上构建了PK,FK,索引和视图时,切换方法是否起作用?使用这种方法会不会破裂?
tj

1
在源表中创建临时表脚本时使用@tj,包括所有索引和约束。然后更改表的名称和约束,以避免冲突。除非建立索引或架构绑定,否则视图不会引起问题。
马丁·史密斯

如果表上有全文本搜索索引,则无法使用。我想可以删除它,然后再重新创建它,但是我还没有测试。
youen

26

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

然后运行查询:

UPDATE table SET Id = Id + 1

然后转到将autonumber属性添加回该列。


3
如果您手动进行更改,则可以要求管理员为更改生成SQL脚本(表设计器菜单,生成更改脚本)。对于此更改,它将创建一个新表并复制数据,然后删除原始表。
罗宾·贝内特

2
@tomaszs -这也是更有效,因为它并没有物理上的所有重建表(这将这样做两次)的代码示例是我已故的答案在这里
马丁·史密斯

您无法添加回身份。你呢?stackoverflow.com/questions/1049210/...
托马斯Kubes

谢谢。太完美了 我有1列在我的表,我是不知道的,所以我对我的表引导脚本有Id自动身份通过1出值
湿婆

18

首先,将IDENTITY_INSERT设置为打开或关闭将无法满足您的要求(用于插入新值,例如插入间隙)。

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


9

可以使用临时表来完成。

这个主意

  • 禁用约束(如果您的ID被外键引用)
  • 用新ID创建一个临时表
  • 删除表内容
  • 将数据从复制的表复制回原始表
  • 启用以前禁用的约束

SQL查询

假设您的test表有两个附加列(column2column3),并且有2个表的外键引用test被称为foreign_table1并且foreign_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

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


4

如果该列不是PK,则可以始终在表中使用递增的数字创建NEW列,删除原始列,然后将新列更改为旧列。

好奇为什么您可能需要这样做...我最想使用Identity列的是回填数字,而我最终还是使用DBCC CHECKIDENT(tablename,RESEED,newnextnumber)

祝好运!


1

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

祝好运

PS它不会失败,因为它正在运行的顺序试图将列表中的值更新为ID列表中已存在的项目?抓住稻草,也许增加行数+1,然后如果行的话减去行数:-S


1

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


1

您可以插入具有修改值的新行,然后删除旧行。以下示例将更改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

1

首先保存所有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时,无法为表“测试”中的标识列插入显式值。


0

我看到了一篇很好的文章,最后帮助了我..我试图在具有标识列的表中插入几行,但操作有误,必须删除。一旦删除行,我的标识列就会更改。我试图找到一种方法来更新插入的列,但是-不走运。因此,在Google上搜索时发现了一个链接..

  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-我更新.aspx的值


1
不过,不确定您是否真的回答了这个问题。
Andrew Barber 2012年

0

非常好的问题,我们首先需要该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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.