PostgreSQL: passa la tabella come argomento nella funzione


11

Sto scoprendo TYPEin PostgreSQL. Ho una TABLE TYPEtabella che alcuni devono rispettare (interfaccia). Per esempio:

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[]
);

Posso creare una tabella usando questo modello con:

CREATE TABLE test OF dataset;

Ho visto molte opzioni nell'API , ma sono un po 'perso. Vorrei sapere se è possibile assegnare questo tipo ai INPUT/OUTPUTparametri della funzione .

Diciamo che ho una FUNCTIONchiamata processche riceve un campione di record da un set di dati TABLE source, li elabora e quindi restituisce uno TABLE sinkcon lo stesso TYPE.

Cioè vorrei sapere se è possibile creare un TYPEche si comporta in questo modo:

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

E questo può essere chiamato così:

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

Mi chiedo che sia possibile con PostgreSQL e chiedo come farlo. Qualcuno di voi lo sa?


Ecco un MWE di ciò che sto cercando di fare:

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);

Ma non ha successo, è come se l'origine sia percepita come una colonna anziché SETOF RECORDScon un tipo di set di dati.

Risposte:


13

Il tuo parametro _sourcenel MWE aggiunto non è referenziato da nessuna parte. L'identificatore sourcenel corpo della funzione non ha un carattere di sottolineatura iniziale e viene interpretato in modo indipendente come nome di tabella costante.

Ancora più importante, non funzionerebbe comunque in questo modo. SQL consente solo di parametrizzare i valori nelle istruzioni DML. Dettagli in questa risposta correlata:

Soluzione

Puoi ancora farlo funzionare usando SQL dinamico con EXECUTEin una funzione plpgsql. Dettagli:

Oppure prova questa ricerca per domande e risposte correlate

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 

Puoi persino farlo funzionare per una determinata tabella:

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!!

Spiegazione dettagliata:


Grazie per aver risposto. Lo controllerò tra poche ore. Solo per sapere prima del test, la tua soluzione accetta di ricevere righe da as SELECT. Intendo SELECT * FROM process((SELECT * FROM source WHERE cond)).
jlandercy,

@j: No, passi il nome di una tabella . Non è possibile passare una tabella da sola (nessuna variabile di tabella). Ci sono molti modi per aggirarlo. Correlato: stackoverflow.com/a/27853965/939860 o stackoverflow.com/a/31167928/939860 . Per lavorare sul risultato di una query, vorrei usare un cursore o una tabella temporanea ...
Erwin Brandstetter,

0

Questo farà ciò che vuoi senza bisogno di alcun SQL dinamico :

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
    )
  );

Per quanto ne so (dopo aver cercato su Google ampiamente, perché ho avuto lo stesso problema) non è possibile passare una tabella direttamente a una funzione.

Tuttavia, come mostrato, è possibile trasformare una tabella in un array []di un tipo personalizzato costituito da diversi tipi di base (simile alla definizione di una tabella).

Quindi è possibile passare quell'array e ripristinarlo in una tabella una volta entrati nella funzione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.