Enklere alternativ til dit indsendte svar. Bør præstere meget bedre.
Denne funktion henter en række fra en given tabel (in_table_name
) og primær nøgleværdi (in_row_pk
), og indsætter den som en ny række i den samme tabel, med nogle værdier erstattet (in_override_values
). Den nye primærnøgleværdi returneres 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 til:
SELECT f_clone_row('t', 1, '"col1"=>"foo_new","col2"=>"bar_new"'::hstore);
-
Brug
regclass
som inputparametertype, så kun gyldige tabelnavne kan bruges til at begynde med, og SQL-injektion er udelukket. Funktionen fejler også tidligere og mere yndefuldt, hvis du skulle angive et ulovligt tabelnavn. -
Brug en
OUT
parameter (pk_new
) for at forenkle syntaksen. -
Det er ikke nødvendigt at finde ud af den næste værdi for den primære nøgle manuelt. Det indsættes automatisk og returneres efter kendsgerningen. Det er ikke kun enklere og hurtigere, du undgår også spildte eller ude af rækkefølge numre.
-
Brug
format()
for at forenkle samlingen af den dynamiske forespørgselsstreng og gøre den mindre fejltilbøjelig. Bemærk, hvordan jeg bruger positionsparametre til henholdsvis identifikatorer og strenge. -
Jeg bygger på din implicitte antagelse at tilladte tabeller har en enkelt primær nøglekolonne af typen heltal med en kolonnestandard . Typisk
serial
kolonner. -
Funktionens nøgleelement er den sidste
INSERT
:- Flet tilsidesættelsesværdier med den eksisterende række ved hjælp af
#=
operatør i et undervælg og nedbryd den resulterende række med det samme. - Så kan du kun vælge relevante kolonner i hoved-
SELECT
. - Lad Postgres tildele standardværdien for PK'en og få den tilbage med
RETURNING
klausul. - Skriv den returnerede værdi i
OUT
parameter direkte. - Alt gjort i en enkelt SQL-kommando, det er generelt hurtigst.
- Flet tilsidesættelsesværdier med den eksisterende række ved hjælp af