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 ersetzen 2
mit 3
:
INSERT INTO people
SELECT (p #= hstore('id', '3')).* FROM people p WHERE id = 2;
Einzelheiten:
Angenommen (es ist nicht in der Frage definiert), dasspeople.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:
INSERT INTO people
SELECT (p #= hstore('id', nextval(pg_get_serial_sequence('people', 'id'))::text)).*
FROM people p WHERE id = 2;
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 :
INSERT INTO people
SELECT (p1).*
FROM (
SELECT p #= hstore('id', nextval(pg_get_serial_sequence('people', 'id'))::text) AS p1
FROM people p WHERE id = 2
) sub;
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 mit json_populate_record()
oder jsonb_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:
BEGIN;
CREATE TEMP TABLE people_tmp ON COMMIT DROP AS
SELECT * FROM people WHERE id = 2;
UPDATE people_tmp SET id = nextval(pg_get_serial_sequence('people', 'id'));
INSERT INTO people TABLE people_tmp;
COMMIT;
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 einer DO
Anweisung aus oder schreiben Sie eine Funktion zur wiederholten Verwendung:
CREATE OR REPLACE FUNCTION f_row_copy(_tbl regclass, _id int, OUT row_ct int) AS
$func$
BEGIN
EXECUTE (
SELECT format('INSERT INTO %1$s(%2$s) SELECT %2$s FROM %1$s WHERE id = $1',
_tbl, string_agg(quote_ident(attname), ', '))
FROM pg_attribute
WHERE attrelid = _tbl
AND NOT attisdropped -- no dropped (dead) columns
AND attnum > 0 -- no system columns
AND attname <> 'id' -- exclude id column
)
USING _id;
GET DIAGNOSTICS row_ct = ROW_COUNT; -- directly assign OUT parameter
END
$func$ LANGUAGE plpgsql;
Anruf:
SELECT f_row_copy('people', 9);
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:
age
eine Art Anti-Muster für eine Spalte ist. (Man sollte das lieber aufbewahrenbirthday
.)