Ein expliziter Wert für die Identitätsspalte in der Tabelle kann nur angegeben werden, wenn eine Spaltenliste verwendet wird und IDENTITY_INSERT ON SQL Server ist

187

Ich versuche diese Abfrage zu machen

INSERT INTO dbo.tbl_A_archive
  SELECT *
  FROM SERVER0031.DB.dbo.tbl_A

aber auch nachdem ich gerannt bin

set identity_insert dbo.tbl_A_archive on

Ich erhalte diese Fehlermeldung

Ein expliziter Wert für die Identitätsspalte in der Tabelle 'dbo.tbl_A_archive' kann nur angegeben werden, wenn eine Spaltenliste verwendet wird und IDENTITY_INSERT aktiviert ist.

tbl_Aist eine riesige Tabelle in Zeilen und Breite, dh sie hat viele Spalten. Ich möchte nicht alle Spalten manuell eingeben müssen. Wie kann ich das zum Laufen bringen?

jhowe
quelle
Ich habe übrigens schon einen Verbindungsserver eingerichtet!
Jhowe
4
Ich habe auch dieses Problem, außer dass das "Einfügen in X select * Y" kein Problem war, bis ich das Tabellenschema geändert habe
FistOfFury

Antworten:

78

Zusammenfassung

Mit SQL Server können Sie keinen expliziten Wert in eine Identitätsspalte einfügen, es sei denn, Sie verwenden eine Spaltenliste. Somit haben Sie folgende Möglichkeiten:

  1. Erstellen Sie eine Spaltenliste (entweder manuell oder mithilfe von Werkzeugen, siehe unten).

ODER

  1. Erstellen Sie die Identitätsspalte in tbl_A_archiveeiner regulären Spalte ohne Identität : Wenn Ihre Tabelle eine Archivtabelle ist und Sie immer einen expliziten Wert für die Identitätsspalte angeben, warum benötigen Sie überhaupt eine Identitätsspalte? Verwenden Sie stattdessen einfach ein reguläres int.

Details zu Lösung 1

Anstatt

SET IDENTITY_INSERT archive_table ON;

INSERT INTO archive_table
  SELECT *
  FROM source_table;

SET IDENTITY_INSERT archive_table OFF;

du musst schreiben

SET IDENTITY_INSERT archive_table ON;

INSERT INTO archive_table (field1, field2, ...)
  SELECT field1, field2, ...
  FROM source_table;

SET IDENTITY_INSERT archive_table OFF;

mit field1, field2, ...den Namen aller Spalten in Ihren Tabellen. Wenn Sie diese Liste von Spalten automatisch generieren möchten, schauen Sie sich Daves Antwort oder Andomars Antwort an .


Details zu Lösung 2

Leider ist es nicht möglich, den Typ einer Identity-Int-Spalte einfach in eine Nicht-Identity-Int-Spalte zu ändern. Grundsätzlich haben Sie folgende Möglichkeiten:

  • Wenn die Archivtabelle noch keine Daten enthält, löschen Sie die Spalte und fügen Sie eine neue ohne Identität hinzu.

ODER

  • Verwenden Sie SQL Server Management Studio, um die Eigenschaft Identity Specification/ (Is Identity)der Identitätsspalte in Ihrer Archivtabelle auf festzulegen No. Hinter den Kulissen wird dies ein Skript erstellen , um die Tabelle und kopieren Sie vorhandene Daten neu zu erstellen, so, das zu tun, werden Sie auch auf ungesetzt benötigen Tools/ Options/ Designers/ Table and Database Designers/ Prevent saving changes that require table re-creation.

ODER

