PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Funkcja Postgres zwracająca wiersz jako wartość JSON

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 ...



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PostgreSQL - utwórz kolumnę auto-inkrementacji dla klucza innego niż podstawowy

  2. jak zapytać o min lub max inet/cidr za pomocą postgres

  3. [Wideo] Integracja danych z PostgreSQL

  4. jak usunąć wszystkie bazy danych poza kilkoma w postgresie

  5. Po przywróceniu mojej bazy danych serial usunięty z kolumny w Postgresql