Entfernen Sie die Identität aus einer Spalte in einer Tabelle


127

Wir haben eine 5-GB-Tabelle (fast 500 Millionen Zeilen) und möchten die Identitätseigenschaft in einer der Spalten entfernen. Wenn wir dies jedoch über SSMS versuchen, tritt eine Zeitüberschreitung auf.

Kann dies über T-SQL erfolgen?


1
Können Sie das Schema der Tabelle hier posten?
Al W

Ich bin mir sicher, dass es gute Gründe gibt, warum SQL Server das Entfernen einer Identitätseigenschaft aus einer Spalte über eine einfache Anweisung ALTER TABLE ... nicht unterstützt, aber es macht mich derzeit trotzdem traurig, dass dies der Fall ist.
Jon Schneider

Antworten:


143

Sie können eine IDENTITYeinmal festgelegte Spezifikation nicht entfernen .

So entfernen Sie die gesamte Spalte:

ALTER TABLE yourTable
DROP COLUMN yourCOlumn;

Informationen zu ALTER TABLE hier

Wenn Sie die Daten behalten, aber die IDENTITYSpalte entfernen müssen , müssen Sie:

  • Erstellen Sie eine neue Spalte
  • Übertragen Sie die Daten aus der vorhandenen IDENTITYSpalte in die neue Spalte
  • Löschen Sie das vorhandene IDENTITY Spalte.
  • Benennen Sie die neue Spalte in den ursprünglichen Spaltennamen um

3
Sie können eine Identitätsspezifikation entfernen. Tatsächlich musste ich es gestern mit SSMS tun, obwohl nicht in 500 Millionen Zeilen.
Simon

33
@simon Wenn Sie Ihre Änderungen per Skript ausführen, wird SSMS tatsächlich eine Kopie der Tabelle ohne die Identitätseigenschaft erstellen.
Code Magier

3
Ich möchte nur hinzufügen, um die neue Spalte in den Namen der ursprünglichen Spalte umzubenennen. Wenn diese identitySpalte als Teil einer foreign keyin einer anderen Tabelle verwendet wird, müssen Sie zuerst die Einschränkungen löschen und dann Maßnahmen ergreifen, wie @AdamWenger über das Entfernen der Identität erwähnt hat. attribute/propertyWeitere Informationen finden Sie auch in diesem Link nur zum Entfernen des Attributs: blog.sqlauthority.com/2009/05/03/… ..Gen viel Glück!
Nonym

1
An die Person, die abgelehnt hat - ich würde mich freuen zu hören, was Ihnen an meiner Antwort nicht gefallen hat.
Adam Wenger

1
Schauen Sie sich die Antwort von Mark Sowul unten an. Es ist nicht mehr erforderlich, Daten von Spalte zu Spalte zu verschieben. Mit Marks Antwort mischen Sie nur Metadaten. Keine große Sache, es sei denn, Sie arbeiten an einer Tabelle mit mehreren zehn oder hundert Millionen Zeilen. Die Antwort von Plus Mark verhindert das Verschieben der Spalte im Tabellenschema. Ich habe es gerade versucht und es hat wie ein Zauber funktioniert. Sehr schlau.
Andrew Steitz

100

Wenn Sie dies tun möchten, ohne eine neue Spalte hinzuzufügen und zu füllen , ohne die Spalten neu zu ordnen und ohne Ausfallzeiten, da sich keine Daten in der Tabelle ändern, lassen Sie uns mit der Partitionierungsfunktion etwas zaubern (da jedoch keine Partitionen verwendet werden, sollten Sie dies nicht tun). brauche keine Enterprise Edition):

  1. Entfernen Sie alle Fremdschlüssel, die auf diese Tabelle verweisen
  2. Skript für die zu erstellende Tabelle; Benennen Sie alles um, z. B. 'MyTable2', 'MyIndex2' usw. Entfernen Sie die IDENTITY-Spezifikation.
  3. Sie sollten jetzt zwei "identische" Tabellen haben, eine voll, die andere leer ohne IDENTITÄT.
  4. Lauf ALTER TABLE [Original] SWITCH TO [Original2]
  5. Jetzt ist Ihre ursprüngliche Tabelle leer und die neue enthält die Daten. Sie haben die Metadaten für die beiden Tabellen gewechselt (sofort).
  6. Löschen Sie das Original (jetzt leere Tabelle), exec sys.sp_renameum die verschiedenen Schemaobjekte wieder in die ursprünglichen Namen umzubenennen, und erstellen Sie dann Ihre Fremdschlüssel neu.

Zum Beispiel gegeben:

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

Sie können Folgendes tun:

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

Ich habe keine Zeit, dies tatsächlich zu versuchen, aber es ist gut zu wissen, wann ich das nächste Mal wirklich eine Identität fallen lassen muss.
CoderDennis

11
Dies sollte die akzeptierte Antwort sein. Dies ist die einzige echte Möglichkeit, eine Identitätsspalte ohne massive Datenmigration zu entfernen.
Vaccano

Beeindruckend! Das ist sehr klug.
Andrew Steitz

Diese Antwort ist phänomenal! Vielen Dank!
Jon

