Geselecteerde kolommen retourneren
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$;
Bel:
SELECT * FROM get_user_by_username('myuser', true);
U had DECLARE result record;
maar heb de variabele niet gebruikt. Ik heb de cruft verwijderd.
U kunt het record rechtstreeks terugsturen vanuit de UPDATE
, wat veel sneller is dan het aanroepen van een extra SELECT
uitspraak. Gebruik RETURN QUERY
en UPDATE
met een RETURNING
clausule.
Als de gebruiker niet _online
. is , standaard een gewone SELECT
. Dit is ook de (veilige) standaard als de tweede parameter wordt weggelaten - wat alleen mogelijk is na het verstrekken van die standaard met DEFAULT false
in de functiedefinitie.
Als u kolomnamen niet kwalificeert voor tabellen (tablename.columnname
) in zoekopdrachten binnen de functie, wees op uw hoede voor naamgevingsconflicten tussen kolomnamen en benoemde parameters, die (de meeste) overal in een functie zichtbaar zijn.
U kunt dergelijke conflicten ook vermijden door positionele verwijzingen te gebruiken ($n
) voor parameters. Of gebruik een voorvoegsel dat u nooit gebruik voor kolomnamen:zoals een onderstrepingsteken (_username
).
Als users.username
is gedefinieerd uniek in uw tabel, dan LIMIT 1
in de tweede vraag is gewoon cruft. Als het niet is , dan de UPDATE
kan meerdere rijen bijwerken, wat hoogstwaarschijnlijk fout is . Ik ga uit van een unieke username
en trim het geluid.
Definieer het retourtype van de functie (zoals @ertx demonstreerde) of je moet een kolomdefinitielijst opgeven bij elke functieaanroep, wat onhandig is.
Het creëren van een type voor dat doel (zoals @ertx voorgesteld) is een geldige benadering, maar waarschijnlijk overkill voor een enkele functie. Dat was de manier om te gaan in oude versies van Postgres voordat we RETURNS TABLE
hadden voor dat doel - zoals hierboven aangetoond.
U geen lus nodig voor deze eenvoudige functie.
Elke functie heeft een taaldeclaratie nodig. LANGUAGE plpgsql
in dit geval.
Ik gebruik timestamptz
(timestamp with time zone
) in plaats van timestamp
(timestamp without time zone
), wat de normale standaard is. Zie:
- Tijdzones volledig negeren in Rails en PostgreSQL
Retourneer (set van) hele rij(en)
Om alle kolommen terug te geven van de bestaande tabel users
, is er een eenvoudigere manier. Postgres definieert automatisch een samengesteld type met dezelfde naam voor elke tafel . Gebruik gewoon RETURNS SETOF users
om de zoekopdracht enorm te vereenvoudigen:
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$;
Gehele rij retourneren plus aangepaste toevoeging
Om de vraag te beantwoorden die door TheRealChx101 in een opmerking hieronder is toegevoegd:
Wat als je naast een hele tabel ook een berekende waarde hebt?
Niet zo simpel, maar wel te doen. We kunnen het hele rijtype verzenden als één veld en voeg meer toe:
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$;
De "magie" zit in de functieaanroep, waar we (optioneel) het rijtype ontleden:
SELECT (users_row).*, custom_addition FROM get_user_by_username('foo', true);
db<>viool hier (laat alles zien)
Als u iets "dynamischer" nodig heeft, overweeg dan:
- Refactor een PL/pgSQL-functie om de uitvoer van verschillende SELECT-query's te retourneren