只有在使用列列表且IDENTITY_INSERT为ON时才能指定表中标识列的显式值SQL Server

时间:2010-01-05 10:45:11

标签: sql-server

我正在尝试进行此查询

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'中标识列的显式值。

tbl_A是一个行和宽度很大的表,即它有很多列。我不想手动输入所有列。我怎样才能让它发挥作用?

17 个答案:

答案 0 :(得分:285)

SET IDENTITY_INSERT tableA ON

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

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

不喜欢“INSERT Into table SELECT ........”

SET IDENTITY_INSERT tableA OFF

答案 1 :(得分:64)

好吧,错误信息基本上都说明了一切。您有以下选择:

  • 创建列列表(INFORMATION_SCHEMA.COLUMNS上的SELECT和优秀的文本编辑器或Andomar和Dave提出的解决方案可以帮助您)

OR

  • 使tbl_A_archive中的标识列成为常规(非标识)int列(因为它是一个存档表,为什么需要标识列?)。

答案 2 :(得分:35)

如果您使用的是SQL Server Management Studio,则不必自行键入列列表 - 只需右键单击对象资源管理器中的表,然后选择脚本表作为< / strong> - &gt; 选择 - &gt; 新查询编辑器窗口

如果不是,那么类似的查询应该有助于作为起点:

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

答案 3 :(得分:17)

同意Heinzi的回答。对于第一个选项,这是一个查询,它在表中生成以逗号分隔的列列表:

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

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

答案 4 :(得分:10)

如果“archive”表是您主表的精确副本,那么我建议您删除id是一个标识列的事实。这样它就可以让你插入它们。

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

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

最后,如果您需要标识列按原样运行,那么您始终可以只运行存储过程。

sp_columns tbl_A_archive 

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

更好

答案 5 :(得分:7)

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

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

而不是

INSERT INTO tbl VALUES ( value1,value2)

答案 6 :(得分:3)

为了将所有列名填充到逗号分隔的列表中,针对此问题提到的解决方案的Select语句,我使用以下选项,因为它们比Andomar的简洁一点。然而,Andomar仍然是完全可以接受的。

<强> 1)

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

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

  

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

     

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

答案 7 :(得分:2)

两者都可以,但是如果你仍然使用#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,....)

答案 8 :(得分:2)

这应该有效。我刚遇到您的问题:

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;

不幸的是,您似乎确实需要包含ID列的列列表才能插入用于指定Identity的记录。 但是,您不必在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);

答案 9 :(得分:1)

如果存在Identity列,则必须指定要插入的列名。 所以命令将如下所示:

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('')

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

答案 10 :(得分: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

答案 11 :(得分:1)

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

答案 12 :(得分:1)

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

当我执行“ 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 *将成功运行。相同。

希望它对某人有帮助。

答案 13 :(得分:0)

[This Image Shows How to Insert to Table When Identity Primary Key Column Is ON.] [此图像显示了身份主键列为ON时如何插入表格。]

答案 14 :(得分:0)

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

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

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

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

有关更多信息,请阅读this article on SQL-helper

答案 15 :(得分:0)

我使用以下命令创建与表完全相同的临时温度,但没有标识:

SELECT TOP 0 CONVERT(INT,0)myid,*从originaltable转换为#temp

ALTER TABLE #temp DROP COLUMN ID

EXEC tempdb.sys.sp_rename N'#temp.myid',N'id',N'COLUMN'

收到有关重命名的警告,但没什么大不了的。 我在生产类系统上使用它。帮助确保该副本将遵循将来对表的任何修改,并且所生成的临时文件能够使任务中的行获得更多的次数。请注意,PK约束也已删除-如果需要,可以在末尾添加。

答案 16 :(得分:-3)

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