如何在SQL Server 2005中删除列的IDENTITY属性

问题描述 投票:9回答:5

我希望能够将具有标识列的表中的数据插入SQL Server 2005中的临时表中。

TSQL看起来像:

-- Create empty temp table
SELECT *
INTO #Tmp_MyTable
FROM MyTable
WHERE 1=0
...
WHILE ...
BEGIN
    ...
    INSERT INTO #Tmp_MyTable
    SELECT TOP (@n) *
    FROM MyTable
    ...

END

上面的代码使用身份列创建了#Tmp_Table,随后插入失败,并显示错误消息“仅当使用列列表且IDENTITY_INSERT为ON时,才能指定表'#Tmp_MyTable'中身份列的显式值。 “

TSQL中有没有办法删除临时表中列的标识属性而没有明确列出所有列?我特别想使用“ SELECT *”,以便如果将新列添加到MyTable,代码将继续工作。

我相信删除并重新创建该列将更改其位置,从而无法使用SELECT *。

更新:

我已尝试按照一个响应中的建议使用IDENTITY_INSERT。它不起作用-请参阅下面的复制内容。我在做什么错?

-- Create test table
CREATE TABLE [dbo].[TestTable](
    [ID] [numeric](18, 0) IDENTITY(1,1) NOT NULL,
    [Name] [varchar](50) NULL,
 CONSTRAINT [PK_TestTable] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
)
) 
GO
-- Insert some data
INSERT INTO TestTable
(Name)
SELECT 'One'
UNION ALL
SELECT 'Two'
UNION ALL
SELECT 'Three'
GO
-- Create empty temp table
SELECT *
INTO #Tmp
FROM TestTable
WHERE 1=0

SET IDENTITY_INSERT #Tmp ON -- I also tried OFF / ON
INSERT INTO #Tmp
SELECT TOP 1 * FROM TestTable

SET IDENTITY_INSERT #Tmp OFF 
GO
-- Drop test table
DROP TABLE [dbo].[TestTable]
GO

请注意,错误消息“表'#TmpMyTable'中的标识列的显式值只能指定使用列列表时并且IDENTITY_INSERT为ON。”-我特别不这样做不想使用如上所述的列列表。

更新2尝试过the suggestion from Mike,但这给出了相同的错误:

-- Create empty temp table
SELECT *
INTO #Tmp
FROM (SELECT
      m1.*
      FROM TestTable                 m1
          LEFT OUTER JOIN TestTable  m2 ON m1.ID=m2.ID
      WHERE 1=0
 ) dt

INSERT INTO #Tmp
SELECT TOP 1 * FROM TestTable

关于我为什么要这样做:MyTable是一个临时表,它可以包含大量要合并到另一个表中的行。我想处理登台表中的行,插入/更新我的主表,并在每个事务处理N行的循环中从登台表中删除它们。我意识到还有其他方法可以实现这一目标。

更新3

我无法使用Mike's solution,但是它建议使用以下解决方案:有效的前缀为非身份列,然后删除身份列:

SELECT CAST(1 AS NUMERIC(18,0)) AS ID2, *
INTO #Tmp
FROM TestTable
WHERE 1=0
ALTER TABLE #Tmp DROP COLUMN ID

INSERT INTO #Tmp
SELECT TOP 1 * FROM TestTable

Mike建议仅将键存储在临时表中也是一个好方法,尽管在这种特定情况下,我有理由希望将所有列都存储在临时表中。

sql sql-server-2005 tsql
5个回答
3
投票

如果您只是按照您的描述来处理行,那么最好只在临时表中选择前N个主键值,如:

CREATE TABLE #KeysToProcess
(
     TempID    int  not null primary key identity(1,1)
    ,YourKey1  int  not null
    ,YourKey2  int  not null
)

INSERT INTO #KeysToProcess (YourKey1,YourKey2)
SELECT TOP n YourKey1,YourKey2  FROM MyTable

键不应该经常更改(我希望),但是其他列对此这样做不会造成任何损害。

获取插入的@@ ROWCOUNT,您可以在TempID上进行一个简单的循环,其中从1到@@ ROWCOUNT

和/或

只需将#KeysToProcess加入您的MyKeys表中,就可以了,无需复制所有数据。

这在我的SQL Server 2005上运行良好,其中MyTable.MyKey是一个标识列。

