Wie kann man Identitätsspaltenwerte programmgesteuert ändern?

160

Ich habe eine MS SQL 2005-Datenbank mit einer Tabelle Testmit Spalte ID. IDist eine Identitätsspalte.

Ich habe Zeilen in dieser Tabelle und alle haben ihren entsprechenden automatisch inkrementierten ID-Wert.

Jetzt möchte ich jede ID in dieser Tabelle folgendermaßen ändern:

ID = ID + 1

Aber wenn ich das mache, bekomme ich eine Fehlermeldung:

Die Identitätsspalte 'ID' kann nicht aktualisiert werden.

Ich habe das versucht:

    ALTER TABLE Test NOCHECK CONSTRAINT ALL 
    set identity_insert ID ON

Dies löst das Problem jedoch nicht.

Ich muss eine Identität für diese Spalte haben, aber ich muss auch von Zeit zu Zeit Werte ändern. Meine Frage ist also, wie ich diese Aufgabe erfüllen kann.

Tomasz Smykowski
quelle

Antworten:

220

Du musst

set identity_insert YourTable ON

Löschen Sie dann Ihre Zeile und fügen Sie sie mit einer anderen Identität wieder ein.

Vergessen Sie nach dem Einfügen nicht, identity_insert zu deaktivieren

set identity_insert YourTable OFF
AK
quelle
134
Die Einstellung funktioniert nur beim Einfügen von Daten und nicht beim Aktualisieren. Die UPDATE-Anweisung schlägt weiterhin fehl.
Husein Roncevic
32
Für ein Update müssen Sie löschen und erneut einfügen. Es gibt keinen anderen Weg.
Asche999
1
@ MartinSmith Ihre Lösung scheint zu lang. Die Möglichkeit, dies in zwei Schritten zu tun (Identität aus, Löschen / Einfügen, Identität ein), ist viel effektiver.
Asche999
3
@ ashes999 Das sind 4 Schritte, nicht 2. Meine Antwort ist nur noch eine (Tabelle erstellen, wechseln, aktualisieren, zurückschalten, löschen). Vermutlich sind Sie mit diesen Schritten besser vertraut, sodass sie weniger kompliziert aussehen. Das Aktualisieren kann viel effizienter sein als das Löschen von Einfügungen, wenn viele Zeilen betroffen sind. Auch wo halten Sie die gelöschten Zeilen? Wenn eine #tempTabelle, die Sie dann löschen, ein weiterer Schritt ist, haben Sie hier nicht gezählt.
Martin Smith
40

IDENTITY Spaltenwerte sind unveränderlich.

Es ist jedoch möglich, die Tabellenmetadaten zu wechseln, um die IDENTITYEigenschaft zu entfernen , die Aktualisierung durchzuführen und dann zurückzuschalten.

Angenommen, die folgende Struktur

CREATE TABLE Test
(
ID INT IDENTITY(1,1) PRIMARY KEY,
X VARCHAR(10)
)

INSERT INTO Test 
OUTPUT INSERTED.*
SELECT 'Foo' UNION ALL
SELECT 'Bar' UNION ALL
SELECT 'Baz'

Dann können Sie tun

/*Define table with same structure but no IDENTITY*/
CREATE TABLE Temp
(
ID INT PRIMARY KEY,
X VARCHAR(10)
)

/*Switch table metadata to new structure*/
ALTER TABLE Test SWITCH TO Temp;

/*Do the update*/
UPDATE Temp SET ID = ID + 1;

/*Switch table metadata back*/
ALTER TABLE Temp SWITCH TO Test;

/*ID values have been updated*/
SELECT *
FROM Test

/*Safety check in case error in preceding step*/
IF NOT EXISTS(SELECT * FROM Temp)
    DROP TABLE Temp /*Drop obsolete table*/

In SQL Server 2012 ist es möglich, eine automatisch inkrementierende Spalte zu haben, mit der auch einfacher aktualisiert werden kann SEQUENCES

