仅当使用列列表并且IDENTITY_INSERT为ON时,才能指定表中Identity列的显式值。


187

我正在尝试执行此查询

INSERT INTO dbo.tbl_A_archive
  SELECT *
  FROM SERVER0031.DB.dbo.tbl_A

但即使我跑了

set identity_insert dbo.tbl_A_archive on

我收到此错误消息

仅当使用列列表且IDENTITY_INSERT为ON时,才能为表'dbo.tbl_A_archive'中的identity列指定一个显式值。

tbl_A是一个行和宽的巨大表,即它有很多列。我不想手动键入所有列。我怎样才能使它工作?


我已经设置了一个链接服务器!
jhowe 2010年

4
我也有这个问题,除了“插入X select * Y”不是问题,直到我更改了表架构
FistOfFury

Answers:


78

摘要

除非您使用列列表,否则SQL Server不允许您在标识列中插入显式值。因此,您有以下选择:

  1. 制作列列表(手动或使用工具,请参见下文)

要么

  1. 将身份列放在tbl_A_archive常规的非身份列中:如果表是存档表,并且始终为身份列指定一个显式值,为什么还要使用身份列呢?只需使用常规int即可。

解决方案1的详细信息

代替

SET IDENTITY_INSERT archive_table ON;

INSERT INTO archive_table
  SELECT *
  FROM source_table;

SET IDENTITY_INSERT archive_table OFF;

你需要写

SET IDENTITY_INSERT archive_table ON;

INSERT INTO archive_table (field1, field2, ...)
  SELECT field1, field2, ...
  FROM source_table;

SET IDENTITY_INSERT archive_table OFF;

field1, field2, ...包含在表中的所有列的名称。如果要自动生成该列列表,请查看Dave的答案Andomar的答案


解决方案2的详细信息

不幸的是,不可能仅将标识int列的类型“更改”为非标识int列。基本上,您有以下选择:

  • 如果存档表尚不包含数据,请删除该列并添加一个没有标识的新列。

要么

  • 使用SQL Server Management Studio将存档表中标识列的Identity Specification/ (Is Identity)属性设置为No。在幕后,这将创建一个脚本来重新创建表,并复制现有的数据,因此,要做到这一点,你还需要取消设置Tools/ Options/ Designers/ Table and Database Designers/ Prevent saving changes that require table re-creation

要么


1
最根本的解决方案:如果该列具有“ IDENTITY(1,1)”或类似的内容,他也将需要暂时将其删除。
Aleksandr Khomenko 2016年

1
@AleksandrKhomenko:是的,这就是我的意思,即“使其成为常规(非标识)int列”
Heinzi '16

1
对困惑感到抱歉。我的意思是实际上他也需要删除自动增量属性。对于SQL Server,它称为“ IDENTITY(1,1)”-您的答案是绝对正确的。但是,MySQL和Oracle拥有它的另一个命令(连带不明显,请看看w3schools.com/sql/sql_autoincrement.asp
亚历山大Khomenko

2
缺少详细信息。我的理解可能是死记硬背一些,但它是无稽之谈别人
DJ。

1
@DJ .:我添加了一些细节。
Heinzi

332
SET IDENTITY_INSERT tableA ON

您必须为INSERT语句创建一个列列表:

INSERT Into tableA ([id], [c2], [c3], [c4], [c5] ) 
SELECT [id], [c2], [c3], [c4], [c5] FROM tableB

不像“将INSERT插入表A SELECT ........”

SET IDENTITY_INSERT tableA OFF

17
+1 ...您只需要查询的SELECT子句中的显式列。您可以在查询的INSERT子句中保留星号
MacGyver 2013年

8
...除非目标中有一个Identity列,而源表中没有一个Identity列
MacGyver,2013年

1
这个答案比Heinzi更为明确,因为它提到SET IDENTITY_INSERT
Marty

4
+1就像魅力一样!但是,“您可以在查询的INSERT子句中保留一个星号”在这里不起作用。
Shai Alon

1
当select子句中的列与目标表不匹配时,出现此错误。确保这一点,该插入内容对我
何塞(Jose)

39

如果您使用的是SQL Server Management Studio,则无需自己键入列列表-只需在对象资源管理器中右键单击该表,然后选择Script Table as- > SELECT to- > New Query Editor Window

如果不是这样,那么类似以下的查询应该可以作为起点:

SELECT SUBSTRING(
    (SELECT ', ' + QUOTENAME(COLUMN_NAME)
        FROM INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_NAME = 'tbl_A'
        ORDER BY ORDINAL_POSITION
        FOR XML path('')),
    3,
    200000);

2
可以选择一个变量并将其用于动态SQL查询。你救了我的日子。非常感谢!
Pawel Cioch 2015年


哇。我拼命寻找这个动态建立我的查询。很惊讶在这里看到这个。谢了哥们。
Yasin Bilir'9

23

同意海因兹的回答。对于第二个选项,以下是一个查询,该查询生成以逗号分隔的表中的列的列表:

select name + ', ' as [text()] 
from sys.columns 
where object_id = object_id('YourTable') 
for xml path('')

对于大表,这可以节省很多打字工作:)


