Verschieben Sie SQL-Daten von einer Tabelle in eine andere

74

Ich habe mich gefragt, ob es möglich ist, alle Datenzeilen von einer Tabelle in eine andere zu verschieben, die einer bestimmten Abfrage entsprechen.

Zum Beispiel muss ich alle Tabellenzeilen von Tabelle1 nach Tabelle2 verschieben, wobei ihr Benutzername = 'X' und ihr Passwort = 'X' sind, damit sie nicht mehr in Tabelle1 angezeigt werden.

Ich verwende SQL Server 2008 Management Studio.

doubleplusgood
quelle

Antworten:

125

Sollte möglich sein, zwei Anweisungen innerhalb einer Transaktion zu verwenden, eine Einfügung und eine Löschung:

BEGIN TRANSACTION;
INSERT INTO Table2 (<columns>)
SELECT <columns>
FROM Table1
WHERE <condition>;

DELETE FROM Table1
WHERE <condition>;

COMMIT;

Dies ist die einfachste Form. Wenn Sie sich Sorgen machen müssen, dass zwischen den beiden Anweisungen neue übereinstimmende Datensätze in Tabelle1 eingefügt werden, können Sie eine hinzufügen and exists <in table2>.

Thorsten
quelle
1
Sie möchten jedoch sicherstellen, dass beide Anweisungen als einzelne Transaktion ausgeführt werden. Deaktivieren Sie beispielsweise die automatische Festschreibung und führen Sie nach dem Löschen nur eine einzige Festschreibung durch, wenn keine Fehler aufgetreten sind. Sie möchten wahrscheinlich nicht löschen, wenn die Einfügung fehlschlägt oder umgekehrt.
Jay
Richtig, aber selbst wenn sie als einzelne Transaktion ausgeführt werden, kann es zu Problemen kommen, wenn während der Ausführung der beiden Anweisungen Einfügungen auftreten. Es werden nicht zu viele Datenbanken so ausgeführt, dass "Lesevorgänge wiederholbar sind" innerhalb einer Transaktion.
Thorsten
Ich glaube, Sie können "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE" für die Transaktion verwenden, um sicherzustellen, dass Sie die neuen Datensätze nicht sehen
Mike L
1
@Dani: Nicht ganz sicher, was du meinst .. vielleicht ist es am einfachsten, wenn du dies als neue Frage mit einer detaillierteren Beschreibung / einem detaillierteren Beispiel postest? Wenn Sie möchten, können Sie dieser Antwort einen Kommentar hinzufügen, damit ich zusätzlich zu allen anderen Stackoverflow-Benutzern einen Blick darauf werfen kann.
Thorsten
1
Ich habe versehentlich alle Daten gelöscht, die ich verschieben wollte, da zu BEGIN TRANSACTION;Beginn dieses Beispiels keine vorhanden waren . Wäre es nicht eine gute Idee, dies dem Beispiel der Antwort hinzuzufügen?
Deantwo
47

Dies ist ein alter Beitrag, sorry, aber ich bin erst jetzt darauf gestoßen und wollte meine Lösung jedem geben, der eines Tages darüber stolpern könnte.

Wie einige erwähnt haben, kann das Ausführen eines INSERTund dann eines DELETEzu Integritätsproblemen führen. Eine Möglichkeit, dies zu umgehen und alles in einer einzigen Anweisung sauber auszuführen, besteht darin, die [deleted]temporäre Tabelle zu nutzen.

