Supprimer l'identité d'une colonne dans une table


127

Nous avons une table de 5 Go (près de 500 millions de lignes) et nous voulons supprimer la propriété d'identité sur l'une des colonnes, mais lorsque nous essayons de le faire via SSMS, le délai expire.

Cela peut-il être fait via T-SQL?


1
Pouvez-vous poster le schéma de la table ici?
Al W

Je suis sûr qu'il y a d'excellentes raisons pour lesquelles SQL Server ne prend pas en charge la suppression d'une propriété d'identité d'une colonne via une simple instruction ALTER TABLE ..., mais cela me rend néanmoins triste actuellement que ce soit le cas.
Jon Schneider

Réponses:


143

Vous ne pouvez pas supprimer une IDENTITYspécification une fois définie.

Pour supprimer toute la colonne:

ALTER TABLE yourTable
DROP COLUMN yourCOlumn;

Informations sur ALTER TABLE ici

Si vous devez conserver les données, mais supprimer la IDENTITYcolonne, vous devrez:

  • Créer une nouvelle colonne
  • Transférer les données de la IDENTITYcolonne existante vers la nouvelle colonne
  • Supprimez la IDENTITYcolonne existante .
  • Renommez la nouvelle colonne avec le nom de colonne d'origine

3
vous pouvez supprimer une spécification d'identité. En fait, j'ai dû le faire hier en utilisant SSMS mais pas sur 500 millions de lignes.
Simon

34
@simon si vous créez un script pour vos modifications, vous verrez que SSMS est en train de créer une copie de la table sans la propriété d'identité.
Code Magician

3
Je veux juste ajouter pour renommer la nouvelle colonne au nom de la colonne d'origine. De plus, si cette identitycolonne est utilisée dans le cadre d'un foreign keydans une autre table, vous devrez d'abord supprimer les contraintes, puis agir comme @AdamWenger l'a mentionné concernant la suppression de l'identité. attribute/propertyVous pouvez également consulter ce lien pour plus de détails. à propos de la suppression de l'attribut uniquement: blog.sqlauthority.com/2009/05/03/… ..Bonne chance!
Nonym

1
À la personne qui a voté à la baisse - j'apprécierais d'entendre ce que vous n'avez pas aimé dans ma réponse.
Adam Wenger

1
Jetez un œil à la réponse de Mark Sowul ci-dessous. Évite le besoin de déplacer les données d'une colonne à l'autre. En utilisant la réponse de Mark, vous ne mélangez que les métadonnées. Pas de problème à moins que vous ne travailliez sur une table comportant des dizaines ou des centaines de millions de lignes. De plus, la réponse de Mark empêche le déplacement de la colonne dans le schéma de table. Je viens de l'essayer et cela a fonctionné comme un charme. Très intelligent.
Andrew Steitz

100

Si vous voulez faire cela sans ajouter et remplir une nouvelle colonne , sans réorganiser les colonnes , et avec presque pas de temps d'arrêt car aucune donnée ne change sur la table, faisons de la magie avec la fonctionnalité de partitionnement (mais comme aucune partition n'est utilisée, vous ne le faites pas. t besoin de l'édition Entreprise):

  1. Supprimez toutes les clés étrangères qui pointent vers cette table
  2. Script la table à créer; renommez tout, par exemple «MyTable2», «MyIndex2», etc. Supprimez la spécification IDENTITY.
  3. Vous devriez maintenant avoir deux tables -ish "identiques", l'une pleine, l'autre vide sans IDENTITÉ.
  4. Courir ALTER TABLE [Original] SWITCH TO [Original2]
  5. Maintenant, votre table d'origine sera vide et la nouvelle aura les données. Vous avez changé les métadonnées pour les deux tables (instantané).
  6. Supprimez la table d'origine (maintenant vide) exec sys.sp_renamepour renommer les différents objets de schéma en leur attribuant les noms d'origine, puis vous pouvez recréer vos clés étrangères.

Par exemple, étant donné:

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

Vous pouvez faire ce qui suit:

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

Je n'ai pas le temps d'essayer cela, mais c'est une bonne chose à savoir la prochaine fois que j'ai vraiment besoin de laisser tomber une identité.
CoderDennis

12
Cela devrait être la réponse acceptée. C'est le seul véritable moyen de supprimer une colonne d'identité sans migration massive de données.
Vaccano

Hou la la! C'est très intelligent.
Andrew Steitz

Cette réponse est phénoménale! Merci!
Jon

5
Si vous utilisez SQL Management Studio pour créer un script de la table, assurez-vous d'activer Outils> Options> Explorateur d'objets SQL Server> Script> Options de table et d'affichage> Index de script (False par défaut)
user423430

61