Heinzi
quelle
1
Zu Ihrer untersten Lösung: Wenn die Spalte "IDENTITY (1, 1)" oder ähnliches hat, muss er sie auch vorübergehend entfernen.
Aleksandr Khomenko
1
@AleksandrKhomenko: Ja, das habe ich mit "mach [es] zu einer regulären ( nicht identitätsbezogenen ) int-Spalte" gemeint .
Heinzi
1
Entschuldigung für die Verwirrung. Ich meinte eigentlich, dass er auch die Auto-Inkrement- Eigenschaft entfernen muss . Im Fall von SQL-Server heißt es "IDENTITY (1, 1)" - Ihre Antwort ist absolut richtig. Aber MySQL und Oracle haben andere Befehle dafür (und es wird nicht offensichtlich, schauen Sie bitte auf w3schools.com/sql/sql_autoincrement.asp )
Aleksandr Khomenko
2
Dies fehlt im Detail. Ich verstehe, dass es für einige vielleicht rot ist, für andere aber Unsinn
DJ.
1
@DJ.: Ich habe einige Details hinzugefügt.
Heinzi
332
SET IDENTITY_INSERT tableA ON

Sie müssen eine Spaltenliste für Ihre INSERT-Anweisung erstellen:

INSERT Into tableA ([id], [c2], [c3], [c4], [c5] ) 
SELECT [id], [c2], [c3], [c4], [c5] FROM tableB

nicht wie "INSERT Into tableA SELECT ........"

SET IDENTITY_INSERT tableA OFF
Ehsan
quelle
17
+1 ... Sie benötigen nur explizite Spalten in der SELECT-Klausel der Abfrage. Sie können ein Sternchen in der INSERT-Klausel der Abfrage behalten
MacGyver
8
... es sei denn, das Ziel hat eine Identitätsspalte und die Quelltabelle hat keine Identitätsspalte
MacGyver
1
Diese Antwort ist etwas klarer als Heinzi, weil sie SET IDENTITY_INSERT
Marty
4
+1 Funktioniert wie ein Zauber! "Sie können ein Sternchen in der INSERT-Klausel der Abfrage behalten" funktioniert hier jedoch nicht.
Shai Alon
1
Ich habe diesen Fehler erhalten, als die Spalten in der select-Klausel nicht mit der Zieltabelle übereinstimmten. Um dies sicherzustellen, funktionierte die Beilage für mich
Jose
39

Wenn Sie SQL Server Management Studio verwenden, müssen Sie die Spaltenliste nicht selbst eingeben. Klicken Sie einfach mit der rechten Maustaste auf die Tabelle im Objekt-Explorer und wählen Sie Skripttabelle als -> AUSWÄHLEN in -> Neues Abfrage-Editor-Fenster .

Wenn dies nicht der Fall ist, sollte eine ähnliche Abfrage als Ausgangspunkt hilfreich sein:

SELECT SUBSTRING(
    (SELECT ', ' + QUOTENAME(COLUMN_NAME)
        FROM INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_NAME = 'tbl_A'
        ORDER BY ORDINAL_POSITION
        FOR XML path('')),
    3,
    200000);
Dave Cluderay
quelle
2
Dies kann in einer Variablen ausgewählt und in dynamischen SQL-Abfragen verwendet werden. Du hast meinen Tag gerettet. Vielen Dank!
Pawel Cioch
1
Vielen Dank! Ich habe das damit gemacht gist.github.com/timabell/0ddd6a69565593f907c7
Tim Abell
Beeindruckend. Ich suchte verzweifelt danach, um meine Abfrage dynamisch zu erstellen. Überrascht, das hier zu sehen. Danke Kumpel.
Yasin Bilir
22

Stimmen Sie Heinzis Antwort zu. Für die erste zweite Option ist hier eine Abfrage, die eine durch Kommas getrennte Liste von Spalten in einer Tabelle generiert:

select name + ', ' as [text()] 
from sys.columns 
where object_id = object_id('YourTable') 
for xml path('')

Bei großen Tischen kann dies viel Schreibarbeit sparen :)

Andomar
quelle
@ Danke, sehr hilfreich. :)
Chirag Thakar
Manchmal braucht man nur eine pragmatische Lösung. Danke dir!
Tobias Feil
15

