(Avertissement: débutant PostgreSQL.)
OK, pour autant que je sache, ma fonction ressemble bien aux échantillons que j'ai vus. Quelqu'un peut-il m'indiquer comment faire en sorte que cela fonctionne?
create or replace function get_user_by_username(
username varchar(250),
online boolean
) returns setof record as $$
declare result record;
begin
if online then
update users
set last_activity = current_timestamp
where user_name = username;
end if;
return query
select
user_id,
user_name,
last_activity,
created,
email,
approved,
last_lockout,
last_login,
last_password_changed,
password_question,
comment
from
users
where
user_name = username
limit 1;
return;
end;
$$ language plpgsql;
si vous souhaitez créer une fonction renvoyant setof record, vous devrez définir les types de colonnes dans votre instruction select
Votre requête devrait ressembler à ceci:
select * from get_user_by_username('Username', True) as
f(user_id integer, user_name varchar, last_activity, varchar, created date, email archar, approved boolean, last_lockout timestamp, last_login timestamp,
last_password_changed timestamp, password_question varchar, comment varchar)
(vous devrez probablement changer les types de données)
Je préfère personnellement l'approche des types. il garantit que si la fonction est modifiée, toutes les requêtes retourneront des résultats corrects. Cela pourrait être pénible car chaque fois que vous modifiez les arguments de la fonction, vous devrez recréer/supprimer des types également.
Par exemple:
CREATE TYPE return_type as
(user_id integer,
user_name varchar,
last_activity varchar,
created timestamp,
email varchar,
approved boolean,
last_lockout timestamp ,
last_login timestamp,
last_password_changed timestamp,
password_question varchar,
comment varchar);
create or replace function get_user_by_username( username varchar(250), online
boolean) returns setof return_type as $$
declare _rec return_type;
begin
if online then
update users
set last_activity = current_timestamp
where user_name = username;
end if;
for _rec in select
user_id,
user_name,
last_activity,
created,
email,
approved,
last_lockout,
last_login,
last_password_changed,
password_question,
comment
from
users
where
user_name = username
limit 1
loop
return next _rec;
end loop
end;
$$ language plpgsql;
CREATE OR REPLACE FUNCTION get_user_by_username(_username text, _online bool)
RETURNS TABLE (
user_id int
,user_name text
,last_activity timestamp
, ... ) AS
$func$
BEGIN
IF _online THEN
RETURN QUERY
UPDATE users u
SET last_activity = current_timestamp
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$ LANGUAGE plpgsql;
Appel:
SELECT * FROM get_user_by_username('myuser', TRUE)
Vous aviez DECLARE result record;
Mais vous n'avez pas utilisé la variable. J'ai supprimé la cruauté.
Vous pouvez renvoyer l'enregistrement directement à partir de UPDATE
, ce qui est beaucoup plus rapide que d'appeler une instruction SELECT
supplémentaire. Utilisez RETURN QUERY
Et UPDATE
avec une clause RETURNING
.
Si l'utilisateur n'est pas _online
, Choisissez par défaut un SELECT
simple.
Si vous ne nommez pas les noms de colonne (tablename.columnname
) Dans les requêtes à l'intérieur de la fonction, méfiez-vous des conflits de dénomination entre les noms de colonne et les paramètres nommés, qui sont visibles (la plupart) partout dans une fonction.
Vous pouvez également éviter de tels conflits en utilisant des références de position ($n
) Pour les paramètres. Ou utilisez un préfixe que vous jamais utilisez pour les noms de colonne: comme un trait de soulignement (_username
).
Si users.username
est défini unique dans votre tableau, alors LIMIT 1
dans la deuxième requête est juste vicié.
Si ce n'est pas pas , alors le UPDATE
peut mettre à jour plusieurs lignes, ce qui est très probablement faux .
J'ai supposé un username
unique et j'ai supprimé la cruauté.
Définissez le type de retour de la fonction (comme @ertx l'a démontré) ou vous devrez fournir une liste de définition de colonne dans chaque appel de fonction, ce qui est gênant.
La création d'un type à cet effet (comme @ertx proposé) est une approche valide, mais probablement exagérée pour une seule fonction. C'était le chemin à parcourir dans les anciennes versions de PostgreSQL avant d'avoir RETURNS TABLE
à cet effet - comme démontré ci-dessus.
Vous n'avez pas besoin d'une boucle pour cette fonction simple.
Chaque fonction a besoin d'une déclaration de langage. LANGUAGE plpgsql
dans ce cas.
Il est probablement inutile de définir une restriction de longueur (varchar(250)
) pour le paramètre. J'ai simplifié pour taper text
.
Si vous souhaitez renvoyer toutes les colonnes de la table users
, il existe un moyen plus simple. PostgreSQL définit automatiquement un type composite du même nom pour chaque table . Dans ce cas, vous pouvez utiliser RETURNS SETOF users
Et simplifier considérablement la requête:
CREATE OR REPLACE FUNCTION get_user_by_username(_username text, _online bool)
RETURNS SETOF users 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$ LANGUAGE plpgsql;
Si vous avez besoin de quelque chose de plus "dynamique", pensez à: