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с типом набора данных.

jlandercy
источник

Ответы:

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

Детальное объяснение:

Эрвин Брандштеттер
источник
Спасибо за ответы. Иль проверит это через несколько часов. Чтобы узнать, тестирует ли ваше решение получение строк от as SELECT. Я имею в виду SELECT * FROM process((SELECT * FROM source WHERE cond)).
Jlandercy
@j: Нет, вы передаете имя таблицы . Нет способа передать саму таблицу (без табличной переменной). Есть несколько способов обойти это. Связанный: stackoverflow.com/a/27853965/939860 или stackoverflow.com/a/31167928/939860 . Для работы с результатом запроса я бы использовал курсор или временную таблицу ...
Эрвин Брандштеттер,
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
    )
  );

Насколько я могу сказать (после экстенсивного гуглинга, потому что у меня была та же проблема), вы не можете передать таблицу непосредственно в функцию.

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

Затем вы можете передать этот массив и удалить его обратно в таблицу, как только окажетесь в функции.

Сэм Фед
источник