【问题标题】:Remove Identity from a column in a table从表中的列中删除标识
【发布时间】:2012-01-04 00:58:39
【问题描述】:

我们有一个 5GB 表(近 5 亿行),我们想要删除其中一列的身份属性,但是当我们尝试通过 SSMS 执行此操作时 - 超时。

这可以通过 T-SQL 完成吗?

【问题讨论】:

  • 你能把表格的架构贴在这里吗?
  • 我确信 SQL Server 不支持通过简单的 ALTER TABLE ... 语句从列中删除标识属性是有充分理由的,但目前让我感到难过的是案例。

标签: sql sql-server tsql


【解决方案1】:

IDENTITY 规范一旦设置就无法删除。

删除整列:

ALTER TABLE yourTable
DROP COLUMN yourCOlumn;

这里有关于ALTER TABLE的信息

如果您需要保留数据,但删除 IDENTITY 列,您需要:

  • 创建一个新列
  • 将数据从现有IDENTITY 列转移到新列
  • 删除现有的IDENTITY 列。
  • 将新列重命名为原始列名

【讨论】:

  • 您可以删除身份规范。事实上,我昨天不得不使用 SSMS 来做这件事,虽然不是在 5 亿行上。
  • @simon 如果您编写更改脚本,您会看到 SSMS 实际上是在创建没有身份属性的表副本。
  • 我只想添加以将新列重命名为原始列的名称。此外,如果此 identity 列在另一个表中用作 foreign key 的一部分,则您将不得不首先删除约束,然后按照@AdamWenger 提到的有关删除身份 attribute/property 的操作采取行动..您也可以查看此链接以获取有关仅删除属性的更多详细信息:blog.sqlauthority.com/2009/05/03/… ..祝你好运!
  • 致投反对票的人 - 如果您不喜欢我的回答,我将不胜感激。
  • 看看下面 Mark Sowul 的回答。无需在列之间移动数据。使用 Mark 的答案,您只是在改组元数据。除非您正在处理具有数千万或数亿行的表,否则没什么大不了的。 Plus Mark 的回答防止了表模式中列的重定位。我刚试过,它就像一个魅力。非常聪明。
【解决方案2】:

如果您想不添加和填充新列不重新排序列以及几乎没有停机时间,因为没有表上的数据正在发生变化,让我们用分区功能做一些魔术(但由于没有使用分区,因此您不需要企业版):

  1. 删除所有指向该表的外键
  2. 为要创建的表编写脚本;重命名所有内容,例如“MyTable2”、“MyIndex2”等 删除 IDENTITY 规范。
  3. 您现在应该有两个“相同”的表,一个是满的,另一个是空的,没有 IDENTITY。
  4. 运行ALTER TABLE [Original] SWITCH TO [Original2]
  5. 现在您的原始表将为空,而新表将包含数据。您已切换两个表的元数据(即时)。
  6. 删除原始(现在为空的表)exec sys.sp_rename 以将各种架构对象重命名为原始名称,然后您可以重新创建外键。

例如,给定:

CREATE TABLE Original
(
  Id INT IDENTITY PRIMARY KEY
, Value NVARCHAR(300)
);
CREATE NONCLUSTERED INDEX IX_Original_Value ON Original (Value);

INSERT INTO Original
SELECT 'abcd'
UNION ALL 
SELECT 'defg';

您可以执行以下操作:

--create new table with no IDENTITY
CREATE TABLE Original2
(
  Id INT PRIMARY KEY
, Value NVARCHAR(300)
);
CREATE NONCLUSTERED INDEX IX_Original_Value2 ON Original2 (Value);

--data before switch
SELECT 'Original', *
FROM Original
UNION ALL
SELECT 'Original2', *
FROM Original2;

ALTER TABLE Original SWITCH TO Original2;

--data after switch
SELECT 'Original', *
FROM Original
UNION ALL
SELECT 'Original2', *
FROM Original2;

--clean up 
IF NOT EXISTS (SELECT * FROM Original) DROP TABLE Original;
EXEC sys.sp_rename 'Original2.IX_Original_Value2', 'IX_Original_Value', 'INDEX';
EXEC sys.sp_rename 'Original2', 'Original', 'OBJECT';


