Widzę dwa główne problemy:
1. Nie możesz umieścić UPDATE
w podzapytanie w ogóle . Możesz rozwiązać ten problem za pomocą modyfikacji danych CTE
jak Patrick demonstruje
, ale jest to droższe i bardziej szczegółowe niż jest to konieczne w rozpatrywanej sprawie.
2. Masz potencjalnie niebezpieczny konflikt nazw , który nie został jeszcze rozwiązany.
Lepsze zapytanie / funkcja
Zostawmy na razie wrapper funkcji SQL (do tego wrócimy). Możesz użyć prostej UPDATE
z RETURNING
klauzula:
UPDATE tbl
SET value1 = 'something_new'
WHERE id = 123
RETURNING row_to_json(ROW(value1, value2));
RETURNING
Klauzula zezwala na dowolne wyrażenia obejmujące kolumny zaktualizowanego wiersza. To krótsze i tańsze niż CTE modyfikujące dane.
Pozostały problem:konstruktor wierszy ROW(...)
nie zachowuje nazw kolumn (co jest znaną słabością), więc otrzymujesz klucze ogólne w swojej wartości JSON:
row_to_json
{"f1":"something_new","f2":"what ever is in value2"}
W Postgresie 9.3 będziesz potrzebował CTE innej funkcji, aby zawrzeć pierwszy krok lub rzutowanie na dobrze zdefiniowany typ wiersza. Szczegóły:
W Postgresie 9.4 po prostu użyj json_build_object()
lub json_object()
:
UPDATE tbl
SET value1 = 'something_new'
WHERE id = 123
RETURNING json_build_object('value1', value1, 'value2', value2);
Lub:
...
RETURNING json_object('{value1, value2}', ARRAY[value1, value2]);
Teraz otrzymujesz oryginalne nazwy kolumn lub cokolwiek, co wybrałeś jako nazwy kluczy:
row_to_json
{"value1":"something_new","value2":"what ever is in value2"}
Łatwo zapakować to w funkcję, która prowadzi nas do twojego drugiego problemu...
Konflikt nazw
W oryginalnej funkcji używasz identycznych nazw dla parametrów funkcji i nazw kolumn. To ogólnie bardzo zły pomysł . Musisz dokładnie zrozumieć, który identyfikator jest pierwszy w jakim zakresie.
W omawianym przypadku wynik jest kompletnym nonsensem:
Create Or Replace Function ExampleTable_Update (id bigint, value1 text) Returns
...
Update ExampleTable
Set Value1 = value1
Where id = id
Returning Value1, Value2;
...
$$ Language SQL;
Chociaż wydaje się, że drugie wystąpienie id
odwołuje się do parametru funkcji, tak nie jest. Nazwa kolumny znajduje się jako pierwsza w zakresie instrukcji SQL, druga instancja odwołuje się do kolumny. w wyniku czego powstaje wyrażenie, które zawsze jest true
z wyjątkiem wartości NULL w id
. W związku z tym zaktualizujesz wszystkie wiersze , co może doprowadzić do katastroficznej utraty danych .Co gorsza, możesz nawet nie zdawać sobie z tego sprawy dopiero później, ponieważ funkcja SQL zwróci jeden dowolny wiersz zdefiniowany przez RETURNING
klauzula funkcji (zwraca jeden wiersz, a nie zestaw wierszy).
W tym konkretnym przypadku miałbyś „szczęście”, ponieważ masz również value1 = value1
, który nadpisuje kolumnę jej wcześniejszą wartością, skutecznie nie robiąc .. nic w bardzo kosztowny sposób (chyba że wyzwalacze coś zrobią). Możesz być zaskoczony, aby uzyskać dowolny wiersz z niezmienioną value1
w rezultacie.
Więc nie rób tego.
Unikaj potencjalnych konfliktów nazw takich jak ten, chyba że wiesz dokładnie, co robisz (co oczywiście nie jest prawdą). Jedną z konwencji, którą lubię, jest dodawanie podkreślenia do nazw parametrów i zmiennych w funkcjach, podczas gdy nazwy kolumn nigdy nie zaczynają się od podkreślenia. W wielu przypadkach możesz po prostu użyć referencji pozycyjnych, aby były jednoznaczne:$1
, $2
..., ale to omija tylko połowę problemu. Każda metoda jest dobra, o ile unikasz konfliktów nazw . Proponuję:
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;
Pamiętaj również, że zwraca to rzeczywistą wartość kolumny w value1
po UPDATE
, który może, ale nie musi być taki sam, jak parametr wejściowy _value1
. Mogą istnieć reguły bazy danych lub wyzwalacze zakłócające ...