插入...选择*,如何忽略身份?
Posted
技术标签:
【中文标题】插入...选择*,如何忽略身份?【英文标题】:Insert into ... Select *, how to ignore identity? 【发布时间】:2008-09-23 18:00:56 【问题描述】:我有一个与具体表 T
完全相同结构的临时表。它是这样创建的:
select top 0 * into #tmp from T
处理完内容填入#tmp
后,我想将内容复制回T
,如下所示:
insert into T select * from #tmp
只要T
没有标识列就可以,但在我的情况下它有。当我复制到T
时,有什么方法可以忽略#tmp
中的自动增量标识列?我的动机是避免在“插入到”列表中拼出每个列名。
编辑:切换 identity_insert 不起作用,因为如果在我的脚本之外将行插入到 T
中,#tmp
中的 pkeys 可能与 T
中的 pkeys 发生冲突,那就是如果 #tmp
自动增加了 pkey首先要与 T 同步。
【问题讨论】:
你能解释一下为什么你首先想要一个临时表副本吗?可能有更好的方法来完成您正在寻找的东西。 1.它让我有机会在插入之前预览数据 2. 作为计算的一部分,我在临时表之间进行了连接;临时表允许我专注于我正在使用的确切数据集。我想就是这样。有什么建议/cmets? 您的目的仅仅是将已经存在的数据翻倍吗?还是在插入之前清除 T? 【参考方案1】:设置 IDENTITY_INSERT 开启
插入命令
设置 IDENTITY_INSERT 关闭
【讨论】:
如果我这样做可能会发生主键冲突,因为当 #tmp 准备好复制时,可能已将相同的键插入到 T 中。 啊,那行得通。直到 #tmp 准备好复制(我有很多 tmp 需要计算)以最小化锁定时间时,我才开始交易。我想现在这是合理的。 我收到此错误(带有标识列):只有在使用列列表且 IDENTITY_INSERT 为 ON 时,才能为表“myTable”中的标识列指定显式值。【参考方案2】:由于无论如何都会在插入过程中生成标识,您是否可以在将数据插入回 T 之前简单地从 #tmp 中删除此列?
alter table #tmp drop column id
UPD:这是我在 SQL Server 2008 中测试过的示例:
create table T(ID int identity(1,1) not null, Value nvarchar(50))
insert into T (Value) values (N'Hello T!')
select top 0 * into #tmp from T
alter table #tmp drop column ID
insert into #tmp (Value) values (N'Hello #tmp')
insert into T select * from #tmp
drop table #tmp
select * from T
drop table T
【讨论】:
从我的简单实验来看,这似乎可以完成工作:) 谢谢。由于源表#tmp 的列比目标 T 少 1 列,任何人都可以想到单元格内容可能未对齐的任何方式吗? 嗨,我试过了,但是插入到原始表中不起作用,它说“列名或提供的值的数量与表定义不匹配。”。即这个命令“insert into T select * from #tmp”不起作用。 @kristianp 我已经在答案中添加了一个完整的工作测试代码,但您可能正在尝试解决一个稍微不同的问题,例如您可能已经启用了身份插入。如果此问题的其他答案和搜索 SO 对您没有帮助,创建一个包含更多关于您的情况的详细信息的新问题可能是有意义的。【参考方案3】:查看答案here 和here:
select * into without_id from with_id
union all
select * from with_id where 1 = 0
原因:
当现有标识列被选入新表时,新列继承 IDENTITY 属性,除非以下条件之一为真:
SELECT 语句包含连接、GROUP BY 子句或聚合函数。 使用 UNION 连接多个 SELECT 语句。 标识列在选择列表中列出了不止一次。 标识列是表达式的一部分。 标识列来自远程数据源。如果其中任何一个条件为真,则创建的列不是 NULL,而不是继承 IDENTITY 属性。如果新表中需要标识列但这样的列不可用,或者您需要与源标识列不同的种子或增量值,请使用 IDENTITY 函数在选择列表中定义该列。请参阅下面示例部分中的“使用 IDENTITY 函数创建标识列”。
所有功劳归Eric Humphrey 和bernd_k
【讨论】:
这太棒了。正是原始海报所要求的。【参考方案4】:不适用于SELECT *
- 如果您选择除标识之外的每一列,那就没问题了。我能看到的唯一方法是您可以通过动态构建 INSERT
语句来做到这一点。
【讨论】:
【参考方案5】:只需列出您要重新插入的列,无论如何您都不应该使用 select *。如果不想输入,直接从对象浏览器中拖拽即可(如果展开表格拖拽word,columns,就会全部得到,删除id列即可)
【讨论】:
设计错误什么的,这个表有大约 80 列,其中 30 列已弃用。由于#tmp的创建方式,我认为使用select *作为例外是可以的。【参考方案6】:插入#Table SELECT MAX(Id) + ROW_NUMBER() OVER(ORDER BY Id)
【讨论】:
【参考方案7】:set identity_insert on
使用这个。
【讨论】:
【参考方案8】:“更新 T.ID = #tmp.ID”可能有效吗?
【讨论】:
【参考方案9】:它让我有机会在插入之前预览数据 我在计算过程中加入了临时表之间的连接;临时表允许我专注于我正在使用的确切数据集。我想就是这样。有什么建议/cmets?
对于第 1 部分,正如 Kolten 在其中一个 cmets 中所提到的,将您的语句封装在事务中并添加一个在显示和提交之间切换的参数将满足您的需求。对于第 2 部分,我需要看看您正在尝试什么“计算”。将数据限制在临时表中可能会使情况过于复杂。
【讨论】:
以上是关于插入...选择*,如何忽略身份?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 PL/SQL 块 (IF/THEN) 中执行“插入...选择...