-- Create empty temp table
SELECT *
INTO #TmpMikeMike
FROM (SELECT
      m1.*
      FROM MyTable                 m1
          LEFT OUTER JOIN MyTable  m2 ON m1.MyKey=m2.MyKey
      WHERE 1=0
 ) dt

INSERT INTO #TmpMike
SELECT TOP 1 * FROM MyTable

SELECT * from #TmpMike

编辑这项工作,没有错误...

-- Create empty temp table
SELECT *
INTO #Tmp_MyTable
FROM (SELECT
          m1.*
          FROM MyTable                 m1
              LEFT OUTER JOIN MyTable  m2 ON m1.KeyValue=m2.KeyValue
          WHERE 1=0
     ) dt
...
WHILE ...
BEGIN
    ...
    INSERT INTO #Tmp_MyTable
    SELECT TOP (@n) *
    FROM MyTable
    ...

END

但是,您真正的问题是什么?为什么在将“ *”插入此临时表时需要循环?您也许可以改变策略,并总体上提出更好的算法。


7
投票

您可以尝试

SET IDENTITY_INSERT #Tmp_MyTable ON 
-- ... do stuff
SET IDENTITY_INSERT #Tmp_MyTable OFF

即使具有标识列,也可以选择#Tmp_MyTable

但是此将不会起作用:

-- Create empty temp table
SELECT *
INTO #Tmp_MyTable
FROM MyTable
WHERE 1=0
...
WHILE ...
BEGIN
    ...
    SET IDENTITY_INSERT #Tmp_MyTable ON 

    INSERT INTO #Tmp_MyTable
    SELECT TOP (@n) *
    FROM MyTable

    SET IDENTITY_INSERT #Tmp_MyTable OFF 
    ...    
END

(导致错误“仅在使用列列表且IDENTITY_INSERT为ON时,才能指定表'#Tmp'中的身份列的显式值。”]

似乎没有办法真正删除该列-但这将改变OP提到的列顺序。丑陋的黑客:基于#Tmp_MyTable创建一个新表...

[我建议您编写一个存储过程,该存储过程基于具有相同列(顺序)但缺少identity属性的表名(MyTable)创建一个临时表。

您可以使用以下代码:

select t.name as tablename, typ.name as typename, c.*
from sys.columns c inner join
     sys.tables t on c.object_id = t.[object_id] inner join
     sys.types typ on c.system_type_id = typ.system_type_id
order by t.name, c.column_id

了解反射在TSQL中的工作方式。我相信您将不得不遍历有问题的表的列,并对生成的表执行动态(手工制作,存储在字符串中然后进行评估的)alter语句。

您介意在世界其他地方发布这样的存储过程吗?这个问题似乎在其他论坛上也出现了很多……


1
投票

[EDIT如Daren所建议的那样,切换IDENTITY_INSERT肯定是一种更优雅的方法,就我而言,我需要消除Identity列,以便将选定的数据重新插入源表中。

我解决这个问题的方法是像您一样创建临时表,显式删除Identity列,然后动态构建sql,这样我就有一个排除Identity列的列列表(如您的情况,如果对架构进行了更改,proc仍然可以正常工作),然后执行sql(这里是一个示例)>

declare @ret int
Select * into #sometemp from sometable
Where
id = @SomeVariable

Alter Table #sometemp Drop column SomeIdentity 

Select @SelectList = ''
Select @SelectList = @SelectList 
+ Coalesce( '[' + Column_name + ']' + ', ' ,'')
from information_schema.columns
where table_name = 'sometable'
and Column_Name <> 'SomeIdentity'

Set @SelectList = 'Insert into sometable (' 
+ Left(@SelectList, Len(@SelectList) -1) + ')'
Set @SelectList = @SelectList 
+ ' Select * from #sometemp '
exec @ret  =  sp_executesql  @selectlist

0
投票

我已经将此程序编写为许多自动和快速删除列标识的答案的汇编:


0
投票

在SQL Server上删除标识列(特别是对于大型数据库)的最有效方法是直接修改DDL元数据,在2005年之前的SQL Server上,可以使用以下方法完成:


0
投票

请检查下面的链接,以通过处理AZURE和本地中的外键关系从表的列中删除IDENTITY属性-

© www.soinside.com 2019 - 2024. All rights reserved.