Wenn die "Archiv" -Tabelle eine exakte Kopie Ihrer Haupttabelle sein soll, würde ich nur vorschlagen, dass Sie die Tatsache entfernen, dass die ID eine Identitätsspalte ist. Auf diese Weise können Sie sie einfügen.

Alternativ können Sie Identitätseinfügungen für die Tabelle mit der folgenden Anweisung zulassen und nicht zulassen

SET IDENTITY_INSERT tbl_A_archive ON
--Your inserts here
SET IDENTITY_INSERT tbl_A_archive OFF

Wenn die Identitätsspalte unverändert funktionieren soll, können Sie den gespeicherten Prozess jederzeit ausführen.

sp_columns tbl_A_archive 

Dadurch erhalten Sie alle Spalten aus der Tabelle, die Sie dann ausschneiden und in Ihre Abfrage einfügen können. (Dies ist fast IMMER besser als die Verwendung eines *)

Robin Day
quelle
11

Für die SQL-Anweisung müssen Sie auch die Spaltenliste angeben. Zum Beispiel.

INSERT INTO tbl (idcol1,col2) VALUES ( value1,value2)

anstatt

INSERT INTO tbl VALUES ( value1,value2)
Mohsin Mahmood
quelle
2
Dies ist bei der Erklärung des OP nicht der Fall. VALUES ist nicht erforderlich, wenn auf INSERT INTO eine SELECT-Anweisung folgt. Bitte überarbeiten oder entfernen Sie Ihre Antwort.
Gary
4

Beides funktioniert, aber wenn Sie mit # 1 immer noch einen Fehler erhalten, wählen Sie # 2

1)

SET IDENTITY_INSERT customers ON
GO
insert into dbo.tbl_A_archive(id, ...)
SELECT Id, ...
FROM SERVER0031.DB.dbo.tbl_A

2)

SET IDENTITY_INSERT customers ON
GO
insert into dbo.tbl_A_archive(id, ...)
VALUES(@Id,....)
Chirag Thakar
quelle
4
Dies ist wirklich nicht der richtige Weg, wenn das OP mehrere Datensätze einfügt. Sie ignorieren "tbl_A ist eine riesige Tabelle in Zeilen und Breite, dh sie enthält viele Spalten. Ich möchte nicht alle Spalten manuell eingeben müssen."
Gary
4

Um alle Spaltennamen in eine durch Kommas getrennte Liste für eine Select-Anweisung für die für diese Frage genannten Lösungen einzufügen, verwende ich die folgenden Optionen, da sie etwas weniger ausführlich sind als die meisten Antworten hier. Obwohl die meisten Antworten hier immer noch vollkommen akzeptabel sind.

1)

SELECT column_name + ',' 
FROM   information_schema.columns 
WHERE  table_name = 'YourTable'

2) Dies ist wahrscheinlich der einfachste Ansatz zum Erstellen von Spalten, wenn Sie über SQL Server SSMS verfügen.

1) Gehen Sie im Objekt-Explorer zur Tabelle und klicken Sie auf das + links neben dem Tabellennamen oder doppelklicken Sie auf den Tabellennamen, um die Unterliste zu öffnen.

2) Ziehen Sie den Spaltenunterordner in den Hauptabfragebereich und die gesamte Spaltenliste wird automatisch für Sie eingefügt.

Ryan
quelle
3

Sie müssen den Spaltennamen angeben, den Sie einfügen möchten, wenn eine Identitätsspalte vorhanden ist. Der Befehl sieht also wie folgt aus:

SET IDENTITY_INSERT DuplicateTable ON

INSERT Into DuplicateTable ([IdentityColumn], [Column2], [Column3], [Column4] ) 
SELECT [IdentityColumn], [Column2], [Column3], [Column4] FROM MainTable

SET IDENTITY_INSERT DuplicateTable OFF

Wenn Ihre Tabelle viele Spalten enthält, rufen Sie diesen Spaltennamen mit diesem Befehl ab.