DELETE FROM [source]
OUTPUT [deleted].<column_list>
INTO [destination] (<column_list>)
that0th3rGuy
quelle
3
Bei einer DELETEAnweisung werden alle Datensätze zuerst in die [deleted]temporäre Tabelle geschrieben, bevor die DELETEKlausel aufgelöst wird. Von dort aus können sie verarbeitet - in diesem Fall in eine andere Tabelle eingefügt - und anschließend die Anweisung aufgelöst werden. Wenn der Verarbeitungsabschnitt der Anweisung fehlschlägt, wird die gesamte Anweisung beendet. nicht nur die INTOKlausel. Außerdem BEGIN TRY...BEGIN CATCHund ROLLBACK TRANSACTIONsind große vorbeugende Aussagen.
that0th3rGuy
6
Dies wird zu Problemen führen, wenn das Ziel an Fremdschlüsselbeziehungen beteiligt ist. Sie erhalten die Fehlermeldung: Die Zieltabelle '<Ziel>' der OUTPUT INTO-Klausel darf sich nicht auf beiden Seiten einer Beziehung (Primärschlüssel, Fremdschlüssel) befinden. Referenzbedingung '<Beschränkungsname>' gefunden.
Niels Harremoes
Können Sie hier erklären, wie das Schlüsselwort OUTPUT funktioniert? Es scheint, als würde versucht, den gelöschten Datensatz in die [gelöschte] Tabelle aufzunehmen, aber die [gelöschte] Tabelle existiert nicht (und existiert nie).
GreySage
1
@GreySage Das OUTPUTSchlüsselwort wird einfach verwendet, um ein eingereichtes Dataset an die nachfolgende Anweisung zurückzugeben, ist jedoch nur in einigen Fällen gültig, z. B. UPDATE( [inserted]) und DELETEwenn ich mich richtig erinnere. Die [deleted]Tabelle ist eine implizite, temporäre Tabelle - nicht genau sicher, wo sie sich befindet oder welcher Prozess sie verwaltet -, die auf die aktuelle Anweisungsausführung ausgerichtet ist. So wie Sie etwas tun können, SELECT [source].[column] INTO [destination]bei dem SELECTein Datensatz zurückgegeben und INTOempfangen wird, können Sie OUTPUTden gelöschten Datensatz an den INTOEmpfänger zurückgeben.
that0th3rGuy
Die OUTPUTAnweisung ist nicht standardmäßiges SQL, FWIW (z. B. funktioniert nicht in sqlite3).
Ijoseph
21

Alle diese Antworten führen dieselbe Abfrage für INSERT und DELETE aus. Wie bereits erwähnt, besteht die Gefahr, dass DELETE zwischen Anweisungen eingefügte Datensätze aufnimmt und bei komplexer Abfrage langsam sein kann (obwohl clevere Engines den zweiten Aufruf schnell ausführen sollten).

Der richtige Weg (vorausgesetzt, INSERT befindet sich in einer neuen Tabelle) besteht darin, DELETE für Tabelle1 mithilfe des Schlüsselfelds von Tabelle2 auszuführen.

Das Löschen sollte sein:

DELETE FROM tbl_OldTableName WHERE id in (SELECT id FROM tbl_NewTableName)

Entschuldigen Sie meine Syntax, ich springe zwischen den Motoren, aber Sie haben die Idee.

Ken Sands
quelle
Ich mag das wirklich .. und funktioniert perfekt mit der Verwendung von UniqueIDs als Schlüssel!
da_jokker
9

Ja ist es. Zuerst INSERT + SELECT und dann DELETE Orginals.

INSERT INTO Table2 (UserName,Password)
SELECT UserName,Password FROM Table1 WHERE UserName='X' AND Password='X'

dann löschen Sie die Ursprünge

DELETE FROM Table1 WHERE UserName='X' AND Password='X'

Möglicherweise möchten Sie UserIDoder einen anderen Primärschlüssel beibehalten, dann können Sie IDENTITY INSERTden Schlüssel beibehalten.

Weitere Informationen finden Sie unter SET IDENTITY_INSERT in MSDN

Pirho
quelle
7

Eine sauberere Darstellung dessen, was einige andere Antworten angedeutet haben:

DELETE sourceTable
OUTPUT DELETED.*
INTO destTable (Comma, separated, list, of, columns)
WHERE <conditions (if any)>
GreySage
quelle
5

Verwenden Sie diese einzelne SQL-Anweisung, für die kein Commit / Rollback mit mehreren Anweisungen erforderlich ist.

INSERT Table2 (
      username,password
) SELECT username,password
      FROM    (
           DELETE Table1
           OUTPUT
                   DELETED.username,
                   DELETED.password
           WHERE username = 'X' and password = 'X'
      ) AS RowsToMove ;

Funktioniert auf SQL Server und nimmt entsprechende Änderungen für MySQL vor

Dheerendra Kulkarni
quelle
3

Versuche dies

INSERT INTO TABLE2 (Cols...) SELECT Cols... FROM TABLE1 WHERE Criteria

Dann

DELETE FROM TABLE1 WHERE Criteria
Adriaan Stander
quelle
3

Sie sollten in der Lage sein, mit einer Unterabfrage in der INSERT-Anweisung.

INSERT INTO table1(column1, column2) SELECT column1, column2 FROM table2 WHERE ...;

