Restituisce le colonne selezionate
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$;
Chiama:
SELECT * FROM get_user_by_username('myuser', true);
Avevi DECLARE result record;
ma non ha usato la variabile. Ho eliminato il cruft.
Puoi restituire il record direttamente da UPDATE
, che è molto più veloce che chiamare un ulteriore SELECT
dichiarazione. Usa RETURN QUERY
e UPDATE
con un RETURNING
clausola.
Se l'utente non è _online
, il valore predefinito è un semplice SELECT
. Questa è anche l'impostazione predefinita (sicura) se il secondo parametro viene omesso, il che è possibile solo dopo aver fornito a tale impostazione predefinita DEFAULT false
nella definizione della funzione.
Se non qualifichi la tabella, i nomi delle colonne (tablename.columnname
) nelle query all'interno della funzione, fai attenzione ai conflitti di denominazione tra nomi di colonna e parametri denominati, che sono visibili (per la maggior parte) ovunque all'interno di una funzione.
Puoi anche evitare tali conflitti utilizzando i riferimenti posizionali ($n
) per i parametri. Oppure usa un prefisso che mai utilizzare per i nomi delle colonne:come un trattino basso (_username
).
Se users.username
è definito unico nella tabella, quindi LIMIT 1
nella seconda query è solo cruft. Se lo è non , quindi UPDATE
può aggiornare più righe, il che molto probabilmente è errato . Presumo un username
univoco e taglia il rumore.
Definisci il tipo di restituzione della funzione (come dimostrato da @ertx) oppure devi fornire un elenco di definizioni di colonna con ogni chiamata di funzione, il che è imbarazzante.
La creazione di un tipo per quello scopo (come proposto da @ertx) è un approccio valido, ma probabilmente eccessivo per una singola funzione. Questa era la strada da percorrere nelle vecchie versioni di Postgres prima che avessimo RETURNS TABLE
a tale scopo - come dimostrato sopra.
Non hai bisogno di un ciclo per questa semplice funzione.
Ogni funzione necessita di una dichiarazione di lingua. LANGUAGE plpgsql
in questo caso.
Uso timestamptz
(timestamp with time zone
) invece di timestamp
(timestamp without time zone
), che è l'impostazione predefinita sana. Vedi:
- Ignora del tutto i fusi orari in Rails e PostgreSQL
Restituisci (insieme di) righe intere
Per restituire tutte le colonne della tabella esistente users
, c'è un modo più semplice. Postgres definisce automaticamente un tipo composito con lo stesso nome per ogni tabella . Usa semplicemente RETURNS SETOF users
per semplificare notevolmente la query:
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$;
Restituisci l'intera riga più l'aggiunta personalizzata
Per rispondere alla domanda aggiunta da TheRealChx101 in un commento qui sotto:
Cosa succede se si dispone anche di un valore calcolato oltre a un'intera tabella? 😑
Non così semplice, ma fattibile. Possiamo inviare l'intero tipo di riga come uno campo e aggiungi altro:
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$;
La "magia" è nella chiamata di funzione, dove (opzionalmente) scomponiamo il tipo di riga:
SELECT (users_row).*, custom_addition FROM get_user_by_username('foo', true);
db<>gioca qui (mostra tutto)
Se hai bisogno di qualcosa di più "dinamico", considera:
- Refactoring di una funzione PL/pgSQL per restituire l'output di varie query SELECT