Returner valgte kolonner
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$;
Ring til:
SELECT * FROM get_user_by_username('myuser', true);
Du havde DECLARE result record;
men brugte ikke variablen. Jeg slettede cruften.
Du kan returnere posten direkte fra UPDATE
, hvilket er meget hurtigere end at kalde en ekstra SELECT
udmelding. Brug RETURN QUERY
og UPDATE
med en RETURNING
klausul.
Hvis brugeren ikke er _online
, standard til en almindelig SELECT
. Dette er også den (sikre) standard, hvis den anden parameter er udeladt - hvilket kun er muligt efter at have angivet denne standard med DEFAULT false
i funktionsdefinitionen.
Hvis du ikke tabelkvalificerer kolonnenavne (tablename.columnname
) i forespørgsler inde i funktionen skal du være på vagt over for navnekonflikter mellem kolonnenavne og navngivne parametre, som er synlige (de fleste) overalt i en funktion.
Du kan også undgå sådanne konflikter ved at bruge positionsreferencer ($n
) for parametre. Eller brug et præfiks, som du aldrig brug til kolonnenavne:som en understregning (_username
).
Hvis users.username
er defineret unik i din tabel, derefter LIMIT 1
i den anden forespørgsel er bare cruft. Hvis det ikke er , derefter UPDATE
kan opdatere flere rækker, hvilket højst sandsynligt er forkert . Jeg antager et unikt username
og trim støjen.
Definer returtype for funktionen (som @ertx demonstreret), eller du skal levere en kolonnedefinitionsliste med hvert funktionskald, hvilket er akavet.
At oprette en type til det formål (som @ertx foreslået) er en gyldig tilgang, men sandsynligvis overkill for en enkelt funktion. Det var vejen at gå i gamle versioner af Postgres, før vi havde RETURNS TABLE
til det formål - som vist ovenfor.
Du behøver ikke en løkke for denne simple funktion.
Hver funktion har brug for en sprogerklæring. LANGUAGE plpgsql
i dette tilfælde.
Jeg bruger timestamptz
(timestamp with time zone
) i stedet for timestamp
(timestamp without time zone
), som er den fornuftige standard. Se:
- Ignorerer tidszoner helt i Rails og PostgreSQL
Returner (sæt af) hele række(r)
For at returnere alle kolonner af den eksisterende tabel users
, der er en enklere måde. Postgres definerer automatisk en sammensat type med samme navn for hver tabel . Brug bare RETURNS SETOF users
for at forenkle forespørgslen markant:
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$;
Returner hele rækken plus tilpasset tilføjelse
For at besvare spørgsmålet tilføjet af TheRealChx101 i en kommentar nedenfor:
Hvad hvis du også har en beregnet værdi ud over en hel tabel? 😑
Ikke så simpelt, men gennemførligt. Vi kan sende hele rækketypen som én felt, og tilføj flere:
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$;
"Magien" er i funktionskaldet, hvor vi (valgfrit) dekomponerer rækketypen:
SELECT (users_row).*, custom_addition FROM get_user_by_username('foo', true);
db<>spil her (viser alle)
Hvis du har brug for noget mere "dynamisk", så overvej:
- Refaktorer en PL/pgSQL-funktion for at returnere output fra forskellige SELECT-forespørgsler