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