Eenvoudiger alternatief voor uw geposte antwoord. Zou veel beter moeten presteren.
Deze functie haalt een rij op uit een bepaalde tabel (in_table_name
) en primaire sleutelwaarde (in_row_pk
), en voegt deze als nieuwe rij in dezelfde tabel in, waarbij enkele waarden zijn vervangen (in_override_values
). De nieuwe primaire sleutelwaarde wordt standaard geretourneerd (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;
Bel:
SELECT f_clone_row('t', 1, '"col1"=>"foo_new","col2"=>"bar_new"'::hstore);
-
Gebruik
regclass
als invoerparametertype, dus alleen geldige tabelnamen kunnen worden gebruikt om mee te beginnen en SQL-injectie is uitgesloten. De functie faalt ook eerder en mooier als je een illegale tabelnaam opgeeft. -
Gebruik een
OUT
parameter (pk_new
) om de syntaxis te vereenvoudigen. -
Het is niet nodig om de volgende waarde voor de primaire sleutel handmatig te achterhalen. Het wordt automatisch ingevoegd en achteraf teruggestuurd. Dat is niet alleen eenvoudiger en sneller, u voorkomt ook verspilde of niet-bestaande volgnummers.
-
Gebruik
format()
om de assemblage van de dynamische queryreeks te vereenvoudigen en deze minder foutgevoelig te maken. Merk op hoe ik positionele parameters gebruik voor respectievelijk identifiers en strings. -
Ik bouw voort op uw impliciete aanname dat toegestane tabellen een enkele primaire sleutelkolom van het type integer hebben met een standaardkolom . Typisch
serial
kolommen. -
Het belangrijkste element van de functie is de laatste
INSERT
:- Override-waarden samenvoegen met de bestaande rij met behulp van de
#=
operator in een subselectie en ontleed de resulterende rij onmiddellijk. - Vervolgens kunt u alleen relevante kolommen selecteren in het hoofdvenster
SELECT
. - Laat Postgres de standaardwaarde voor de PK toewijzen en terugkrijgen met de
RETURNING
clausule. - Schrijf de geretourneerde waarde in de
OUT
parameter direct. - Alles gedaan in een enkele SQL-opdracht, dat is over het algemeen het snelst.
- Override-waarden samenvoegen met de bestaande rij met behulp van de