Zwróć wybrane kolumny
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$;
Zadzwoń:
SELECT * FROM get_user_by_username('myuser', true);
Masz DECLARE result record;
ale nie użył zmiennej. Usunąłem cruft.
Możesz zwrócić rekord bezpośrednio z UPDATE
, co jest znacznie szybsze niż wywołanie dodatkowego SELECT
oświadczenie. Użyj RETURN QUERY
i UPDATE
z RETURNING
klauzula.
Jeśli użytkownik nie jest _online
, domyślnie zwykły SELECT
. Jest to również (bezpieczna) wartość domyślna, jeśli pominięto drugi parametr - co jest możliwe tylko po podaniu tej wartości domyślnej z DEFAULT false
w definicji funkcji.
Jeśli nie kwalifikujesz nazw kolumn w tabeli (tablename.columnname
) w zapytaniach wewnątrz funkcji uważaj na konflikty nazw między nazwami kolumn a nazwanymi parametrami, które są widoczne (w większości) wszędzie wewnątrz funkcji.
Można również uniknąć takich konfliktów, używając odwołań pozycyjnych ($n
) dla parametrów. Lub użyj prefiksu, którego nigdy użyj dla nazw kolumn:jak podkreślenie (_username
).
Jeśli users.username
jest zdefiniowany jako unikalny w tabeli, a następnie LIMIT 1
w drugim zapytaniu to po prostu cruft. Jeśli to nie , a następnie UPDATE
może zaktualizować wiele wierszy, co najprawdopodobniej jest błędem . Zakładam unikalną username
i przytnij hałas.
Zdefiniuj typ zwrotu funkcji (jak zademonstrowano @ertx) lub musisz dostarczyć listę definicji kolumn przy każdym wywołaniu funkcji, co jest niewygodne.
Tworzenie typu do tego celu (jak zaproponowany @ertx) jest prawidłowym podejściem, ale prawdopodobnie przesadą dla pojedynczej funkcji. To była droga w starych wersjach Postgresa, zanim pojawiła się RETURNS TABLE
w tym celu - jak pokazano powyżej.
Nie potrzebujesz pętli dla tej prostej funkcji.
Każda funkcja wymaga deklaracji języka. LANGUAGE plpgsql
w tym przypadku.
Używam timestamptz
(timestamp with time zone
) zamiast timestamp
(timestamp without time zone
), co jest rozsądną wartością domyślną. Zobacz:
- Całkowite ignorowanie stref czasowych w Rails i PostgreSQL
Zwróć (zestaw) całych wierszy
Aby zwrócić wszystkie kolumny istniejącej tabeli users
, jest prostszy sposób. Postgres automatycznie definiuje typ złożony o tej samej nazwie dla każdej tabeli . Wystarczy użyć RETURNS SETOF users
aby znacznie uprościć zapytanie:
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$;
Zwróć cały wiersz plus niestandardowy dodatek
Aby odpowiedzieć na pytanie dodane przez TheRealChx101 w poniższym komentarzu:
Co zrobić, jeśli oprócz całej tabeli masz również obliczoną wartość?
Nie tak proste, ale wykonalne. Możemy wysłać cały typ wiersza jako jeden pole i dodaj więcej:
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$;
„Magia” znajduje się w wywołaniu funkcji, gdzie (opcjonalnie) rozkładamy typ wiersza:
SELECT (users_row).*, custom_addition FROM get_user_by_username('foo', true);
db<>graj tutaj (pokazuje wszystko)
Jeśli potrzebujesz czegoś bardziej „dynamicznego”, rozważ:
- Refaktoryzuj funkcję PL/pgSQL, aby zwrócić dane wyjściowe różnych zapytań SELECT