CREATE SEQUENCE Seq
    AS INT
    START WITH 1
    INCREMENT BY 1

CREATE TABLE Test2
(
ID INT DEFAULT NEXT VALUE FOR Seq NOT NULL PRIMARY KEY,
X VARCHAR(10)
)

INSERT INTO Test2(X)
SELECT 'Foo' UNION ALL
SELECT 'Bar' UNION ALL
SELECT 'Baz'

UPDATE Test2 SET ID+=1
Martin Smith
quelle
1
Sehr glatt. Lernen Sie jeden Tag etwas Neues (Übrigens habe ich dies auf SQLExpress getestet; trotz SWITCH TO wird anscheinend keine Partitionierung verwendet). Beachten Sie, dass die Tabelle ziemlich genau übereinstimmen muss (Indizes, FKs usw.)
Mark Sowul
1
Funktioniert die Umschaltmethode, wenn PK, FKs, Index und Ansichten auf der ursprünglichen Tabelle basieren? Werden sie mit dieser Methode brechen?
tj
1
@tj beim Erstellen des temporären Tabellenskripts aus der Quelltabelle einschließlich aller Indizes und Einschränkungen. Ändern Sie dann die Namen der Tabelle und die Einschränkungen, um Kollisionen zu vermeiden. Ansichten verursachen kein Problem, es sei denn, sie sind indiziert oder schemaboundiert.
Martin Smith
Funktioniert nicht, wenn die Tabelle einen Volltextsuchindex enthält. Ich nehme an, es ist möglich, es zu entfernen und anschließend neu zu erstellen, aber ich habe es nicht getestet.
Youen
26

Ändern Sie über die Benutzeroberfläche in SQL Server 2005 Manager die Spalte, und entfernen Sie die Eigenschaft für die automatische Nummerierung (Identität) der Spalte (wählen Sie die Tabelle aus, indem Sie mit der rechten Maustaste darauf klicken und "Design" auswählen).

Führen Sie dann Ihre Abfrage aus:

UPDATE table SET Id = Id + 1

Fügen Sie dann die Eigenschaft autonumber wieder zur Spalte hinzu.

Michael Pryor
quelle
3
Wenn Sie die Änderung manuell vornehmen, können Sie den Manager bitten, das SQL-Skript für die Änderung zu generieren (Tabellen-Designer-Menü, Änderungsskript generieren). Für diese Änderung wird eine neue Tabelle erstellt, die Daten werden kopiert und anschließend das Original gelöscht.
Robin Bennett
2
@tomaszs - Ein Codebeispiel, das auch effizienter ist, da es die Tabelle überhaupt nicht physisch neu erstellt (dies würde dies zweimal tun), ist in meiner späten Antwort hier
Martin Smith
Sie können die Identität nicht wieder hinzufügen. Machst du? stackoverflow.com/questions/1049210/…
Tomas Kubes
Vielen Dank. Das war perfekt. Ich hatte 1 Zeile in meiner Tabelle, die mir nicht bekannt war, daher hatten meine Bootstrap-Skripte für meine Tabelle IdAuto-Identity-Werte um 1.
Shiva
18

Erstens funktioniert die Einstellung von IDENTITY_INSERT für diese Angelegenheit nicht für das, was Sie benötigen (sie wird zum Einfügen neuer Werte verwendet, z. B. zum Einfügen von Lücken).

Wenn Sie den Vorgang über die GUI ausführen, wird lediglich eine temporäre Tabelle erstellt, alle Daten werden in eine neue Tabelle ohne Identitätsfeld kopiert und die Tabelle umbenannt.

Meilen D.
quelle
9

Dies kann mithilfe einer temporären Tabelle erfolgen.

Die Idee

  • Deaktivieren Sie Einschränkungen (falls Ihre ID von einem Fremdschlüssel referenziert wird)
  • Erstellen Sie eine temporäre Tabelle mit der neuen ID
  • Löschen Sie den Tabelleninhalt
  • Kopieren Sie die Daten aus der kopierten Tabelle in Ihre ursprüngliche Tabelle zurück
  • Aktivieren Sie zuvor deaktivierte Einschränkungen

