PostgreSQL: Pass table sebagai argumen dalam fungsi

11

Saya menemukan TYPEdi PostgreSQL. Saya punya TABLE TYPEbeberapa tabel yang harus dihormati (antarmuka). Sebagai contoh:

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

Saya bisa membuat tabel menggunakan templat ini dengan:

CREATE TABLE test OF dataset;

Saya telah melihat banyak opsi di API , tetapi saya agak bingung. Saya ingin tahu apakah mungkin untuk menetapkan tipe ini ke INPUT/OUTPUTparameter fungsi .

Membiarkan mengatakan bahwa saya telah sebuah FUNCTIONdisebut processyang menerima sampel catatan dari dataset TABLE source, proses mereka dan kemudian kembali TABLE sinkdengan sama TYPE.

Saya ingin tahu apakah mungkin untuk membuat TYPEyang berperilaku seperti ini:

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

Dan itu bisa disebut seperti ini:

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

Saya bertanya-tanya apakah mungkin dengan PostgreSQL, dan bertanya bagaimana melakukannya. Apakah ada di antara kalian yang tahu?


Inilah MWE dari apa yang saya coba lakukan:

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

Tapi itu tidak berhasil, itu seperti sumber dianggap sebagai kolom bukan SETOF RECORDSdengan tipe dataset.

jlandercy
sumber

Jawaban:

13

Parameter Anda _sourcedalam MWE yang ditambahkan tidak dirujuk di mana pun. Pengidentifikasi sourcedi fungsi tubuh tidak memiliki garis bawah memimpin dan ditafsirkan sebagai nama tabel konstan secara independen.

Lebih penting lagi, itu tidak akan berfungsi seperti ini. SQL hanya memungkinkan untuk membuat parameter nilai dalam pernyataan DML. Detail dalam jawaban terkait ini:

Larutan

Anda masih bisa membuatnya bekerja menggunakan SQL dinamis dengan EXECUTEfungsi plpgsql. Detail:

Atau coba pencarian ini untuk pertanyaan dan jawaban terkait

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 

Anda bahkan dapat membuat ini berfungsi untuk tabel apa pun yang diberikan:

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

Penjelasan detail:

Erwin Brandstetter
sumber
Terimakasih telah menjawab. Il akan memeriksanya dalam beberapa jam. Hanya untuk mengetahui sebelum pengujian, apakah solusi Anda menerima untuk menerima baris dari sebagai SELECT. Maksudku SELECT * FROM process((SELECT * FROM source WHERE cond)).
jlandercy
@ j: Tidak, Anda memasukkan nama tabel . Tidak ada cara untuk melewatkan tabel itu sendiri (tidak ada variabel tabel). Ada beberapa cara mengatasinya. Terkait: stackoverflow.com/a/27853965/939860 atau stackoverflow.com/a/31167928/939860 . Untuk mengerjakan hasil kueri saya akan menggunakan kursor atau tabel sementara ...
Erwin Brandstetter
0

Ini akan melakukan apa yang Anda inginkan tanpa memerlukan SQL dinamis :

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

Sejauh yang saya tahu (setelah googeling dengan extensivly, karena saya memiliki masalah yang sama), Anda tidak dapat melewatkan tabel langsung ke suatu fungsi.

Namun, seperti yang ditunjukkan, Anda bisa mengubah tabel menjadi larik []tipe kustom yang terdiri dari beberapa tipe dasar (mirip dengan definisi tabel).

Kemudian Anda bisa melewatkan array itu dan mengembalikannya ke tabel setelah Anda berada dalam fungsi.

Sam Fed
sumber