Jednodušší alternativa k vaší zveřejněné odpovědi. Mělo by fungovat mnohem lépe.
Tato funkce načte řádek z dané tabulky (in_table_name
) a hodnotu primárního klíče (in_row_pk
) a vloží jej jako nový řádek do stejné tabulky, přičemž některé hodnoty budou nahrazeny (in_override_values
). Vrátí se nová výchozí hodnota primárního klíče (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;
Volejte:
SELECT f_clone_row('t', 1, '"col1"=>"foo_new","col2"=>"bar_new"'::hstore);
-
Použijte
regclass
jako typ vstupního parametru, takže pro začátek lze použít pouze platné názvy tabulek a je vyloučena injekce SQL. Funkce také selže dříve a elegantněji, pokud zadáte neplatný název tabulky. -
Použijte
OUT
parametr (pk_new
) pro zjednodušení syntaxe. -
Není třeba zjišťovat další hodnotu primárního klíče ručně. Vkládá se automaticky a poté se vrací. To je nejen jednodušší a rychlejší, ale také se vyhnete plýtvání nebo nefunkčním pořadovým číslům.
-
Použijte
format()
zjednodušit sestavování řetězce dynamického dotazu a snížit jeho náchylnost k chybám. Všimněte si, jak používám poziční parametry pro identifikátory a řetězce. -
Stavím na vašem implicitním předpokladu že povolené tabulky mají jeden sloupec primárního klíče typu integer s výchozí hodnotou sloupce . Obvykle
sériový
sloupce. -
Klíčovým prvkem funkce je poslední
INSERT
:- Sloučit přepsané hodnoty se stávajícím řádkem pomocí
#=
operátor v dílčím výběru a okamžitě rozložte výsledný řádek. - Pak můžete vybrat pouze relevantní sloupce v hlavním
SELECT
. - Nechte Postgres přiřadit výchozí hodnotu pro PK a získat ji zpět pomocí
RETURNING
doložka. - Zapište vrácenou hodnotu do
OUT
parametr přímo. - Vše se provádí jediným příkazem SQL, který je obecně nejrychlejší.
- Sloučit přepsané hodnoty se stávajícím řádkem pomocí