gefolgt vom Löschen aus Tabelle1.

Denken Sie daran, es als einzelne Transaktion auszuführen, damit Sie den gesamten Vorgang zurücksetzen können, wenn etwas schief geht.

workmad3
quelle
2

Sie könnten dies versuchen:

SELECT * INTO tbl_NewTableName 
FROM tbl_OldTableName
WHERE Condition1=@Condition1Value

Führen Sie dann einen einfachen Löschvorgang aus:

DELETE FROM tbl_OldTableName
WHERE Condition1=@Condition1Value
royse41
quelle
2

Sie können "Logical Partitioning" verwenden , um Daten zwischen Tabellen zu wechseln:

Durch Aktualisieren der Partitionsspalte werden Daten automatisch in die andere Tabelle verschoben:

Hier ist das Beispiel:

CREATE TABLE TBL_Part1
(id  INT NOT NULL,
 val VARCHAR(10) NULL,
 PartitionColumn  VARCHAR(10) CONSTRAINT CK_Part1 CHECK(PartitionColumn = 'TBL_Part1'),
 CONSTRAINT TBL_Part1_PK PRIMARY KEY(PartitionColumn, id)
);

CREATE TABLE TBL_Part2
(id  INT NOT NULL,
 val VARCHAR(10) NULL,
 PartitionColumn  VARCHAR(10) CONSTRAINT CK_Part2 CHECK(PartitionColumn = 'TBL_Part2'),
 CONSTRAINT TBL_Part2_PK  PRIMARY KEY(PartitionColumn, id)
);

GO

CREATE VIEW TBL(id, val, PartitionColumn)
WITH SCHEMABINDING
AS
     SELECT id, val, PartitionColumn FROM dbo.TBL_Part1
     UNION ALL  
     SELECT id, val, PartitionColumn FROM dbo.TBL_Part2;

GO

--Insert sample to TBL ( will be inserted to Part1 )
INSERT INTO TBL
VALUES(1, 'rec1', 'TBL_Part1');

INSERT INTO TBL
VALUES(2, 'rec2', 'TBL_Part1');

GO

--Query sub table to verify
SELECT * FROM TBL_Part1

GO
--move the data to table TBL_Part2 by Logical Partition switching technique
UPDATE TBL
  SET
      PartitionColumn = 'TBL_Part2';

GO

--Query sub table to verify
SELECT * FROM TBL_Part2
abdkok
quelle
1

So geht's mit einer einzelnen Anweisung

WITH deleted_rows AS (
DELETE FROM source_table WHERE id = 1
RETURNING *
) 
INSERT INTO destination_table 
SELECT * FROM deleted_rows;

BEISPIEL:

    postgres=# select * from test1 ;
 id |  name
----+--------
  1 | yogesh
  2 | Raunak
  3 | Varun
(3 rows)


postgres=# select * from test2;
 id | name
----+------
(0 rows)


postgres=# WITH deleted_rows AS (
postgres(# DELETE FROM test1 WHERE id = 1
postgres(# RETURNING *
postgres(# )
postgres-# INSERT INTO test2
postgres-# SELECT * FROM deleted_rows;
INSERT 0 1


postgres=# select * from test2;
 id |  name
----+--------
  1 | yogesh
(1 row)

postgres=# select * from test1;
 id |  name
----+--------
  2 | Raunak
  3 | Varun
gpdude_
quelle
1
Dies verwendet eine falsche Syntax (in der Nähe von as und return) und funktioniert einfach nicht wie beabsichtigt.
GreySage
0

Wenn die beiden Tabellen dieselbe ID verwenden oder einen gemeinsamen EINZIGARTIGEN Schlüssel haben:

1) Fügen Sie den ausgewählten Datensatz in Tabelle 2 ein

INSERT INTO table2 SELECT * FROM table1 WHERE (conditions)

2) Löschen Sie den ausgewählten Datensatz aus Tabelle1, falls in Tabelle2 vorhanden

DELETE FROM table1 as A, table2 as B WHERE (A.conditions) AND  (A.ID = B.ID)
user1847437
quelle
0

Es wird eine Tabelle erstellt und alle Daten von der alten Tabelle in die neue Tabelle kopiert

SELECT * INTO event_log_temp FROM event_log

Und Sie können die alten Tabellendaten löschen.

DELETE FROM event_log

Rakesh Singh Balhara
quelle