Angenommen, ich habe eine Tabelle mit dem folgenden Namen people
, in der id
sich ein Primärschlüssel befindet:
+-----------+---------+---------+
| id | fname | lname |
| (integer) | (text) | (text) |
+===========+=========+=========+
| 1 | Daniel | Edwards |
| 2 | Fred | Holt |
| 3 | Henry | Smith |
+-----------+---------+---------+
Ich versuche, eine Abfrage zur Zeilenduplizierung zu schreiben, die robust genug ist, um Schemaänderungen an der Tabelle zu berücksichtigen. Jedes Mal, wenn ich der Tabelle eine Spalte hinzufüge, möchte ich nicht zurückgehen und die Duplizierungsabfrage ändern müssen.
Ich weiß, dass ich dies tun kann, wodurch die Datensatz-ID 2 dupliziert und dem duplizierten Datensatz eine neue ID zugewiesen wird:
INSERT INTO people (fname, lname) SELECT fname, lname FROM people WHERE id = 2;
Wenn ich jedoch eine age
Spalte hinzufüge , muss ich die Abfrage ändern, um auch die Altersspalte zu berücksichtigen.
Offensichtlich kann ich Folgendes nicht tun, da dadurch auch der Primärschlüssel dupliziert wird, was zu einem duplicate key value violates unique constraint
- führt. Und ich möchte nicht, dass sie trotzdem dieselbe ID haben:
INSERT INTO people SELECT * FROM people WHERE id = 2
Was wäre ein vernünftiger Ansatz zur Lösung dieser Herausforderung? Ich würde es vorziehen, mich von gespeicherten Prozeduren fernzuhalten, aber ich bin nicht zu 100% gegen sie, nehme ich an ...
quelle
age
eine Art Anti-Muster für eine Spalte ist. (Man sollte das lieber aufbewahrenbirthday
.)Antworten:
Einfach mit
hstore
Wenn Sie das zusätzliche Modul
hstore
installiert haben ( Anweisungen im Link unten ), gibt es eine überraschend einfache Möglichkeit, die Werte einzelner Felder zu ersetzen, ohne etwas über andere Spalten zu wissen:Einfaches Beispiel: duplizieren Sie die Zeile mit ,
id = 2
aber ersetzen2
mit3
:Einzelheiten:
Angenommen (es ist nicht in der Frage definiert), dass
people.id
es sich um eineserial
Spalte mit einer angehängten Sequenz handelt, möchten Sie den nächsten Wert aus der Sequenz. Wir können den Sequenznamen mit bestimmenpg_get_serial_sequence()
. Einzelheiten:Oder Sie können den Sequenznamen einfach fest codieren, wenn er sich nicht ändert.
Wir hätten diese Frage:
Was funktioniert , aber unter einer Schwäche im Postgres-Abfrageplaner leidet: Der Ausdruck wird für jede einzelne Spalte in der Zeile separat ausgewertet, wodurch Sequenznummern und Leistung verschwendet werden. Um dies zu vermeiden, verschieben Sie den Ausdruck in eine Unterabfrage und zerlegen Sie die Zeile nur einmal :
Wahrscheinlich am schnellsten für eine einzelne (oder wenige) Zeile (n) gleichzeitig.
json / jsonb
Wenn Sie keine
hstore
zusätzlichen Module installiert haben und nicht installieren können, können Sie einen ähnlichen Trick mitjson_populate_record()
oderjsonb_populate_record()
ausführen. Diese Funktion ist jedoch nicht dokumentiert und möglicherweise unzuverlässig.Vorübergehende temporäre Tabelle
Eine andere einfache Lösung wäre, ein vorübergehendes temporäres wie dieses zu verwenden:
Ich habe hinzugefügt
ON COMMIT DROP
, um die Tabelle am Ende der Transaktion automatisch zu löschen. Folglich habe ich die Operation auch in eine eigene Transaktion verpackt. Beides ist nicht unbedingt erforderlich.Dies bietet eine Vielzahl zusätzlicher Optionen - Sie können vor dem Einfügen alles mit der Zeile tun, sie wird jedoch aufgrund des Overheads beim Erstellen und Löschen einer temporären Tabelle etwas langsamer.
Diese Lösung funktioniert für eine einzelne Zeile oder für eine beliebige Anzahl von Zeilen gleichzeitig . Jede Zeile erhält automatisch einen neuen Standardwert aus der Sequenz.
Verwenden der Kurznotation (SQL-Standard)
TABLE people
.Dynamisches SQL
Für viele Zeilen gleichzeitig wird dynamisches SQL am schnellsten sein. Verketten Sie die Spalten aus der Systemtabelle
pg_attribute
oder aus dem Informationsschema und führen Sie sie dynamisch in einerDO
Anweisung aus oder schreiben Sie eine Funktion zur wiederholten Verwendung:Anruf:
Funktioniert für jede Tabelle mit einer Ganzzahlspalte mit dem Namen
id
. Sie können den Spaltennamen auch leicht dynamisch gestalten ...Vielleicht nicht Ihre erste Wahl, seit Sie wollten
stay away from stored procedures
, aber andererseits ist es sowieso keine "gespeicherte Prozedur" ...Verbunden:
Erweiterte Lösung
Eine
serial
Spalte ist ein Sonderfall. Wenn Sie mehr oder alle Spalten mit ihren jeweiligen Standardwerten füllen möchten, wird dies komplexer. Betrachten Sie diese verwandte Antwort:quelle
hstore
Der Ansatz funktioniert großartig, aber ich denke, ich werde mit demjsonb
Ansatz herumspielen, da ich mich bereits stark darauf verlasse. Vielen Dank für das tolle Schreiben, Erwin!... WHERE fieldA=1 ... SET ... fieldA=2 ...
Versuchen Sie, eine
trigger
Einfügung zu erstellen :In diesem Trigger machen Sie die ID NULL. Wenn der Trigger beendet ist, ist das Einfügen abgeschlossen und Postgres wird eine ID bereitstellen. Ich gehe davon aus, dass Sie die ID als definiert haben
DEFAULT NEXTVAL('A_SEQUENCE'::REGCLASS)
.quelle
NEXTVAL('A_SEQUENCE'::REGCLASS)
, geben Sie niemals selbst eine ID für einen neuen Eintrag an.Dynamic SQL Arbeitet super, ich suche das seit ein paar Jahren,
Wenn Sie mehr als eine ausgeschlossene Spalte haben, versuchen Sie es einfach.
quelle