Comment puis-je utiliser le résultat de la requête stocké dans une variable de type d'enregistrement pour une autre requête dans la même fonction stockée? J'utilise Postgres 9.4.4.
Avec une table comme celle-ci:
create table test (id int, tags text[]);
insert into test values (1,'{a,b,c}'),
(2,'{c,d,e}');
J'ai écrit une fonction (simplifiée) comme ci-dessous:
CREATE OR REPLACE FUNCTION func(_tbl regclass)
RETURNS TABLE (t TEXT[], e TEXT[])
LANGUAGE plpgsql AS $$
DECLARE
t RECORD;
c INT;
BEGIN
EXECUTE format('SELECT id, tags FROM %s', _tbl) INTO t;
SELECT count(*) FROM t INTO c;
RAISE NOTICE '% results', c;
SELECT * FROM t;
END
$$;
... mais n'a pas fonctionné:
select func('test');
ERROR: 42P01: relation "t" does not exist LINE 1: SELECT count(*) FROM t ^ QUERY: SELECT count(*) FROM t CONTEXT: PL/pgSQL function func(regclass) line 7 at SQL statement LOCATION: parserOpenTable, parse_relation.c:986
Le principal malentendu: une variable record
contient une ligne single (ou est NULL), pas une table (0-n lignes d'un type bien connu). Il n'y a pas de "variables de table" dans Postgres ou PL/pgSQL . Selon la tâche, il existe différentes alternatives:
Par conséquent, vous ne pouvez pas affecter multiple des lignes à une variable de type record
. Dans cette déclaration:
EXECUTE format('SELECT id, tags FROM %s', _tbl) INTO t;
... Postgres assigne seulement la première ligne et rejette le reste. Puisque "le premier" n'est pas bien défini dans votre requête, vous vous retrouvez avec un choix arbitraire. Evidemment à cause du malentendu évoqué au départ.
Une variable record
ne peut pas non plus être utilisée à la place des tables dans les requêtes SQL. C'est la principale cause de l'erreur que vous obtenez:
la relation "t" n'existe pas
Il devrait être clair maintenant que count(*)
n'aurait aucun sens pour commencer, puisque t
n'est qu'un seul enregistrement/ligne - en plus d'être impossible de toute façon.
Enfin (même si le reste fonctionnerait), votre type de retour semble incorrect: . Puisque vous sélectionnez (t TEXT[], e TEXT[])
id, tags
Dans t
, vous souhaitez renvoyer quelque chose comme (id int, e TEXT[])
.
Ce que vous essayez de faire fonctionnerait comme ceci :
CREATE OR REPLACE FUNCTION func(_tbl regclass)
RETURNS TABLE (id int, e text[]) AS
$func$
DECLARE
_ct int;
BEGIN
EXECUTE format(
'CREATE TEMP TABLE tmp ON COMMIT DROP AS
SELECT id, tags FROM %s'
, _tbl);
GET DIAGNOSTICS _ct = ROW_COUNT; -- cheaper than another count(*)
-- ANALYZE tmp; -- if you are going to run multiple queries
RAISE NOTICE '% results', _ct;
RETURN QUERY TABLE tmp;
END
$func$ LANGUAGE plpgsql;
Appelez (notez la syntaxe!) :
SELECT * FROM func('test');
En relation:
Juste une preuve de concept. Pendant que vous sélectionnez la table entière, vous utiliserez simplement la table sous-jacente à la place. En réalité, vous aurez une clause WHERE
dans la requête ...
Attention à la non-concordance du type qui se cache, count()
renvoie bigint
, vous ne pouvez pas l'assigner à une variable integer
. Aurait besoin d'un transtypage: count(*)::int
.
Mais j'ai complètement remplacé ça, c'est moins cher de faire ça juste après EXECUTE
:
GET DIAGNOSTICS _ct = ROW_COUNT;
Pourquoi ANALYZE
?
En plus: les CTE en SQL simple peuvent souvent faire le travail: