如何使用 SELECT INTO 复制表但忽略 IDENTITY 属性?

ber*_*d_k 45 sql-server-2005 sql-server identity

我有一个带有标识列的表说:

create table with_id (
 id int identity(1,1),
 val varchar(30)
);
Run Code Online (Sandbox Code Playgroud)

众所周知,这

select * into copy_from_with_id_1 from with_id;
Run Code Online (Sandbox Code Playgroud)

导致 copy_from_with_id_1 在 id 上也带有标识。

以下堆栈溢出问题提到明确列出所有列。

咱们试试吧

select id, val into copy_from_with_id_2 from with_id;
Run Code Online (Sandbox Code Playgroud)

糟糕,即使在这种情况下 id 也是一个标识列。

我想要的是一张像

create table without_id (
 id int,
 val varchar(30)
);
Run Code Online (Sandbox Code Playgroud)

Eri*_*elp 59

网上书籍

new_table 的格式通过评估选择列表中的表达式来确定。new_table 中的列按照选择列表指定的顺序创建。new_table 中的每一列都与选择列表中的相应表达式具有相同的名称、数据类型、可为空性和值。除非在备注部分的“使用标识列”中定义的条件下,否则会转移列的 IDENTITY 属性

在页面下方:

将现有标识列选入新表时,新列将继承 IDENTITY 属性,除非满足以下条件之一:

  • SELECT 语句包含连接、GROUP BY 子句或聚合函数。
  • 多个 SELECT 语句使用 UNION 连接。
  • 标识列在选择列表中多次列出。
  • 标识列是表达式的一部分。
  • 标识列来自远程数据源。

如果这些条件中的任何一个为真,则将创建 NOT NULL 列,而不是继承 IDENTITY 属性。如果新表中需要标识列但此类列不可用,或者您需要与源标识列不同的种子或增量值,请使用 IDENTITY 函数在选择列表中定义该列。请参阅下面示例部分中的“使用 IDENTITY 函数创建标识列”。

所以......理论上你可以逃脱:

select id, val 
into copy_from_with_id_2 
from with_id

union all

select 0, 'test_row' 
where 1 = 0;
Run Code Online (Sandbox Code Playgroud)

注释此代码以解释它很重要,以免下次有人查看它时将其删除。


ber*_*d_k 35

受 Erics 回答的启发,我找到了以下解决方案,它仅取决于表名,不使用任何特定的列名:

select * into without_id from with_id where 1 = 0
union all
select * from with_id where 1 = 0
;
insert into without_id select * from with_id;
Run Code Online (Sandbox Code Playgroud)

编辑

甚至可以将其改进为

select * into without_id from with_id
union all
select * from with_id where 1 = 0
;
Run Code Online (Sandbox Code Playgroud)


And*_*y M 14

您可以使用联接一次性创建和填充新表:

SELECT
  t.*
INTO
  dbo.NewTable
FROM
  dbo.TableWithIdentity AS t
  LEFT JOIN dbo.TableWithIdentity ON 1 = 0
;
Run Code Online (Sandbox Code Playgroud)

由于1 = 0条件的原因,右侧将没有匹配项,从而防止左侧行的重复,并且由于这是一个外部联接,左侧行也不会被消除。最后,因为这是一个连接,所以消除了 IDENTITY 属性。

因此,仅选择左侧列将仅生成dbo.TableWithIdentity的精确副本,即仅在数据方面,即删除 IDENTITY 属性。

尽管如此Max Vernon在评论中提出了一个值得记住的有效观点。如果查看上述查询的执行计划:

执行计划

您会注意到在执行计划中只提到了一次源表。另一个实例已被优化器消除。

因此,如果优化器可以正确地确定计划中不需要连接的右侧,那么可以合理地期望在 SQL Server 的未来版本中它可能能够确定 IDENTITY 属性不需要也删除了,因为根据查询计划在源行集中不再有另一个 IDENTITY 列。这意味着上述查询可能会在某个时候停止按预期工作。

但是,正如ypercube正确指出的那样??,到目前为止,手册已经明确指出,如果有连接,则不保留 IDENTITY 属性:

将现有标识列选入新表时,新列将继承 IDENTITY 属性,除非 [...] [t] SELECT 语句包含联接。

因此,只要手册不断提及它,我们就可以放心,行为将保持不变。

荣誉对Shaneisypercube?用于在聊天中提出相关主题。


小智 7

试试这个代码..

SELECT isnull(Tablename_old.IDENTITYCOL + 0, -1) AS 'New Identity Column'
INTO   dbo.TableName_new
FROM   dbo.TableName_old 
Run Code Online (Sandbox Code Playgroud)

ISNULL调用可确保创建的新列具有可NOT NULL空性。