PostgreSQL: passer la table comme argument dans la fonction


11

Je découvre TYPEdans PostgreSQL. J'en ai un TABLE TYPEque 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 l' API , mais je suis un peu perdu. Je voudrais savoir s'il est possible d'assigner ce type aux INPUT/OUTPUTparamètres de fonction .

Disons que j'ai un FUNCTIONappelé processqui reçoit un échantillon d'enregistrements d'un ensemble de données TABLE source, les traite puis renvoie un TABLE sinkavec le même TYPE.

C'est-à-dire que j'aimerais savoir s'il est possible de créer un TYPEqui 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'une SETOF RECORDSavec le type d'ensemble de données.

Réponses:


13

Votre paramètre _sourcedans le MWE ajouté n'est référencé nulle part. L'identifiant sourcedans 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 des valeurs dans les instructions DML. Détails dans cette réponse connexe:

Solution

Vous pouvez toujours le faire fonctionner en utilisant SQL dynamique avec EXECUTEdans 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 ce travail 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:


Merci de répondre. Il le vérifiera dans quelques heures. Juste pour savoir avant de tester, votre solution accepte de recevoir des lignes de as SELECT. Je veux dire SELECT * FROM process((SELECT * FROM source WHERE cond)).
jlandercy

@j: Non, vous passez un nom de table . Il n'y a aucun moyen de passer une table elle-même (pas de variable de table). Il existe plusieurs façons de contourner ce problème. Connexes: stackoverflow.com/a/27853965/939860 ou stackoverflow.com/a/31167928/939860 . Pour travailler sur le résultat d'une requête, j'utiliserais un curseur ou une table temporaire ...
Erwin Brandstetter

0

Cela fera ce que vous voulez sans avoir besoin de 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 un 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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.