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

Posted

技术标签:

【中文标题】只有在使用列列表并且 IDENTITY_INSERT 为 ON SQL Server 时,才能为表中的标识列指定显式值【英文标题】:An explicit value for the identity column in table can only be specified when a column list is used and IDENTITY_INSERT is ON SQL Server 【发布时间】:2011-01-01 14:28:57 【问题描述】:

我正在尝试做这个查询

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 在行和宽度上是一个巨大的表格,即它有很多列。我不想手动输入所有列。我怎样才能让它工作?

【问题讨论】:

对了,我已经设置了链接服务器! 我也遇到了这个问题,除了在我更改表架构之前“插入 X 选择 * Y”不是问题 【参考方案1】:
SET IDENTITY_INSERT tableA ON

您必须为您的 INSERT 语句制作一个列列表:

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

不像“INSERT Into tableA SELECT ........”

SET IDENTITY_INSERT tableA OFF

【讨论】:

+1 ... 您只需要查询的 SELECT 子句中的显式列。您可以在查询的 INSERT 子句中保留一个星号 ...除非目标有标识列,而源表没有标识列 这个答案比 Heinzi 更清楚一点,因为它提到了 SET IDENTITY_INSERT +1 像魅力一样工作!但是,“您可以在查询的 INSERT 子句中保留星号”在这里不起作用。 当 select 子句中的列与目标表不匹配时,我收到此错误。确保这一点,插入对我有用【参考方案2】:

总结

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

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

    使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's answer 或 Andomar's answer。


解决方案 2 的详细信息

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

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

使用 SQL Server Management Studio 将存档表中标识列的 Identity Specification/(Is Identity) 属性设置为 No。在幕后,这将创建一个脚本来重新创建表并复制现有数据,因此,为此,您还需要取消设置 Tools/Options/Designers/Table and Database Designers/@987654336 @。

使用此答案中描述的解决方法之一:Remove Identity from a column in a table

【讨论】:

到你的底部解决方案:如果列有“IDENTITY (1, 1)”或类似的东西,他也需要临时删除它。 @AleksandrKhomenko:是的,这就是我所说的“使 [it] 成为常规 (non-identity) int 列” 的意思。。跨度> 很抱歉给您带来了困惑。我的意思是他也需要删除 auto-increment 属性。在 SQL-Server 的情况下,它调用“IDENTITY (1, 1)”——你的答案是绝对正确的。但是 mysql 和 Oracle 有另一个命令(它变得不明显,请看w3schools.com/sql/sql_autoincrement.asp) 这里缺少细节。我知道这对某些人来说可能是死记硬背,但对其他人来说是无稽之谈 @DJ.:我添加了一些细节。【参考方案3】:

如果您使用的是 SQL Server Management Studio,则不必自己键入列列表 - 只需右键单击 Object Explorer 中的表并选择 Script Table as -> SELECT to -> 新建查询编辑器窗口

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

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

【讨论】:

这个可以选择成变量,用于动态SQL查询。你拯救了我的一天。非常感谢! 谢谢!我用它做了这个gist.github.com/timabell/0ddd6a69565593f907c7 哇。我一直在寻找这个来动态构建我的查询。很惊讶在这里看到这个。谢谢老兄。【参考方案4】:

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

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

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

【讨论】:

@谢谢,真的很有帮助。 :) 有时您只需要一个务实的解决方案。谢谢!【参考方案5】:

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

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

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

最后,如果您需要身份列按原样工作,那么您可以随时运行存储的过程。

sp_columns tbl_A_archive 

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

【讨论】:

【参考方案6】:

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

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

而不是

INSERT INTO tbl VALUES ( value1,value2)

【讨论】:

OP 的声明并非如此。如果 INSERT INTO 后跟 SELECT 语句,则不需要 VALUES。请修改或删除您的答案。【参考方案7】:

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

【讨论】:

如果 OP 插入多条记录,这确实不是可行的方法。您忽略了“tbl_A 在行和宽度上是一个巨大的表,即它有很多列。我不想手动输入所有列。”【参考方案8】:
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

【讨论】:

【参考方案9】:

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

1)

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

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

1) 进入对象资源管理器中的表并单击表名左侧的+或双击表名以打开子列表。

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

【讨论】:

【参考方案10】:

如果有标识列,您必须指定要插入的列名。 所以命令将如下所示:

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

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

【讨论】:

【参考方案11】:

这应该可行。我刚刚遇到了你的问题:

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);

【讨论】:

【参考方案12】:

此代码 sn-p 显示了当身份主键列为 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

【讨论】:

【参考方案13】:

如果您想通过存储过程将值从一个表插入到另一个表中。我使用了this 和this,后者几乎就像安多玛的答案。

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

【讨论】:

【参考方案14】:

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

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

我有两个这样的表,我将数据从 Excel(作为链接服务器)加载到数据库中的表中。在数据库表中,我添加了一个名为“id”的标识列,该列在源 Excel 中没有。对于一个表,查询运行成功,而在另一个表中,我收到错误“表中标识列的显式值只能在使用列列表并且 IDENTITY_INSERT 在 SQL Server 上时指定”。这令人费解,因为这两个查询的场景完全相同。所以我对此进行了调查,我发现在查询中我遇到错误 INSERT INTO .. SELECT *:

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

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

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

希望它对某人有所帮助。

【讨论】:

只是为了我给你1UP的努力【参考方案15】:

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

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

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

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

更多信息请阅读this article on SQL-helper。

【讨论】:

【参考方案16】:

我使用以下内容创建一个与表完全相同但没有标识的临时:

SELECT TOP 0 CONVERT(INT,0)myid,* INTO #temp FROM originaltable

ALTER TABLE #temp DROP COLUMN id

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

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

【讨论】:

【参考方案17】:

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

【讨论】:

为我工作:我忘记在设计中的表格中添加一列,在尝试其他答案之前先检查一下。

以上是关于只有在使用列列表并且 IDENTITY_INSERT 为 ON SQL Server 时,才能为表中的标识列指定显式值的主要内容,如果未能解决你的问题,请参考以下文章

按列表选择列(并且列是列表的子集)

我的(Vba)代码仅适用于列表中的1个变量,并且在列表框中使用多个变量时仅返回空白

仅当使用了列列表并且 IDENTITY_INSERT 为 ON 时,才能为表'xxxx'中的标识列指定显式值

仅当使用列列表并且 IDENTITY_INSERT 为 ON [重复] 时,才能指定表“客户”中标识列的显式值

如何在EXCEL中把两列表格里的数字合成一列并且中间用逗号隔开?

仅当使用了列列表并且 IDENTITY_INSERT 为 ON 时,才能为表'Address'中的标识列指