Cela devient compliqué avec les contraintes de clés étrangères et primaires, alors voici quelques scripts pour vous aider sur votre chemin:

Tout d'abord, créez une colonne en double avec un nom temporaire:

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

Ensuite, obtenez le nom de votre contrainte de clé primaire:

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

Maintenant, essayez de supprimer la contrainte de clé primaire pour votre colonne:

ALTER TABLE yourTable DROP CONSTRAINT PK_yourTable_id;

Si vous avez des clés étrangères, cela échouera, donc si c'est le cas, supprimez les contraintes de clé étrangère. CONSERVEZ LES TABLES POUR LESQUELLES VOUS FONCTIONNEZ AFIN DE POUVOIR AJOUTER LES CONTRAINTES PLUS TARD !!!

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

Une fois que toutes vos contraintes de clé étrangère ont été supprimées, vous pourrez supprimer la contrainte PK, supprimer cette colonne, renommer votre colonne temporaire et ajouter la contrainte PK à cette colonne:

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;

Enfin, rajoutez les contraintes FK dans:

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

El Fin!


faites attention à la version de votre serveur sql, j'ai essayé dans le serveur azure sql, toutes les opérations ici ne sont pas prises en charge dans la version azure sql.
dasons

Merci d'avoir répondu! M'a beaucoup aidé! Vous devez simplement ajouter une vérification pour les index qui font référence à la colonne. Quelque chose comme: 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
Alexandre Junges

bon truc, m'a beaucoup aidé, mais un point, je veux mentionner ici, c'est que, lorsque nous ajoutons une nouvelle colonne en double comme mentionné à la 1ère étape, elle a été ajoutée à la fin mais généralement nous voulons que notre colonne de clé primaire, c'est-à-dire Id soit la 1ère colonne de ce tableau. Alors, y a-t-il une solution de contournement à cela?
Ashish Shukla

19

J'ai juste eu ce même problème. 4 instructions dans SSMS au lieu d'utiliser l'interface graphique et c'était très rapide.

  • Créer une nouvelle colonne

    alter table users add newusernum int;

  • Copier les valeurs sur

    update users set newusernum=usernum;

  • Supprimer l'ancienne colonne

    alter table users drop column usernum;

  • Renommez la nouvelle colonne avec l'ancien nom de colonne

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


Ce n'est pas aussi simple si vous avez des contraintes sur l'ancienne colonne.
Suncat2000

13

Le script suivant supprime le champ d'identité pour une colonne nommée 'Id'

J'espère que ça aide.

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

4

Le code ci-dessous fonctionne aussi bien, lorsque nous ne connaissons pas le nom de la colonne d'identité .

Besoin de copier les données dans une nouvelle table temporaire comme Invoice_DELETED. et la prochaine fois que nous utiliserons:

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

Pour plus d'explications, voir: https://dba.stackexchange.com/a/138345/101038


1
LOVE YAAAAAAAAAA. Exactement ce que je cherchais, un moyen simple d'éviter de générer une "IDENTITY" à partir d'un "SELECT * INTO" (pour créer une nouvelle table temp pour les sauvegardes qui ont besoin)
KurzedMetal

il est petit et beau;)
Zolfaghari

3
ALTER TABLE tablename add newcolumn int
update tablename set newcolumn=existingcolumnname
ALTER TABLE tablename DROP COLUMN existingcolumnname;
EXEC sp_RENAME 'tablename.oldcolumn' , 'newcolumnname', 'COLUMN'

Cependant, le code ci-dessus ne fonctionne que si aucune relation de clé primaire-étrangère


1

Juste pour quelqu'un qui a le même problème que moi. Si vous voulez juste faire une insertion juste une fois, vous pouvez faire quelque chose comme ça.

Supposons que vous ayez une table avec deux colonnes

ID Identity (1,1) | Name Varchar

et que vous voulez insérer une ligne avec l'ID = 4. Vous l'avez donc réensemencée à 3 pour que la suivante soit 4

DBCC CHECKIDENT([YourTable], RESEED, 3)

Faire l'insertion

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

Et ramenez votre graine à l'ID le plus élevé, supposons que ce soit 15

DBCC CHECKIDENT([YourTable], RESEED, 15)

Terminé!


1

J'avais la même exigence, et vous pourriez essayer de cette façon, ce que je vous recommande personnellement, veuillez concevoir manuellement votre table et générer le script, et ce que j'ai fait ci-dessous a été de renommer l'ancienne table et aussi sa contrainte de sauvegarde.

/* 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

1

Dans SQL Server, vous pouvez activer et désactiver l'insertion d'identité comme ceci:

SET IDENTITY_INSERT nom_table ON

- lancez vos requêtes ici

SET IDENTITY_INSERT nom_table OFF

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.