PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Функция на Postgres, връщаща ред като JSON стойност

Виждам два основни проблема:
1. Вие не можете да поставите UPDATE в подзаявка изобщо . Можете да разрешите това с модифициране на данни CTE като Патрик демонстрира , но това е по-скъпо и многословно, отколкото е необходимо за конкретния случай.
2. Имате потенциално опасен конфликт на име , който все още не е разгледан.

По-добра заявка/функция

Оставяме обвивката на SQL функцията настрана за момента (ще се върнем към това). Можете да използвате просто UPDATE с RETURNING клауза:

UPDATE tbl
SET    value1 = 'something_new'
WHERE  id = 123
RETURNING row_to_json(ROW(value1, value2));

RETURNING клауза позволява произволни изрази, включващи колони от актуализирания ред. Това е по-кратко и по-евтино от CTE, модифициращ данни.

Оставащият проблем:конструкторът на ред ROW(...) не запазва имена на колони (което е известна слабост), така че получавате общи ключове във вашата JSON стойност:

row_to_json
{"f1":"something_new","f2":"what ever is in value2"}

В Postgres 9.3 ще ви трябва CTE друга функция за капсулиране на първата стъпка или преобразуване към добре дефиниран тип ред. Подробности:

В Postgres 9.4 просто използвайте json_build_object() или json_object() :

UPDATE tbl
SET    value1 = 'something_new'
WHERE  id = 123
RETURNING json_build_object('value1', value1, 'value2', value2);

Или:

...
RETURNING json_object('{value1, value2}', ARRAY[value1, value2]);

Сега получавате оригинални имена на колони или каквото сте избрали като имена на ключове:

row_to_json
{"value1":"something_new","value2":"what ever is in value2"}

Лесно е да обвиете това във функция, което ни води до втория ви проблем ...

Конфликт при именуване

Във вашата оригинална функция използвате идентични имена за функционални параметри и имена на колони. Като цяло това е много лоша идея . Ще трябва да разберете отблизо кой идентификатор е на първо място в кой обхват.

В разглеждания случай резултатът е пълна глупост:

Create Or Replace Function ExampleTable_Update (id bigint, value1 text) Returns 
...
    Update ExampleTable
    Set Value1 = value1
    Where id = id
    Returning Value1, Value2;
...
$$ Language SQL;

Въпреки че изглежда очаквате, че второто копие на id ще препраща към параметъра на функцията, но не го прави. Името на колоната е първо в обхвата на SQL оператор, а второто копие препраща към колоната. което води до израз, който винаги е true с изключение на NULL стойности в id . Следователно ще актуализирате всички редове , което може да доведе до катастрофална загуба на данни .Което е по-лошо, може дори да не го осъзнаете до по-късно, защото SQL функцията ще върне one произволен ред, както е дефиниран от RETURNING клауза на функцията (връща едно ред, а не набор от редове).

В този конкретен случай ще имате "късмет", защото също така имате value1 = value1 , който презаписва колоната с нейната предварително съществуваща стойност, като ефективно прави .. нищо по много скъп начин (освен ако тригерите не направят нещо). Може да сте озадачени да получите произволен ред с непроменена value1 като резултат.

Така че недейте.

Избягвайте потенциални конфликти при именуване като този, освен ако не знаете точно какво правите (което очевидно не е така). Една конвенция, която харесвам, е да добавям долна черта към имена на параметри и променливи във функциите, докато имената на колони никога не започват с долна черта. В много случаи можете просто да използвате позиционни препратки, за да бъдете недвусмислени:$1 , $2 , ..., но това заобикаля само половината от проблема. Всеки метод е добър, стига да избягвате конфликти в имената . Предлагам:

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;

Също така имайте предвид, че това връща действителната стойност на колоната в value1 след UPDATE , което може или не може да бъде същото като вашия входен параметър _value1 . Може да има правила за база данни или задействания, които пречат...



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Postgres тригер и заключване на редове

  2. Принудете Liquibase да картографира Blob към BYTEA на PostgreSQL

  3. postgresql:промяна на enum и знаци, актуализиране

  4. Опции за извличане на текущата (в момент на изпълнение на заявката) стойност на последователност

  5. Дата на форматиране за Postgresql