SQL-Abfragen

Angenommen, Ihre testTabelle enthält zwei zusätzliche Spalten ( column2und column3) und es gibt zwei Tabellen mit Fremdschlüsselreferenzen, die testals foreign_table1und bezeichnet werden foreign_table2(da Probleme im wirklichen Leben niemals einfach sind).

alter table test nocheck constraint all;
alter table foreign_table1 nocheck constraint all;
alter table foreign_table2 nocheck constraint all;
set identity_insert test on;

select id + 1 as id, column2, column3 into test_copy from test v;
delete from test;
insert into test(id, column2, column3)
select id, column2, column3 from test_copy

alter table test check constraint all;
alter table foreign_table1 check constraint all;
alter table foreign_table2 check constraint all;
set identity_insert test off;
drop table test_copy;

Das ist es.

Manitra Andriamitondra
quelle
6

DBCC CHECKIDENT ('databaseenname.dbo.orders', RESEED, 999) Sie können mit diesem Befehl eine beliebige Identitätsspaltennummer ändern, und Sie können diese Feldnummer auch von jeder gewünschten Nummer aus starten. Beispiel: In meinem Befehl möchte ich beginnen 1000 (999 + 1) hoffen, dass es genug wäre ... viel Glück

Roxana
quelle
4

Wenn die Spalte keine PK ist, können Sie jederzeit eine NEUE Spalte in der Tabelle mit den inkrementierten Zahlen erstellen, das Original löschen und dann die neue in die alte ändern.

Ich bin gespannt, warum Sie dies möglicherweise tun müssen. Das meiste, was ich jemals mit Identitätsspalten zu tun hatte, war das Auffüllen von Zahlen. Am Ende habe ich DBCC CHECKIDENT (Tabellenname, RESEED, newnextnumber) verwendet.

Viel Glück!

Christopher Klein
quelle
1

Die Änderung der Identität kann abhängig von einer Reihe von Faktoren fehlschlagen, die sich hauptsächlich um die mit der ID-Spalte verknüpften Objekte / Beziehungen drehen. Es scheint, als ob DB-Design hier genauso problematisch ist, wie sich IDs selten oder nie ändern sollten (ich bin sicher, Sie haben Ihre Gründe und kaskasieren die Änderungen). Wenn Sie die IDs von Zeit zu Zeit wirklich ändern müssen, würde ich vorschlagen, entweder eine neue Dummy-ID-Spalte zu erstellen, die nicht der Primärschlüssel / die Autonummer ist, die Sie selbst verwalten und aus den aktuellen Werten generieren können. Alternativ wäre die obige Idee von Chrisotphers mein anderer Vorschlag, wenn Sie Probleme mit dem Zulassen der Identitätseinfügung haben.

Viel Glück

PS: Es schlägt nicht fehl, weil in der Reihenfolge, in der es ausgeführt wird, versucht wird, einen Wert in der Liste auf ein Element zu aktualisieren, das bereits in der Liste der IDs vorhanden ist. Wenn Sie sich an Strohhalme klammern, addieren Sie möglicherweise die Anzahl der Zeilen + 1, und wenn dies funktioniert, subtrahieren Sie die Anzahl der Zeilen: -S

Gerber
quelle
1

Wenn Sie die IDs gelegentlich ändern müssen, ist es wahrscheinlich am besten, keine Identitätsspalte zu verwenden. In der Vergangenheit haben wir Felder für die automatische Nummerierung manuell mithilfe einer 'Counters'-Tabelle implementiert, die die nächste ID für jede Tabelle verfolgt. IIRC haben wir dies getan, weil Identitätsspalten eine Datenbankbeschädigung in SQL2000 verursachten, aber das Ändern von IDs gelegentlich zum Testen nützlich war.

Robin Bennett
quelle
1

