Enklare alternativ till ditt postade svar. Borde prestera mycket bättre.
Denna funktion hämtar en rad från en given tabell (in_table_name
) och primärnyckelvärde (in_row_pk
), och infogar den som en ny rad i samma tabell, med några värden ersatta (in_override_values
). Det nya primärnyckelvärdet returneras som standard (pk_new
).
CREATE OR REPLACE FUNCTION f_clone_row(in_table_name regclass
, in_row_pk int
, in_override_values hstore
, OUT pk_new int) AS
$func$
DECLARE
_pk text; -- name of PK column
_cols text; -- list of names of other columns
BEGIN
-- Get name of PK column
SELECT INTO _pk a.attname
FROM pg_catalog.pg_index i
JOIN pg_catalog.pg_attribute a ON a.attrelid = i.indrelid
AND a.attnum = i.indkey[0] -- 1 PK col!
WHERE i.indrelid = 't'::regclass
AND i.indisprimary;
-- Get list of columns excluding PK column
_cols := array_to_string(ARRAY(
SELECT quote_ident(attname)
FROM pg_catalog.pg_attribute
WHERE attrelid = in_table_name -- regclass used as OID
AND attnum > 0 -- exclude system columns
AND attisdropped = FALSE -- exclude dropped columns
AND attname <> _pk -- exclude PK column
), ',');
-- INSERT cloned row with override values, returning new PK
EXECUTE format('
INSERT INTO %1$I (%2$s)
SELECT %2$s
FROM (SELECT (t #= $1).* FROM %1$I t WHERE %3$I = $2) x
RETURNING %3$I'
, in_table_name, _cols, _pk)
USING in_override_values, in_row_pk -- use override values directly
INTO pk_new; -- return new pk directly
END
$func$ LANGUAGE plpgsql;
Ring:
SELECT f_clone_row('t', 1, '"col1"=>"foo_new","col2"=>"bar_new"'::hstore);
-
Använd
regclass
som indataparametertyp, så endast giltiga tabellnamn kan användas till att börja med och SQL-injektion är utesluten. Funktionen misslyckas också tidigare och mer elegant om du skulle ange ett olagligt tabellnamn. -
Använd en
OUT
parameter (pk_new
) för att förenkla syntaxen. -
Du behöver inte räkna ut nästa värde för primärnyckeln manuellt. Den infogas automatiskt och returneras i efterhand. Det är inte bara enklare och snabbare, du slipper också bortkastade eller felaktiga sekvensnummer.
-
Använd
format()
för att förenkla sammansättningen av den dynamiska frågesträngen och göra den mindre felbenägen. Notera hur jag använder positionsparametrar för identifierare respektive strängar. -
Jag bygger på ditt underförstådda antagande att tillåtna tabeller har en enkla primärnyckelkolumn av typen heltal med en kolumnstandard . Vanligtvis
seriell
kolumner. -
Nyckelelementet i funktionen är den sista
INSERT
:- Slå samman åsidosättningsvärden med den befintliga raden med
#=
operatör i en undermarkering och dekomponera den resulterande raden omedelbart. - Då kan du bara välja relevanta kolumner i huvud
SELECT
. - Låt Postgres tilldela standardvärdet för PK och få tillbaka det med
RETURNING
klausul. - Skriv det returnerade värdet i
OUT
parameter direkt. - Allt gjort i ett enda SQL-kommando, det är vanligtvis snabbast.
- Slå samman åsidosättningsvärden med den befintliga raden med