SELECT column_name + ','
FROM   information_schema.columns 
WHERE  table_name = 'TableName'
for xml path('')

(nach dem Entfernen des letzten Kommas (',')) Kopieren Sie einfach den Namen der letzten Spalte.

Rousonur Jaman
quelle
3

Das sollte funktionieren. Ich bin gerade auf Ihr Problem gestoßen:

SET IDENTITY_INSERT dbo.tbl_A_archive ON;
INSERT INTO     dbo.tbl_A_archive (IdColumn,OtherColumn1,OtherColumn2,...)
SELECT  *
FROM        SERVER0031.DB.dbo.tbl_A;
SET IDENTITY_INSERT dbo.tbl_A_archive OFF;

Leider scheint es, dass Sie eine Liste der Spalten einschließlich der Identitätsspalte benötigen, um Datensätze einzufügen, die die Identität angeben. Sie MÜSSEN jedoch nicht die Spalten in SELECT auflisten. Wie von @Dave Cluderay vorgeschlagen, führt dies zu einer formatierten Liste, die Sie kopieren und einfügen können (wenn weniger als 200000 Zeichen vorhanden sind).

Ich habe die USE hinzugefügt, da ich zwischen Instanzen wechsle.

USE PES
SELECT SUBSTRING(
    (SELECT ', ' + QUOTENAME(COLUMN_NAME)
        FROM INFORMATION_SCHEMA.COLUMNS
        WHERE TABLE_NAME = 'Provider'
        ORDER BY ORDINAL_POSITION
        FOR XML path('')),
    3,
    200000);
Gary
quelle
3
SET IDENTITY_INSERT tableA ON

INSERT Into tableA ([id], [c2], [c3], [c4], [c5] ) 
SELECT [id], [c2], [c3], [c4], [c5] FROM tableB

So nicht

INSERT INTO tableA
SELECT * FROM tableB

SET IDENTITY_INSERT tableA OFF
Hase
quelle
2

Dieses Codeausschnitt zeigt, wie in eine Tabelle eingefügt wird, wenn die Spalte Identitätsprimärschlüssel aktiviert ist.

SET IDENTITY_INSERT [dbo].[Roles] ON
GO
insert into Roles (Id,Name) values(1,'Admin')
GO
insert into Roles (Id,Name) values(2,'User')
GO
SET IDENTITY_INSERT [dbo].[Roles] OFF
GO
AminGolmahalle
quelle
1

Zum Beispiel, wenn Sie Ihre Werte über eine gespeicherte Prozedur von einer Tabelle in eine andere einfügen möchten. Ich habe dies und das benutzt , letzteres ist fast wie Andomars Antwort.

CREATE procedure [dbo].[RealTableMergeFromTemp]
    with execute as owner
AS
BEGIN
BEGIN TRANSACTION RealTableDataMerge
SET XACT_ABORT ON

    DECLARE @columnNameList nvarchar(MAX) =
     STUFF((select ',' + a.name
      from sys.all_columns a
      join sys.tables t on a.object_id = t.object_id 
       where t.object_id = object_id('[dbo].[RealTable]') 
    order by a.column_id
    for xml path ('')
    ),1,1,'')

    DECLARE @SQLCMD nvarchar(MAX) =N'INSERT INTO [dbo].[RealTable] (' + @columnNameList + N') SELECT * FROM [#Temp]'

    SET IDENTITY_INSERT [dbo].[RealTable] ON;
    exec(@sqlcmd)
    SET IDENTITY_INSERT [dbo].[RealTable] OFF

COMMIT TRANSACTION RealTableDataMerge
END

GO
Rubenisme
quelle
0

Es gibt eine oder mehrere Spalten mit der Eigenschaft zum automatischen Inkrementieren, oder der Wert dieses Attributs wird als Einschränkung berechnet. Sie versuchen, diese Spalte zu ändern.

Es gibt zwei Möglichkeiten, dies zu lösen: 1) Erwähnen Sie andere Spalten explizit und legen Sie nur deren Werte fest. Der Primärschlüssel oder der Spaltenwert für die automatische Inkrementierung werden automatisch festgelegt.

