PostgreSQL: передайте таблицю як аргумент у функції


11

Я відкриваю TYPEв PostgreSQL. У мене є TABLE TYPEте, що деяка таблиця повинна поважати (інтерфейс). Наприклад:

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

Я можу створити таблицю за допомогою цього шаблону за допомогою:

CREATE TABLE test OF dataset;

Я бачив багато варіантів в API , але я трохи загубився. Мені хотілося б знати, чи можна призначити цей тип функціональним INPUT/OUTPUTпараметрам.

Скажімо, у мене є FUNCTIONвиклик, processякий отримує зразок записів із набору даних TABLE source, обробляє їх, а потім повертає a TABLE sinkз тим же TYPE.

Тобто я хотів би знати, чи можна створити таке, TYPEяке поводиться так:

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

І це можна назвати так:

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

Цікаво, що це можливо з PostgreSQL, і запитаю, як це зробити. Хтось із вас знає?


Ось MWE з того, що я намагаюся зробити:

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

Але це не вдається, це схоже на те, що джерело сприймається як стовпець, а не SETOF RECORDSтип з набором даних.

Відповіді:


13

Ваш параметр _sourceу доданому MWE ніде не посилається. Ідентифікатор sourceв тілі функції не має провідного підкреслення і інтерпретується як постійна назва таблиці незалежно.

Що ще важливіше, це все одно не діяло б. SQL дозволяє лише параметризувати значення в операторах DML. Деталі у цій відповіді:

Рішення

Ви все ще можете змусити його працювати за допомогою динамічного SQL з EXECUTEфункцією plpgsql. Деталі:

Або спробуйте здійснити пошук пов’язаних питань та відповідей

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 

Ви навіть можете зробити цю роботу для будь-якої таблиці:

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

Детальне пояснення:


Дякую за відповідь. Я перевіряю це через кілька годин. Тільки щоб знати, перш ніж тестувати, чи ваше рішення приймає отримання рядків від SELECT. Я маю на увазі SELECT * FROM process((SELECT * FROM source WHERE cond)).
jlandercy

@j: Ні, ви передаєте ім'я таблиці . Немає можливості передавати саму таблицю (немає змінної таблиці). Навколо цього існує кілька способів. Пов’язано: stackoverflow.com/a/27853965/939860 або stackoverflow.com/a/31167928/939860 . Для роботи над результатом запиту я використовував би курсор або тимчасову таблицю ...
Erwin Brandstetter

0

Це зробить все, що ви хочете, не потребуючи жодного динамічного SQL :

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

Наскільки я можу сказати (після googeling extensivly, тому що у мене була така ж проблема), ви не можете передати таблицю безпосередньо функції.

Однак, як показано, ви можете перетворити таблицю в масив []користувацького типу, який складається з декількох основних типів (аналогічно визначенню таблиці).

Потім ви можете передати цей масив і зняти його назад у таблицю, як тільки ви перебуваєте у функції.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.