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

PostgreSQL:ГРЕШКА:42601:изисква се списък с дефиниции на колони за функции, връщащи запис

Връщане на избраните колони

CREATE OR REPLACE FUNCTION get_user_by_username(_username text
                                              , _online bool DEFAULT false)
  RETURNS TABLE (
    user_id int
  , user_name varchar
  , last_activity timestamptz
  )
  LANGUAGE plpgsql AS
$func$
BEGIN
   IF _online THEN
      RETURN QUERY
      UPDATE users u 
      SET    last_activity = current_timestamp  -- ts with time zone
      WHERE  u.user_name = _username
      RETURNING u.user_id
              , u.user_name
              , u.last_activity;
   ELSE
      RETURN QUERY
      SELECT u.user_id
           , u.user_name
           , u.last_activity
      FROM   users u
      WHERE  u.user_name = _username;
   END IF;
END
$func$;

Обадете се:

SELECT * FROM get_user_by_username('myuser', true);

Имахте DECLARE result record; но не използва променливата. Изтрих крафта.

Можете да върнете записа директно от UPDATE , което е много по-бързо от извикването на допълнителен SELECT изявление. Използвайте RETURN QUERY и UPDATE с RETURNING клауза.

Ако потребителят не е _online , по подразбиране е обикновен SELECT . Това също е (безопасно) по подразбиране, ако вторият параметър е пропуснат - което е възможно само след предоставяне на това по подразбиране с DEFAULT false в дефиницията на функцията.

Ако не квалифицирате имената на колони в таблицата (tablename.columnname ) в заявки във функцията, внимавайте сконфликти при имена между имената на колони и наименуваните параметри, които са видими (повечето) навсякъде във функцията.
Можете също да избегнете подобни конфликти, като използвате позиционни препратки ($n ) за параметри. Или използвайте префикс, който никога използвайте за имена на колони:като долно черта (_username ).

Ако users.username се дефинирауникален във вашата таблица, след това LIMIT 1 във втората заявка е просто cruft. Ако ене , след това UPDATE може да актуализира няколко реда, което най-вероятно е грешно . Предполагам, че има уникално username и отрежете шума.

Определете типа връщане на функцията (като @ertx демонстрира) или трябва да предоставите списък с дефиниции на колони с всяко извикване на функция, което е неудобно.

Създаването на тип за тази цел (както е предложен @ertx) е валиден подход, но вероятно прекомерно за една функция. Това беше начинът да вървим в старите версии на Postgres, преди да имаме RETURNS TABLE за тази цел - както е показано по-горе.

Виене се нуждаете от примка за тази проста функция.

Всяка функция се нуждае от езикова декларация. LANGUAGE plpgsql в този случай.

Използвам timestamptz (timestamp with time zone ) вместо timestamp (timestamp without time zone ), което е нормалното по подразбиране. Вижте:

  • Изцяло игнориране на часовите зони в Rails и PostgreSQL

Връщане (набор от) цели редове

За да върнете всички колони на съществуващата таблица users , има по-прост начин. Postgres автоматично дефинира съставен тип със същото име за всяка таблица . Просто използвайте RETURNS SETOF users за значително опростяване на заявката:

CREATE OR REPLACE FUNCTION get_user_by_username(_username text
                                              , _online bool DEFAULT false)
  RETURNS SETOF users
  LANGUAGE plpgsql AS
$func$
BEGIN
   IF _online THEN
      RETURN QUERY
      UPDATE users u 
      SET    last_activity = current_timestamp
      WHERE  u.user_name = _username
      RETURNING u.*;
   ELSE
      RETURN QUERY
      SELECT *
      FROM   users u
      WHERE  u.user_name = _username;
   END IF;
END
$func$;

Връщане на цял ред плюс персонализирано добавяне

За да отговорим на въпроса, добавен от TheRealChx101 в коментар по-долу:

Ами ако имате и изчислена стойност в допълнение към цяла таблица? 😑

Не толкова просто, но изпълнимо. Можем да изпратим целия тип ред като един поле и добавете още:

CREATE OR REPLACE FUNCTION get_user_by_username3(_username text
                                               , _online bool DEFAULT false)
  RETURNS TABLE (
    users_row users
  , custom_addition text
  )
  LANGUAGE plpgsql AS
$func$
BEGIN
   IF _online THEN
      RETURN QUERY
      UPDATE users u 
      SET    last_activity = current_timestamp  -- ts with time zone
      WHERE  u.user_name = _username
      RETURNING u  -- whole row
              , u.user_name || u.user_id;
   ELSE
      RETURN QUERY
      SELECT u, u.user_name || u.user_id
      FROM   users u
      WHERE  u.user_name = _username;
   END IF;
END
$func$;

„Магията“ е в извикването на функцията, където (по избор) разлагаме типа на реда:

SELECT (users_row).*, custom_addition FROM get_user_by_username('foo', true);

db<>цигулка тук (показват се всички)

Ако имате нужда от нещо по-динамично, помислете за:

  • Рефакторирайте функция PL/pgSQL, за да върнете резултатите от различни SELECT заявки


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. 7 неща, за които трябва да внимавате при внедряването на PostgreSQL

  2. невалидна последователност от байтове за кодиране на UTF8

  3. Изтриването на база данни трябва ли да се извършва не при никаква транзакция?

  4. Как да изброите таблици, засегнати от каскадно изтриване

  5. PostgreSQL:Сравнение на низове без регистри