Återställ valda kolumner
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:
SELECT * FROM get_user_by_username('myuser', true);
Du hade DECLARE result record;
men använde inte variabeln. Jag tog bort cruften.
Du kan returnera posten direkt från UPDATE
, vilket är mycket snabbare än att anropa ytterligare SELECT
påstående. Använd RETURN QUERY
och UPDATE
med en RETURNING
klausul.
Om användaren inte är _online
, standard till en vanlig SELECT
. Detta är också den (säkra) standarden om den andra parametern utelämnas - vilket endast är möjligt efter att den förinställda standarden har angetts med DEFAULT false
i funktionsdefinitionen.
Om du inte tabellkvalificerar kolumnnamn (tablename.columnname
) i frågor i funktionen, var försiktig med namnkonflikter mellan kolumnnamn och namngivna parametrar, som är synliga (de flesta) överallt i en funktion.
Du kan också undvika sådana konflikter genom att använda positionsreferenser ($n
) för parametrar. Eller använd ett prefix som du aldrig använd för kolumnnamn:som ett understreck (_username
).
Om users.username
är definierad som unik i din tabell, sedan LIMIT 1
i den andra frågan är bara cruft. Om det är inte , sedan UPDATE
kan uppdatera flera rader, vilket med största sannolikhet är fel . Jag antar ett unikt username
och trimma ljudet.
Definiera returtypen för funktionen (som @ertx demonstrerat) eller så måste du tillhandahålla en kolumndefinitionslista med varje funktionsanrop, vilket är besvärligt.
Att skapa en typ för det ändamålet (som @ertx föreslagit) är ett giltigt tillvägagångssätt, men förmodligen överdrivet för en enskild funktion. Det var vägen att gå i gamla versioner av Postgres innan vi hade RETURNS TABLE
för det ändamålet - som visats ovan.
Du behöver ingen loop för denna enkla funktion.
Varje funktion behöver en språkdeklaration. LANGUAGE plpgsql
i det här fallet.
Jag använder timestamptz
(timestamp with time zone
) istället för timestamp
(timestamp without time zone
), vilket är den normala standarden. Se:
- Ignorerar tidszoner helt och hållet i Rails och PostgreSQL
Returnera (uppsättning av) hela rad(er)
För att returnera alla kolumner av den befintliga tabellen users
, det finns ett enklare sätt. Postgres definierar automatiskt en sammansatt typ med samma namn för varje tabell . Använd bara RETURNS SETOF users
för att avsevärt förenkla frågan:
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$;
Returnera hela raden plus anpassat tillägg
För att ta itu med frågan som lagts till av TheRealChx101 i en kommentar nedan:
Tänk om du också har ett beräknat värde utöver en hel tabell? 😑
Inte lika enkelt, men genomförbart. Vi kan skicka hela radtypen som en och lägg till fler:
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$;
"Magin" finns i funktionsanropet, där vi (valfritt) bryter ner radtypen:
SELECT (users_row).*, custom_addition FROM get_user_by_username('foo', true);
db<>spela här (visar alla)
Om du behöver något mer "dynamiskt", överväg:
- Refaktorera en PL/pgSQL-funktion för att returnera utdata från olika SELECT-frågor