UPDATE Original
SET Id = Id + 1;

SELECT *
FROM Original;

郑重声明,随着这个越来越流行,我一直想追查原始来源(我自己没想到),但我当然早就忘记了我在哪里找到了最初的想法。它可能就在这里;这是我能在这个答案之前找到的唯一一个(时间过得真快,男孩和女孩):https://social.technet.microsoft.com/wiki/contents/articles/17738.sql-server-quick-way-to-remove-the-identity-property.aspx

【讨论】:

  • 我没有时间实际尝试这个,但是下次我真的需要放弃身份时知道是件好事。
  • 这应该是公认的答案。这是在不进行大量数据迁移的情况下删除标识列的唯一真正方法。
  • 这个答案太棒了!谢谢!
  • 如果使用 SQL Management Studio 编写表脚本,请务必打开工具 > 选项 > SQL Server 对象资源管理器 > 脚本 > 表和视图选项 > 脚本索引(默认为 False)
  • 请记住在开始时删除外键约束并在过程结束时再次设置。谢谢!
【解决方案3】:

这会使外键和主键约束变得混乱,所以这里有一些脚本可以帮助您:

首先,创建一个具有临时名称的重复列:

alter table yourTable add tempId int NOT NULL default -1;
update yourTable set tempId = id;

接下来,获取主键约束的名称:

SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'yourTable';

现在尝试删除列的主键约束:

ALTER TABLE yourTable DROP CONSTRAINT PK_yourTable_id;

如果你有外键,它会失败,所以如果有,就放弃外键约束。 跟踪您为哪些表运行此操作,以便稍后重新添加约束!!!

SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'otherTable';
alter table otherTable drop constraint fk_otherTable_yourTable;
commit;
..

删除所有外键约束后,您将能够删除 PK 约束、删除该列、重命名临时列并将 PK 约束添加到该列:

ALTER TABLE yourTable DROP CONSTRAINT PK_yourTable_id;
alter table yourTable drop column id;
EXEC sp_rename 'yourTable.tempId', 'id', 'COLUMN';
ALTER TABLE yourTable ADD CONSTRAINT PK_yourTable_id PRIMARY KEY (id) 
commit;

最后,添加 FK 约束:

alter table otherTable add constraint fk_otherTable_yourTable foreign key (yourTable_id) references yourTable(id);
..

埃尔芬!

【讨论】:

  • 注意你的sql server版本,我在azure sql server上试过,不是azure版本sql支持这里所有操作。
  • 感谢您的回答!帮了我很多!您只需为引用该列的索引添加验证。比如:Select t.name 'Table', i.name 'Index', c.name 'Column', i.is_primary_key, i.is_unique From sys.tables t Inner Join sys.indexes i On i.object_id = t.object_id Inner Join sys.index_columns ic ON ic.object_id = i.object_id And i.index_id = ic.index_id Inner Join sys.columns c ON ic.object_id = c.object_id and ic.column_id = c.column_id Where t.name = 'tableName' Order By t.name, i.name, i.index_id, ic.index_column_id
  • 好技巧,帮了我很多,但有一点,我想在这里提到的是,当我们像第一步中提到的那样添加一个新的重复列时,它会在最后添加,但通常我们想要那个我们的主键列即 Id 是该表中的第一列。那么有什么解决方法吗?
【解决方案4】:

我也遇到了同样的问题。 SSMS 中的 4 个语句,而不是使用 GUI,而且速度非常快。

  • 新建一栏

    alter table users add newusernum int;

  • 复制值

    update users set newusernum=usernum;

  • 删除旧列

    alter table users drop column usernum;

  • 将新列重命名为旧列名

    EXEC sp_RENAME 'users.newusernum' , 'usernum', 'COLUMN';

【讨论】:

  • 如果你对旧列有任何限制,那就没那么简单了。
【解决方案5】:

以下脚本删除名为“Id”的列的标识字段

希望对你有帮助。

