Ausgewählte Spalten zurückgeben
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$;
Aufruf:
SELECT * FROM get_user_by_username('myuser', true);
Sie hatten DECLARE result record;
habe die Variable aber nicht verwendet. Ich habe die Cruft gelöscht.
Sie können den Datensatz direkt aus dem UPDATE
zurückgeben , was viel schneller ist, als ein zusätzliches SELECT
aufzurufen Erklärung. Verwenden Sie RETURN QUERY
und UPDATE
mit einem RETURNING
Klausel.
Wenn der Benutzer nicht _online
ist , standardmäßig ein einfaches SELECT
. Dies ist auch die (sichere) Standardeinstellung, wenn der zweite Parameter weggelassen wird – was nur möglich ist, nachdem diese Standardeinstellung mit DEFAULT false
versehen wurde in der Funktionsdefinition.
Wenn Sie Spaltennamen nicht tabellenqualifizieren (tablename.columnname
) in Abfragen innerhalb der Funktion, achten Sie auf Namenskonflikte zwischen Spaltennamen und benannten Parametern, die (fast) überall innerhalb einer Funktion sichtbar sind.
Sie können solche Konflikte auch vermeiden, indem Sie Positionsreferenzen verwenden ($n
) für Parameter. Oder verwenden Sie ein Präfix, das Sie nie verwenden Verwenden Sie für Spaltennamen:wie einen Unterstrich (_username
).
Wenn users.username
ist eindeutig definiert in Ihrer Tabelle, dann LIMIT 1
in der zweiten Abfrage ist nur cruft. Wenn es nicht ist , dann das UPDATE
kann mehrere Zeilen aktualisieren, was höchstwahrscheinlich falsch ist . Ich gehe von einem eindeutigen username
aus und trimme das Rauschen.
Definieren Sie den Rückgabetyp der Funktion (wie @ertx demonstriert) oder Sie müssen bei jedem Funktionsaufruf eine Spaltendefinitionsliste angeben, was umständlich ist.
Das Erstellen eines Typs für diesen Zweck (wie von @ertx vorgeschlagen) ist ein gültiger Ansatz, aber wahrscheinlich übertrieben für eine einzelne Funktion. Das war der Weg in alten Versionen von Postgres, bevor wir RETURNS TABLE
hatten zu diesem Zweck - wie oben gezeigt.
Sie brauchen keine Schleife für diese einfache Funktion.
Jede Funktion benötigt eine Sprachdeklaration. LANGUAGE plpgsql
in diesem Fall.
Ich verwende timestamptz
(timestamp with time zone
) anstelle von timestamp
(timestamp without time zone
), was die vernünftige Standardeinstellung ist. Siehe:
- Zeitzonen in Rails und PostgreSQL komplett ignorieren
Gib (Satz von) ganzen Zeilen zurück
Um alle Spalten zurückzugeben der bestehenden Tabelle users
, es gibt einen einfacheren Weg. Postgres definiert automatisch einen zusammengesetzten Typ mit demselben Namen für jede Tabelle . Verwenden Sie einfach RETURNS SETOF users
um die Abfrage erheblich zu vereinfachen:
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$;
Ganze Zeile plus benutzerdefinierter Zusatz zurückgeben
Um die von TheRealChx101 in einem Kommentar unten hinzugefügte Frage zu beantworten:
Was ist, wenn Sie neben einer ganzen Tabelle auch einen berechneten Wert haben? 😑
Nicht so einfach, aber machbar. Wir können den gesamten Zeilentyp als einen senden Feld und fügen Sie weitere hinzu:
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$;
Die "Magie" liegt im Funktionsaufruf, wo wir (optional) den Zeilentyp zerlegen:
SELECT (users_row).*, custom_addition FROM get_user_by_username('foo', true);
db<>hier fummeln (zeigt alle)
Wenn Sie etwas "Dynamischeres" brauchen, ziehen Sie in Betracht:
- Refaktorisieren Sie eine PL/pgSQL-Funktion, um die Ausgabe verschiedener SELECT-Abfragen zurückzugeben