@谢谢,真的很有帮助。:)
Chirag Thakar's

有时您只需要一个务实的解决方案。谢谢!
Tobias Feil

15

如果“存档”表是您主表的精确副本,那么我只建议您删除ID是identiy列的事实。这样,您就可以插入它们。

或者,您可以使用以下语句为表允许和禁止标识插入

SET IDENTITY_INSERT tbl_A_archive ON
--Your inserts here
SET IDENTITY_INSERT tbl_A_archive OFF

最后,如果您需要标识列按原样工作,则始终可以运行存储的proc。

sp_columns tbl_A_archive 

这将返回表中的所有列,然后您可以将其剪切并粘贴到查询中。(这几乎总是比使用*好)


11

对于SQL语句,还必须指定列列表。例如。

INSERT INTO tbl (idcol1,col2) VALUES ( value1,value2)

代替

INSERT INTO tbl VALUES ( value1,value2)

2
OP的声明不是这种情况。如果INSERT INTO后跟SELECT语句,则不需要VALUES。请修改或删除您的答案。
加里

4

两者都可以使用,但是如果您仍然使用#1出错,请继续使用#2

1)

SET IDENTITY_INSERT customers ON
GO
insert into dbo.tbl_A_archive(id, ...)
SELECT Id, ...
FROM SERVER0031.DB.dbo.tbl_A

2)

SET IDENTITY_INSERT customers ON
GO
insert into dbo.tbl_A_archive(id, ...)
VALUES(@Id,....)

4
如果OP插入多个记录,这确实不是走的路。您将忽略“ tbl_A是行和宽的巨大表,即它具有很多列。我不想手动键入所有列。”
加里

4

为了将所有列名填充到一个用逗号分隔的列表中,以用于针对此问题提到的解决方案的Select语句,我使用以下选项,因为它们比此处的大多数答复都更冗长。尽管,这里的大多数响应仍然是完全可以接受的。

1)

SELECT column_name + ',' 
FROM   information_schema.columns 
WHERE  table_name = 'YourTable'

2)如果您有SQL Server SSMS,这可能是创建列的最简单方法。

1)转到对象资源管理器中的表,然后单击表名左侧的+或双击表名以打开子列表。

2)将列子文件夹拖到主查询区域,它将为您自动粘贴整个列列表。


3

如果有“标识”列,则必须指定要插入的列名称。因此,命令如下所示:

SET IDENTITY_INSERT DuplicateTable ON

INSERT Into DuplicateTable ([IdentityColumn], [Column2], [Column3], [Column4] ) 
SELECT [IdentityColumn], [Column2], [Column3], [Column4] FROM MainTable

SET IDENTITY_INSERT DuplicateTable OFF

如果您的表有许多列,则使用此命令获取这些列的名称。

SELECT column_name + ','
FROM   information_schema.columns 
WHERE  table_name = 'TableName'
for xml path('')

(删除最后一个逗号(',')之后)只需复制过去的列名即可。


3

这应该工作。我刚遇到您的问题:

SET IDENTITY_INSERT dbo.tbl_A_archive ON;
INSERT INTO     dbo.tbl_A_archive (IdColumn,OtherColumn1,OtherColumn2,...)
SELECT  *
FROM        SERVER0031.DB.dbo.tbl_A;
SET IDENTITY_INSERT dbo.tbl_A_archive OFF;

不幸的是,您似乎确实需要包含身份列的列列表才能插入指定身份的记录。但是,您不必在SELECT中列出列。正如@Dave Cluderay所建议的那样,这将为您复制和粘贴格式列表(如果少于200000个字符)。

由于在实例之间进行切换,因此添加了USE。

