Vidím dva hlavní problémy:
1. Nemůžete vložit UPDATE
do poddotazu vůbec . Můžete to vyřešit pomocí úpravy dat CTE
jako Patrick předvádí
, ale to je dražší a podrobnější, než je pro daný případ nutné.
2. Máte potenciálně nebezpečný konflikt názvů , která ještě nebyla řešena.
Lepší dotaz/funkce
Obal funkce SQL ponecháme prozatím stranou (k tomu se ještě vrátíme). Můžete použít jednoduchý UPDATE
s RETURNING
klauzule:
UPDATE tbl
SET value1 = 'something_new'
WHERE id = 123
RETURNING row_to_json(ROW(value1, value2));
RETURNING
klauzule umožňuje libovolné výrazy zahrnující sloupce aktualizovaného řádku. To je kratší a levnější než CTE upravující data.
Zbývající problém:konstruktor řádků ROW(...)
nezachovává názvy sloupců (což je známá slabina), takže v hodnotě JSON získáte obecné klíče:
row_to_json
{"f1":"something_new","f2":"what ever is in value2"}
V Postgres 9.3 byste potřebovali další funkci CTE k zapouzdření prvního kroku nebo přetypování na dobře definovaný typ řádku. Podrobnosti:
V Postgres 9.4 stačí použít json_build_object()
nebo json_object()
:
UPDATE tbl
SET value1 = 'something_new'
WHERE id = 123
RETURNING json_build_object('value1', value1, 'value2', value2);
Nebo:
...
RETURNING json_object('{value1, value2}', ARRAY[value1, value2]);
Nyní získáte původní názvy sloupců nebo cokoliv, co jste vybrali jako názvy klíčů:
row_to_json
{"value1":"something_new","value2":"what ever is in value2"}
Je snadné to zabalit do funkce, což nás přivádí k vašemu druhému problému ...
Konflikt názvů
Ve své původní funkci používáte stejné názvy pro parametry funkcí a názvy sloupců. Toto je obecně velmi špatný nápad . Museli byste důvěrně rozumět tomu, který identifikátor je v jakém rozsahu na prvním místě.
V tomto případě je výsledek naprostý nesmysl:
Create Or Replace Function ExampleTable_Update (id bigint, value1 text) Returns
...
Update ExampleTable
Set Value1 = value1
Where id = id
Returning Value1, Value2;
...
$$ Language SQL;
I když se zdá, že očekáváte, že druhý výskyt id
by odkazoval na parametr funkce, není tomu tak. Název sloupce je na prvním místě v rozsahu příkazu SQL, druhá instance odkazuje na sloupec. výsledkem je výraz, který je vždy true
kromě hodnot NULL v id
. V důsledku toho byste aktualizovali všechny řádky , což by mohlo vést k katastrofické ztrátě dat .Co je horší, možná si to ani neuvědomíte až později, protože funkce SQL vrátí jedna libovolný řádek definovaný pomocí RETURNING
klauzule funkce (vrací jedna řádek, nikoli sadu řádků).
V tomto konkrétním případě byste měli "štěstí", protože máte také value1 = value1
, který přepíše sloupec jeho již existující hodnotou, čímž efektivně nedělá .. nic velmi nákladným způsobem (pokud spouštěče něco nedělají). Možná budete zmateni, když získáte libovolný řádek s nezměněnou value1
jako výsledek.
Takže ne.
Vyhněte se potenciálním konfliktům pojmenování, jako je tento, pokud přesně nevíte, co děláte (což samozřejmě není tento případ). Jedna konvence, kterou mám rád, je přidat před názvy parametrů a proměnných ve funkcích podtržítko, zatímco názvy sloupců nikdy nezačínají podtržítkem. V mnoha případech stačí použít poziční odkazy, aby byly jednoznačné:$1
, $2
, ..., ale to obchází pouze jednu polovinu problému. Jakákoli metoda je dobrá, pokud se vyhnete konfliktům v názvech . Navrhuji:
CREATE OR REPLACE FUNCTION foo (_id bigint, _value1 text)
RETURNS json AS
$func$
UPDATE tbl
SET value1 = _value1
WHERE id = _id
RETURNING json_build_object('value1', value1, 'value2', value2);
$func$ LANGUAGE sql;
Všimněte si také, že to vrátí skutečnou hodnotu sloupce v value1
po UPDATE
, který může nebo nemusí být stejný jako váš vstupní parametr _value1
. Mohou existovat pravidla databáze nebo spouštěče rušící ...