web-dev-qa-db-fra.com

PostgreSQL: passer la table comme argument dans la fonction

Je découvre TYPE dans PostgreSQL. J'ai un TABLE TYPE que certaines tables doivent respecter (interface). Par exemple:

CREATE TYPE dataset AS(
    ChannelId INTEGER
   ,GranulityIdIn INTEGER
   ,GranulityId INTEGER
   ,TimeValue TIMESTAMP
   ,FloatValue FLOAT
   ,Status BIGINT
   ,QualityCodeId INTEGER
   ,DataArray FLOAT[]
   ,DataCount BIGINT
   ,Performance FLOAT
   ,StepCount INTEGER
   ,TableRegClass regclass
   ,Tags TEXT[]
   ,WeightedMean FLOAT
   ,MeanData FLOAT
   ,StdData FLOAT
   ,MinData FLOAT
   ,MaxData FLOAT
   ,MedianData FLOAT
   ,Percentiles FLOAT[]
);

Je peux créer un tableau en utilisant ce modèle avec:

CREATE TABLE test OF dataset;

J'ai vu de nombreuses options dans le API , mais je suis un peu perdu. Je voudrais savoir s'il est possible d'affecter ce type à la fonction INPUT/OUTPUT paramètres.

Supposons que j'ai un FUNCTION appelé process qui reçoit un échantillon d'enregistrements d'un ensemble de données TABLEsource, les traite puis renvoie un TABLEsink avec le même TYPE.

C'est-à-dire que je voudrais savoir s'il est possible de créer un TYPE qui se comporte comme ceci:

CREATE FUNCTION process(
    input dataset
) RETURNS dataset
AS ...

Et cela peut être appelé comme ceci:

SELECT
    *
FROM
    source, process(input := source) AS sink;

Je me demande si c'est possible avec PostgreSQL, et je demande comment faire. Est-ce que quelqu'un d'entre vous le sait?


Voici un MWE de ce que j'essaie de faire:

DROP TABLE IF EXISTS source;
DROP FUNCTION IF EXISTS process(dataset);
DROP TYPE dataset;

CREATE TYPE dataset AS (
    id INTEGER
   ,t  TIMESTAMP
   ,x  FLOAT
);


CREATE TABLE source OF dataset;
ALTER TABLE source ADD PRIMARY KEY(Id);
INSERT INTO source VALUES
    (1, '2016-01-01 00:00:00', 10.0)
   ,(2, '2016-01-01 00:30:00', 11.0)
   ,(3, '2016-01-01 01:00:00', 12.0)
   ,(4, '2016-01-01 01:30:00',  9.0)
   ;

CREATE OR REPLACE FUNCTION process(
    _source dataset
)
RETURNS SETOF dataset
AS
$BODY$
SELECT * FROM source;
$BODY$
LANGUAGE SQL;

SELECT * FROM process(source);

Mais cela ne réussit pas, c'est comme si la source était perçue comme une colonne au lieu d'un SETOF RECORDS avec le type de jeu de données.

11
jlandercy

Votre paramètre _source dans le MWE ajouté n'est référencé nulle part. L'identifiant source dans le corps de la fonction n'a pas de soulignement de tête et est interprété indépendamment comme un nom de table constant.

Plus important encore, cela ne fonctionnerait pas de cette façon de toute façon. SQL permet uniquement de paramétrer les valeurs dans les instructions DML. Détails dans cette réponse connexe:

Solution

Vous pouvez toujours le faire fonctionner en utilisant du SQL dynamique avec EXECUTE dans une fonction plpgsql. Détails:

Ou essayez cette recherche de questions et réponses connexes

CREATE TYPE dataset AS (id integer, t timestamp, x float);
CREATE TABLE source OF dataset (PRIMARY KEY(Id));  -- add constraints in same command

INSERT INTO source VALUES
    (1, '2016-01-01 00:00:00', 10.0)
   ,(2, '2016-01-01 00:30:00', 11.0);

CREATE OR REPLACE FUNCTION process(_tbl regclass)
  RETURNS SETOF dataset AS
$func$
BEGIN
RETURN QUERY EXECUTE 'SELECT * FROM ' || _tbl;
END
$func$  LANGUAGE plpgsql;

SELECT * FROM process('source');  -- table name as string literal 

Vous pouvez même faire fonctionner cela pour n'importe quelle table donnée:

CREATE OR REPLACE FUNCTION process2(_tbl anyelement)
  RETURNS SETOF anyelement AS
$func$
BEGIN
RETURN QUERY EXECUTE 'SELECT * FROM ' || pg_typeof(_tbl);
END
$func$  LANGUAGE plpgsql;

SELECT * FROM process2(NULL::source);  -- note the call syntax!!

Explication détaillée:

13
Erwin Brandstetter

Cela fera ce que vous voulez sans besoin de tout SQL dynamique:

drop table if exists source cascade;
drop function if exists process(dataset) cascade;
drop type if exists dataset cascade;

create type dataset as (
    id integer
   ,t  timestamp
   ,x  float
);

create table source of dataset;
alter table source add primary key(id);
insert into source values
   (1, '2016-01-01 00:00:00', 10.0)
 , (2, '2016-01-01 00:30:00', 11.0)
;

create or replace function process(
    x_source dataset[]
)
returns setof dataset
as
$body$
select * from unnest(x_source);
$body$
language sql;

select *
from
  process(
    array(
      select
        row(id, t, x)::dataset
      from source
    )
  );

Autant que je sache (après avoir googlé longuement, car j'ai eu le même problème), vous ne pouvez pas passer directement une table à une fonction.

Cependant, comme illustré, vous pouvez transformer une table en tableau [] d'un type personnalisé composé de plusieurs types de base (similaire à une définition de table).

Ensuite, vous pouvez passer ce tableau et le réintégrer dans une table une fois que vous êtes dans la fonction.

0
Sam Fed