USE PES
SELECT SUBSTRING(
    (SELECT ', ' + QUOTENAME(COLUMN_NAME)
        FROM INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_NAME = 'Provider'
        ORDER BY ORDINAL_POSITION
        FOR XML path('')),
    3,
    200000);

3
SET IDENTITY_INSERT tableA ON

INSERT Into tableA ([id], [c2], [c3], [c4], [c5] ) 
SELECT [id], [c2], [c3], [c4], [c5] FROM tableB

不是这样

INSERT INTO tableA
SELECT * FROM tableB

SET IDENTITY_INSERT tableA OFF

2

此代码段显示了当身份主键列为ON时如何插入表中。

SET IDENTITY_INSERT [dbo].[Roles] ON
GO
insert into Roles (Id,Name) values(1,'Admin')
GO
insert into Roles (Id,Name) values(2,'User')
GO
SET IDENTITY_INSERT [dbo].[Roles] OFF
GO

1

如果您想通过存储过程将值从一个表插入到另一个表。我使用了thisthis,后者几乎就像Andomar的答案一样。

CREATE procedure [dbo].[RealTableMergeFromTemp]
    with execute as owner
AS
BEGIN
BEGIN TRANSACTION RealTableDataMerge
SET XACT_ABORT ON

    DECLARE @columnNameList nvarchar(MAX) =
     STUFF((select ',' + a.name
      from sys.all_columns a
      join sys.tables t on a.object_id = t.object_id 
       where t.object_id = object_id('[dbo].[RealTable]') 
    order by a.column_id
    for xml path ('')
    ),1,1,'')

    DECLARE @SQLCMD nvarchar(MAX) =N'INSERT INTO [dbo].[RealTable] (' + @columnNameList + N') SELECT * FROM [#Temp]'

    SET IDENTITY_INSERT [dbo].[RealTable] ON;
    exec(@sqlcmd)
    SET IDENTITY_INSERT [dbo].[RealTable] OFF

COMMIT TRANSACTION RealTableDataMerge
END

GO

0

有一个或多个具有自动增量属性的列,否则该属性的值将被计算为约束。您正在尝试修改该列。

有两种解决方法:1)明确提及其他列并仅设置其值,PrimaryKey或自动增量列值将自动设置。

2)您可以打开INDENTITY_INSERT,然后执行插入查询,最后关闭IDENTITY_INSERT。

建议:遵循第一步,因为这是一种更合适,更有效的方法。

有关更多信息,请阅读有关SQL-helper的本文


0

请确保从中选择记录的表中的列名,数据类型和顺序与目标表完全相同。唯一的区别应该是目标表的第一列是标识列,而不是源表中的列。

当我执行“ INSERT INTO table_Dest SELECT * FROM table_source_linked_server_excel”时,我遇到了类似的问题。该表有115列。

我有两个这样的表,用于将Excel(作为链接服务器)中的数据加载到数据库中的表中。在数据库表中,我添加了一个名为“ id”的身份列,该列在源Excel中不存在。对于一个表,查询成功运行,而在另一个表中,出现错误“仅当使用列列表且IDENTITY_INSERT为ON SQL Server时,才能指定表中Identity列的显式值”。这令人困惑,因为两个查询的场景完全相同。因此,我对此进行了调查,发现是在查询中因INSERT INTO .. SELECT *而出错的查询:

  1. 尽管值正确,但源表中的某些列名已被修改
  2. 除了SELECT *选择的实际数据列以外,还有一些额外的列。我通过使用源Excel表(在链接服务器下)的“脚本表为>选择为>新查询窗口”选项发现了这一点。Excel中的最后一列之后有一个隐藏的列,尽管它没有任何数据。我在源Excel表中删除了该列并将其保存。

进行上述两项更改后,对INSERT INTO ... SELECT *的查询成功运行。目标表中的标识列按预期为每个插入的行生成了标识值。

因此,即使目标表可能具有源表中不存在的标识列,但如果源和目标中的名称,数据类型和列顺序完全相同,则INSERT INTO .. SELECT *将成功运行。

希望它能帮助某人。


-1

我认为发生此错误是由于与表定义中的列数和插入查询中的列数不匹配。输入的值也将省略列的长度。因此,只需查看表定义即可解决此问题


为我工作:我忘了在设计中向表添加一列,在尝试其他答案之前先检查一下是一件好事。
Exel Gamboa
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.