PostgreSQL: Tabelle als Argument in Funktion übergeben

11

Ich entdecke TYPEin PostgreSQL. Ich habe eine TABLE TYPE, die eine Tabelle respektieren muss (Schnittstelle). Beispielsweise:

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

Ich kann mit dieser Vorlage eine Tabelle erstellen mit:

CREATE TABLE test OF dataset;

Ich habe viele Optionen in der API gesehen , bin aber etwas verloren. Ich würde gerne wissen, ob es möglich ist, diesen Typ Funktionsparametern zuzuweisen INPUT/OUTPUT.

Angenommen, ich habe einen FUNCTIONAnruf process, der eine Stichprobe von Datensätzen aus einem Datensatz empfängt TABLE source, verarbeitet und dann einen TABLE sinkmit demselben zurückgibt TYPE.

Das heißt, ich würde gerne wissen, ob es möglich ist, eine zu erstellen TYPE, die sich so verhält:

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

Und das kann man so nennen:

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

Ich frage mich, ob dies mit PostgreSQL möglich ist, und frage, wie das geht. Weiß jemand von euch?


Hier ist eine MWE von dem, was ich versuche zu tun:

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

Aber es gelingt nicht, es ist, als würde die Quelle als Spalte statt als SETOF RECORDSmit dem Dataset-Typ wahrgenommen .

jlandercy
quelle

Antworten:

13

Ihr Parameter _sourcein der hinzugefügten MWE wird nirgendwo referenziert. Der Bezeichner sourceim Funktionskörper hat keinen führenden Unterstrich und wird unabhängig als konstanter Tabellenname interpretiert.

Noch wichtiger ist, dass es sowieso nicht so funktionieren würde. SQL erlaubt nur die Parametrisierung von Werten in DML-Anweisungen. Details in dieser verwandten Antwort:

Lösung

Sie können es weiterhin mit dynamischem SQL EXECUTEin einer plpgsql-Funktion zum Laufen bringen. Einzelheiten:

Oder versuchen Sie diese Suche nach verwandten Fragen und Antworten

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 

Sie können diese Funktion sogar für eine bestimmte Tabelle ausführen:

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

Ausführliche Erklärung:

Erwin Brandstetter
quelle
Danke für die Antwort. Ich werde es in ein paar Stunden überprüfen. Nur um vor dem Testen zu wissen, akzeptiert Ihre Lösung, Zeilen von as zu empfangen SELECT. Ich meine SELECT * FROM process((SELECT * FROM source WHERE cond)).
Jlandercy
@j: Nein, Sie eine Tabelle übergeben Namen . Es gibt keine Möglichkeit, eine Tabelle selbst zu übergeben (keine Tabellenvariable). Es gibt verschiedene Möglichkeiten, dies zu umgehen. Verwandte Themen : stackoverflow.com/a/27853965/939860 oder stackoverflow.com/a/31167928/939860 . Um am Ergebnis einer Abfrage zu arbeiten, würde ich einen Cursor oder eine temporäre Tabelle verwenden ...
Erwin Brandstetter
0

Dies macht, was Sie wollen, ohne dynamisches SQL zu benötigen :

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

Soweit ich das beurteilen kann (nachdem ich ausgiebig gegoogelt habe, weil ich das gleiche Problem hatte), kann man eine Tabelle nicht direkt an eine Funktion übergeben.

Wie gezeigt, können Sie eine Tabelle jedoch in ein Array umwandeln [] eines benutzerdefinierten Typs , der aus mehreren Basistypen besteht (ähnlich einer Tabellendefinition).

Dann können Sie dieses Array übergeben und es wieder in eine Tabelle aufnehmen, sobald Sie in der Funktion sind.

Sam Fed
quelle