Sie können neue Zeilen mit geänderten Werten einfügen und dann alte Zeilen löschen. Im folgenden Beispiel ändern Sie die ID so, dass sie mit der PersonId des Fremdschlüssels übereinstimmt

SET IDENTITY_INSERT [PersonApiLogin] ON

INSERT INTO [PersonApiLogin](
       [Id]
      ,[PersonId]
      ,[ApiId]
      ,[Hash]
      ,[Password]
      ,[SoftwareKey]
      ,[LoggedIn]
      ,[LastAccess])
SELECT [PersonId]
      ,[PersonId]
      ,[ApiId]
      ,[Hash]
      ,[Password]
      ,[SoftwareKey]
      ,[LoggedIn]
      ,[LastAccess]
FROM [db304].[dbo].[PersonApiLogin]
GO

DELETE FROM [PersonApiLogin]
WHERE [PersonId] <> ID
GO
SET IDENTITY_INSERT [PersonApiLogin] OFF
GO
Tomas Kubes
quelle
1

Speichern Sie zuerst alle IDs und ändern Sie sie programmgesteuert auf die Werte, die Sie nicht möchten. Entfernen Sie sie dann aus der Datenbank und fügen Sie sie erneut mit etwas Ähnlichem ein:

use [Name.Database]
go
set identity_insert [Test] ON
insert into [dbo].[Test]
           ([Id])
     VALUES
           (2)
set identity_insert [Test] OFF

Für Masseneinsätze verwenden:

use [Name.Database]
go
set identity_insert [Test] ON
BULK INSERT [Test]
FROM 'C:\Users\Oscar\file.csv'
WITH (FIELDTERMINATOR = ';',
      ROWTERMINATOR = '\n',
      KEEPIDENTITY)
set identity_insert [Test] OFF

Beispieldaten aus file.csv:

2;
3;
4;
5;
6;

Wenn Sie nicht identity_insertausschalten, wird der folgende Fehler angezeigt:

Es kann kein expliziter Wert für die Identitätsspalte in die Tabelle 'Test' eingefügt werden, wenn IDENTITY_INSERT auf OFF gesetzt ist.

Ogglas
quelle
0

Ich habe einen guten Artikel gesehen, der mir im letzten Moment geholfen hat. Ich habe versucht, einige Zeilen in eine Tabelle einzufügen, die eine Identitätsspalte hatte, dies aber falsch gemacht hat und zurück löschen musste. Nachdem ich die Zeilen gelöscht hatte, wurde meine Identitätsspalte geändert. Ich habe versucht, einen Weg zu finden, um die eingefügte Spalte zu aktualisieren, aber - kein Glück. Also, während der Suche auf Google einen Link gefunden ..

  1. Löschte die falsch eingefügten Spalten
  2. Verwenden Sie Force Insert mit Identity On / Off (siehe unten).

http://beyondrelational.com/modules/2/blogs/28/posts/10337/sql-server-how-do-i-insert-an-explicit-value-into-an-identity-column-how-do- i-update-the-value-of-an.aspx

Balu
quelle
1
Ich bin mir nicht sicher, ob Sie die Frage hier wirklich beantwortet haben.
Andrew Barber
0

Sehr schöne Frage, zuerst müssen wir auf der IDENTITY_INSERT für die spezifische Tabelle, danach die Einfüge-Abfrage ausführen (muss den Spaltennamen angeben).

Hinweis: Nach dem Bearbeiten der die Identitätsspalte, nicht vergessen aus der IDENTITY. Wenn Sie dies nicht getan haben, können Sie die Identitätsspalte für keine andere Tabelle bearbeiten.

SET IDENTITY_INSERT Emp_tb_gb_Menu ON
     INSERT Emp_tb_gb_Menu(MenuID) VALUES (68)
SET IDENTITY_INSERT Emp_tb_gb_Menu OFF

http://allinworld99.blogspot.com/2016/07/how-to-edit-identity-field-in-sql.html

Asith Raj
quelle