Връщане на избраните колони
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 заявки