2) Sie können INDENTITY_INSERT aktivieren und dann Ihre Einfügeabfrage ausführen. IDENTITY_INSERT wird schließlich deaktiviert.

Vorschlag: Befolgen Sie den ersten Schritt, da dies ein geeigneterer und effizienter Ansatz ist.

Weitere Informationen finden Sie in diesem Artikel über SQL-Helper .

Shanu Dey
quelle
0

Stellen Sie sicher, dass die Spaltennamen, Datentypen und die Reihenfolge in der Tabelle, aus der Sie Datensätze auswählen, genau mit der Zieltabelle übereinstimmen. Der einzige Unterschied sollte sein, dass die Zieltabelle eine Identitätsspalte als erste Spalte hat, die nicht in der Quelltabelle vorhanden ist.

Ich hatte ein ähnliches Problem, als ich "INSERT INTO table_Dest SELECT * FROM table_source_linked_server_excel" ausführte. Die Tabellen hatten 115 Spalten.

Ich hatte zwei solche Tabellen, in denen ich Daten aus Excel (als Verbindungsserver) in Tabellen in der Datenbank lud. In Datenbanktabellen hatte ich eine Identitätsspalte mit dem Namen 'id' hinzugefügt, die im Quell-Excel nicht vorhanden war. Für eine Tabelle wurde die Abfrage erfolgreich ausgeführt und in einer anderen wurde die Fehlermeldung "Ein expliziter Wert für die Identitätsspalte in der Tabelle kann nur angegeben werden, wenn eine Spaltenliste verwendet wird und IDENTITY_INSERT ON SQL Server ist" angezeigt. Dies war rätselhaft, da das Szenario für beide Abfragen genau gleich war. Also habe ich das untersucht und festgestellt, dass in der Abfrage, in der ich einen Fehler mit INSERT INTO .. ​​SELECT * erhalten habe:

  1. Einige der Spaltennamen in der Quelltabelle wurden geändert, obwohl die Werte korrekt waren
  2. Es gab einige zusätzliche Spalten über die tatsächlichen Datenspalten hinaus, die von SELECT * ausgewählt wurden. Ich habe dies mithilfe der Option "Skripttabelle als> Auswählen zu> neues Abfragefenster" in der Excel-Quelltabelle (unter Verbindungsservern) festgestellt. Es gab eine versteckte Spalte direkt nach der letzten Spalte in Excel, obwohl sie keine Daten enthielt. Ich habe diese Spalte in der Excel-Quelltabelle gelöscht und gespeichert.

Nach den beiden oben genannten Änderungen wurde die Abfrage für INSERT INTO ... SELECT * erfolgreich ausgeführt. Die Identitätsspalte in der Zieltabelle generierte erwartungsgemäß Identitätswerte für jede eingefügte Zeile.

Obwohl die Zieltabelle möglicherweise eine Identitätsspalte enthält, die nicht in der Quelltabelle vorhanden ist, wird INSERT INTO .. ​​SELECT * erfolgreich ausgeführt, wenn die Namen, Datentypen und Spaltenreihenfolgen in Quelle und Ziel genau identisch sind.

Hoffe es hilft jemandem.

Uttam
quelle
-1

Ich denke, dieser Fehler tritt aufgrund der Nichtübereinstimmung mit der Anzahl der Spalten in der Tabellendefinition und der Anzahl der Spalten in der Einfügeabfrage auf. Auch die Länge der Spalte wird mit dem eingegebenen Wert weggelassen. Überprüfen Sie einfach die Tabellendefinition, um dieses Problem zu beheben

Codemaker
quelle
Arbeitete für mich: Ich habe vergessen, der Tabelle aus dem Entwurf eine Spalte hinzuzufügen. Gut, dass Sie dies zuerst überprüfen, bevor Sie andere Antworten versuchen.
Exel Gamboa