BEGIN TRAN
BEGIN TRY
    EXEC sp_rename '[SomeTable].[Id]', 'OldId';

    ALTER TABLE [SomeTable] ADD Id int NULL

    EXEC ('UPDATE [SomeTable] SET Id = OldId')

    ALTER TABLE [SomeTable] NOCHECK CONSTRAINT ALL

    ALTER TABLE [SomeTable] DROP CONSTRAINT [PK_constraintName];
    ALTER TABLE [SomeTable] DROP COLUMN OldId
    ALTER TABLE [SomeTable] ALTER COLUMN [Id] INTEGER NOT NULL
    ALTER TABLE [SomeTable] ADD CONSTRAINT PK_JobInfo PRIMARY KEY (Id)

    ALTER TABLE [SomeTable] CHECK CONSTRAINT ALL

    COMMIT TRAN
END TRY
BEGIN CATCH
    ROLLBACK TRAN   
    SELECT ERROR_MESSAGE ()
END CATCH

【讨论】:

    【解决方案6】:

    波纹管代码工作正常,当我们不知道标识列名称时

    需要将数据复制到新的临时表中,例如Invoice_DELETED。 下次我们使用:

    insert into Invoice_DELETED select * from Invoice where ...
    
    
    SELECT t1.*
    INTO Invoice_DELETED
    FROM Invoice t1
    LEFT JOIN Invoice ON 1 = 0
    --WHERE t1.InvoiceID = @InvoiceID
    

    更多解释见: https://dba.stackexchange.com/a/138345/101038

    【讨论】:

    • 爱 YAAAAAAAAAA。正是我正在寻找的,一种避免从“SELECT * INTO”生成“IDENTITY”的简单方法(为需要的备份创建新表临时)
    • 它又小又漂亮;)
    【解决方案7】:
    ALTER TABLE tablename add newcolumn int
    update tablename set newcolumn=existingcolumnname
    ALTER TABLE tablename DROP COLUMN existingcolumnname;
    EXEC sp_RENAME 'tablename.oldcolumn' , 'newcolumnname', 'COLUMN'
    

    但是上面的代码只有在没有主外键关系时才有效

    【讨论】:

      【解决方案8】:

      在 SQL Server 中,您可以像这样打开和关闭身份插入:

      SET IDENTITY_INSERT table_name ON

      -- 在此处运行您的查询

      SET IDENTITY_INSERT 表名关闭

      【讨论】:

      • 正打算发布相同的内容。我不确定在 OP 发生时是否存在此功能。但这绝对是现在的解决方案。
      • 这是对我有用的解决方案;我使用的是 mySQL Server 2019 数据库。
      【解决方案9】:

      仅针对与我遇到相同问题的人。 如果你只想插入一次,你可以这样做。

      假设您有一个包含两列的表格

      ID Identity (1,1) | Name Varchar
      

      并且想要插入 ID = 4 的行。所以你将它重新设置为 3,所以下一个是 4

      DBCC CHECKIDENT([YourTable], RESEED, 3)
      

      进行插入

      INSERT  INTO [YourTable]
              ( Name )
      VALUES  ( 'Client' )
      

      让你的种子恢复到最高 ID,假设是 15

      DBCC CHECKIDENT([YourTable], RESEED, 15)
      

      完成!

      【讨论】:

        【解决方案10】:

        我也有同样的需求,你可以试试这个方法,我个人推荐你,请手动设计你的表并生成脚本,下面我所做的就是重命名旧表以及它的备份约束。

        /* To prevent any potential data loss issues, you should review this script in detail before running it outside the context of the database designer.*/
        BEGIN TRANSACTION
        
        SET QUOTED_IDENTIFIER ON
        SET ARITHABORT ON
        SET NUMERIC_ROUNDABORT OFF
        SET CONCAT_NULL_YIELDS_NULL ON
        SET ANSI_NULLS ON
        SET ANSI_PADDING ON
        SET ANSI_WARNINGS ON
        COMMIT
        BEGIN TRANSACTION
        GO
        ALTER TABLE dbo.SI_Provider_Profile
            DROP CONSTRAINT DF_SI_Provider_Profile_SIdtDateTimeStamp
        GO
        ALTER TABLE dbo.SI_Provider_Profile
            DROP CONSTRAINT DF_SI_Provider_Profile_SIbHotelPreLoaded
        GO
        CREATE TABLE dbo.Tmp_SI_Provider_Profile
            (
            SI_lProvider_Profile_ID int NOT NULL,
            SI_lSerko_Integrator_Token_ID int NOT NULL,
            SI_sSerko_Integrator_Provider varchar(50) NOT NULL,
            SI_sSerko_Integrator_Profile varchar(50) NOT NULL,
            SI_dtDate_Time_Stamp datetime NOT NULL,
            SI_lProvider_ID int NULL,
            SI_sDisplay_Name varchar(10) NULL,
            SI_lPurchased_From int NULL,
            SI_sProvider_UniqueID varchar(255) NULL,
            SI_bHotel_Pre_Loaded bit NOT NULL,
            SI_sSiteName varchar(255) NULL
            )  ON [PRIMARY]
        GO
        ALTER TABLE dbo.Tmp_SI_Provider_Profile SET (LOCK_ESCALATION = TABLE)
        GO
        ALTER TABLE dbo.Tmp_SI_Provider_Profile ADD CONSTRAINT
            DF_SI_Provider_Profile_SIdtDateTimeStamp DEFAULT (getdate()) FOR SI_dtDate_Time_Stamp
        GO
        ALTER TABLE dbo.Tmp_SI_Provider_Profile ADD CONSTRAINT
            DF_SI_Provider_Profile_SIbHotelPreLoaded DEFAULT ((0)) FOR SI_bHotel_Pre_Loaded
        GO
        IF EXISTS(SELECT * FROM dbo.SI_Provider_Profile)
                EXEC('INSERT INTO dbo.Tmp_SI_Provider_Profile (SI_lProvider_Profile_ID, SI_lSerko_Integrator_Token_ID, SI_sSerko_Integrator_Provider, SI_sSerko_Integrator_Profile, SI_dtDate_Time_Stamp, SI_lProvider_ID, SI_sDisplay_Name, SI_lPurchased_From, SI_sProvider_UniqueID, SI_bHotel_Pre_Loaded, SI_sSiteName)
                SELECT SI_lProvider_Profile_ID, SI_lSerko_Integrator_Token_ID, SI_sSerko_Integrator_Provider, SI_sSerko_Integrator_Profile, SI_dtDate_Time_Stamp, SI_lProvider_ID, SI_sDisplay_Name, SI_lPurchased_From, SI_sProvider_UniqueID, SI_bHotel_Pre_Loaded, SI_sSiteName FROM dbo.SI_Provider_Profile WITH (HOLDLOCK TABLOCKX)')
        GO
        
        -- Rename the primary key constraint or unique key In SQL Server constraints such as primary keys or foreign keys are objects in their own right, even though they are dependent upon the "containing" table.
        EXEC sp_rename 'dbo.SI_Provider_Profile.PK_SI_Provider_Profile', 'PK_SI_Provider_Profile_Old';
        GO
        -- backup old table in case of 
        EXECUTE sp_rename N'dbo.SI_Provider_Profile', N'SI_Provider_Profile_Old', 'OBJECT'
        GO
        
        EXECUTE sp_rename N'dbo.Tmp_SI_Provider_Profile', N'SI_Provider_Profile', 'OBJECT'
        GO
        
        ALTER TABLE dbo.SI_Provider_Profile ADD CONSTRAINT
            PK_SI_Provider_Profile PRIMARY KEY NONCLUSTERED 
            (
            SI_lProvider_Profile_ID
            ) WITH( PAD_INDEX = OFF, FILLFACTOR = 90, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
        
        GO
        COMMIT TRANSACTION
        

        【讨论】:

          【解决方案11】:
          ALTER TABLE TABLE_NAME MODIFY (COLUMN_NAME DROP IDENTITY);
          

          【讨论】:

          • 我怀疑这是否有帮助——甚至根本没有用。介意解释吗? How to Answer.
          • 我被派到这里寻找 Oracle 的解决方案。给定的 sql 确实适用于 oracle
          猜你喜欢
          • 2022-08-19
          • 1970-01-01
          • 1970-01-01
          • 2015-01-24
          • 1970-01-01
          • 2022-09-27
          • 1970-01-01
          • 2015-09-07
          相关资源
          最近更新 更多