Å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