Vrátit vybrané sloupce
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$;
Volejte:
SELECT * FROM get_user_by_username('myuser', true);
Měli jste DECLARE result record;
ale nepoužil proměnnou. Smazal jsem cruft.
Záznam můžete vrátit přímo z UPDATE
, což je mnohem rychlejší než volání dalšího SELECT
prohlášení. Použijte RETURN QUERY
a UPDATE
s RETURNING
doložka.
Pokud uživatel není _online
, výchozí je prostý SELECT
. Toto je také (bezpečné) výchozí nastavení, pokud je vynechán druhý parametr – což je možné pouze po zadání tohoto výchozího nastavení s DEFAULT false
v definici funkce.
Pokud názvy sloupců nekvalifikujete podle tabulky (tablename.columnname
) v dotazech uvnitř funkce si dávejte pozor na konflikty pojmenování mezi názvy sloupců a pojmenovanými parametry, které jsou viditelné (většinou) všude uvnitř funkce.
Takovým konfliktům se můžete vyhnout také použitím pozičních odkazů ($n
) pro parametry. Nebo použijte předponu, kterou nikdy nepoužíváte použijte pro názvy sloupců:jako podtržítko (_username
).
Pokud users.username
je definováno jako jedinečné v tabulce a poté LIMIT 1
ve druhém dotazu je jen cruft. Pokud není a poté UPDATE
může aktualizovat více řádků, což je s největší pravděpodobností špatně . Předpokládám jedinečné username
a ořízněte hluk.
Definujte typ návratu funkce (jako předvedený @ertx) nebo musíte při každém volání funkce poskytnout seznam definic sloupců, což je nepříjemné.
Vytvoření typu pro tento účel (jako @ertx navrhovaný) je platný přístup, ale pravděpodobně přehnaný pro jednu funkci. To byl způsob, jak jít ve starých verzích Postgres, než jsme měli RETURNS TABLE
za tímto účelem – jak je ukázáno výše.
Nepotřebujete smyčku pro tuto jednoduchou funkci.
Každá funkce potřebuje deklaraci jazyka. LANGUAGE plpgsql
v tomto případě.
Používám timestamptz
(timestamp with time zone
) namísto timestamp
(timestamp without time zone
), což je rozumné výchozí nastavení. Viz:
- Úplné ignorování časových pásem v Rails a PostgreSQL
Vrátí (množinu) celých řádků
Chcete-li vrátit všechny sloupce existující tabulky users
, existuje jednodušší způsob. Postgres automaticky definuje složený typ se stejným názvem pro každou tabulku . Stačí použít RETURNS SETOF users
k výraznému zjednodušení dotazu:
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$;
Vrátit celý řádek plus vlastní přidání
Chcete-li odpovědět na otázku, kterou přidal TheRealChx101 v komentáři níže:
Co když máte kromě celé tabulky také vypočítanou hodnotu? 😑
Ne tak jednoduché, ale proveditelné. Celý typ řádku můžeme odeslat jako jeden pole a přidejte další:
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$;
"Kouzlo" je ve volání funkce, kde (volitelně) rozložíme typ řádku:
SELECT (users_row).*, custom_addition FROM get_user_by_username('foo', true);
db<>zde hrajte (zobrazuje se vše)
Pokud potřebujete něco „dynamičtějšího“, zvažte:
- Refaktorujte funkci PL/pgSQL tak, aby vrátila výstup různých SELECT dotazů