5
Wenn Sie SQL Management Studio zum Skripten der Tabelle verwenden, müssen Sie Extras> Optionen> SQL Server-Objekt-Explorer> Skripterstellung> Tabellen- und Ansichtsoptionen> Skriptindizes (standardmäßig falsch)
aktivieren

61

Dies wird mit Fremd- und Primärschlüsseleinschränkungen unübersichtlich. Hier sind einige Skripte, die Ihnen auf Ihrem Weg helfen sollen:

Erstellen Sie zunächst eine doppelte Spalte mit einem temporären Namen:

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

Als Nächstes erhalten Sie den Namen Ihrer Primärschlüsseleinschränkung:

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

Versuchen Sie nun, die Primärschlüsseleinschränkung für Ihre Spalte zu löschen:

ALTER TABLE yourTable DROP CONSTRAINT PK_yourTable_id;

Wenn Sie Fremdschlüssel haben, schlägt dies fehl. Wenn dies der Fall ist, lassen Sie die Fremdschlüsseleinschränkungen fallen. Behalten Sie den Überblick darüber, für welche Tabellen Sie dies ausführen, damit Sie die Einschränkungen später wieder hinzufügen können !!!

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

Nachdem alle Fremdschlüsseleinschränkungen entfernt wurden, können Sie die PK-Einschränkung entfernen, diese Spalte löschen, Ihre temporäre Spalte umbenennen und die PK-Einschränkung zu dieser Spalte hinzufügen:

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;

Fügen Sie abschließend die FK-Einschränkungen wieder hinzu in:

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

El Fin!


Achten Sie auf Ihre SQL Server-Version, ich habe es in Azure SQL Server versucht, nicht alle Operationen hier werden in Azure Version SQL unterstützt.
Dasons

Danke für die Antwort! Hat mir viel geholfen! Sie sollten nur eine Überprüfung für Indizes hinzufügen, die auf die Spalte verweisen. So etwas wie: 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

Guter Trick, hat mir sehr geholfen. Aber ein Punkt, den ich hier erwähnen möchte, ist, dass, wenn wir eine neue doppelte Spalte hinzufügen, wie im ersten Schritt erwähnt, diese am Ende hinzugefügt wird, aber im Allgemeinen möchten wir, dass unsere Primärschlüsselspalte, dh ID, ist die 1. Spalte in dieser Tabelle. Gibt es also eine Problemumgehung?
Ashish Shukla

19

Ich hatte gerade das gleiche Problem. 4 Anweisungen in SSMS anstelle der GUI und es war sehr schnell.

  • Erstellen Sie eine neue Spalte

    alter table users add newusernum int;

  • Werte kopieren über

    update users set newusernum=usernum;

  • Lass die alte Spalte fallen

    alter table users drop column usernum;

  • Benennen Sie die neue Spalte in den alten Spaltennamen um

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


Es ist nicht so einfach, wenn Sie Einschränkungen für die alte Spalte haben.
Suncat2000

13

Das folgende Skript entfernt das Identitätsfeld für eine Spalte mit dem Namen 'Id'.

Ich hoffe es hilft.

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

Der folgende Code funktioniert einwandfrei, wenn wir den Namen der Identitätsspalte nicht kennen .

Müssen Daten in neue temporäre Tabelle wie kopieren Invoice_DELETED. und das nächste Mal verwenden wir:

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

Weitere Erläuterungen finden Sie unter: https://dba.stackexchange.com/a/138345/101038


1
LIEBE YAAAAAAAAAA. Genau das, wonach ich gesucht habe, ein einfacher Weg, um zu vermeiden, dass "IDENTITY" aus einem "SELECT * INTO" generiert wird (um eine neue Tabellentemperatur für Backups zu erstellen, die benötigt werden)
KurzedMetal

es ist klein und schön;)
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'

Der obige Code funktioniert jedoch nur, wenn keine Primär-Fremdschlüssel-Beziehung besteht


1

Nur für jemanden, der das gleiche Problem hat wie ich. Wenn Sie nur einmal eine Einfügung vornehmen möchten, können Sie so etwas tun.

Nehmen wir an, Sie haben eine Tabelle mit zwei Spalten

ID Identity (1,1) | Name Varchar

und möchten eine Zeile mit der ID = 4 einfügen. Sie setzen sie also auf 3, sodass die nächste 4 ist

DBCC CHECKIDENT([YourTable], RESEED, 3)

Machen Sie den Einsatz

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

Und bringen Sie Ihren Samen zurück auf die höchste ID, nehmen wir an, er ist 15

DBCC CHECKIDENT([YourTable], RESEED, 15)

Getan!


1

Ich hatte die gleiche Anforderung, und Sie könnten diese Methode ausprobieren, die ich Ihnen persönlich empfehle. Bitte entwerfen Sie Ihre Tabelle manuell und generieren Sie das Skript. Im Folgenden habe ich die alte Tabelle und auch deren Einschränkung für die Sicherung umbenannt.

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

In SQL Server können Sie das Einfügen von Identitäten wie folgt aktivieren und deaktivieren:

SET IDENTITY_INSERT Tabellenname EIN

- Führen Sie hier Ihre Abfragen aus

SET IDENTITY_INSERT Tabellenname AUS

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.