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

Заранее я не (не хочу) знать тип столбца, т.е. тип элементов в возвращаемом массиве.

Я пытался создать функцию с полиморфным anyarray в качестве возвращаемого типа, но продолжаю использовать синтаксические ошибки. Пробовал как в SQL, так и в PLPQSQL.

CREATE OR REPLACE FUNCTION minmax(tablename character varying, columnname character varying, OUT minmaxa anyarray) AS
$function$
BEGIN
   EXECUTE FORMAT('SELECT array_agg(foo) AS %s FROM (
            (SELECT min(%s) AS foo FROM %s)
            UNION
            (SELECT max(%s) AS foo FROM %s)
    ) AS foobar'
       , columnname
       , columnname, tablename
       , columnname, tablename)
   INTO minmaxa; 
END
$function$
LANGUAGE plpgsql;

Строка из ФОРМАТА будет:

SELECT array_agg(foo) AS columnname FROM ( 
  (SELECT min(columnname) AS foo FROM tablename)
  UNION
  (SELECT max(columnname) AS foo FROM tablename)
 ) AS foobar

Тогда тестовый пример:

create table example (columnA int, columnB varchar);
insert into example (columnA, columnB) values (1, 'ac'), (2, 'ab'), (3, 'aa');

select minmax('example', 'columnA');
select minmax('example', 'columnB');

select array_agg(columnA) from (
   (select min(columnA) AS columnA from example)
   UNION
   (select max(columnA) AS columnA from example)
  ) AS foobar;

Должен вернуться:

{1,3 }
{ ' Аа ', ' ас ' }
{1,3 }

Но в настоящее время определение функции дает: «Ошибка SQL [42P13]: ОШИБКА: невозможно определить тип данных результата. Подробно: у функции, возвращающей полиморфный тип, должен быть хотя бы один полиморфный аргумент».

Я могу заставить его работать, определив фиктивный параметр функции 'foo':

CREATE OR REPLACE FUNCTION minmax(tablename character varying, columnname character varying, foo anyelement, OUT minmaxa anyarray)

(Примечание. Параметр функции foo нигде не используется в теле функции.)

А теперь даем фиктивное значение правильного типа возвращаемого массива при вызове функции:

select minmax('example', 'columnB', ''::varchar);
select minmax('example', 'columnA', 0::int);

Тогда это работает, но что нужно изменить, чтобы мне не понадобился этот фиктивный параметр функции?

1
zimon 29 Май 2019 в 18:08

2 ответа

Лучший ответ

Вы не можете сделать это со стандартными полиморфными типами, но вы можете сделать это с помощью jsonb.

CREATE OR REPLACE FUNCTION minmax(tablename regclass, columnname character varying)
RETURNS JSONB AS
$function$
DECLARE
  minmaxa jsonb;
BEGIN
   EXECUTE FORMAT('SELECT jsonb_agg(foo) AS %I FROM (
            (SELECT min(%I) AS foo FROM %I)
            UNION
            (SELECT max(%I) AS foo FROM %I)
    ) AS foobar'
       , columnname
       , columnname, tablename
       , columnname, tablename)
   INTO minmaxa;
   RETURN minmaxa;
END
$function$
STABLE
LANGUAGE plpgsql;


-- Test with integers
CREATE TABLE test (a int);
insert into test (a) values (1), (2), (3);
select minmax('test'::regclass, 'a');
 minmax
--------
 [1, 2]
(1 row)


-- Test with timestamps
create table test2 (a timestamp);
insert into test2 values ('2012-01-01T00:00:00'), ('2015-01-01T00:00:00');
select minmax('test2'::regclass, 'a');
                     minmax
------------------------------------------------
 ["2012-01-01T00:00:00", "2015-01-01T00:00:00"]
(1 row)

Я использовал regclass вместо строки для имени таблицы, чтобы помочь найти правильную таблицу в соответствии с вашим путем поиска.

2
Jeremy 29 Май 2019 в 17:40

Боюсь, что невозможно легко избавиться от того атрибута, который вы называете foo. Как я понимаю из документации значение передается как {{ Параметр X1}} требуется, чтобы сообщить Postgres, какой будет тип возвращаемых данных.

< Сильный > 38.2.5 . Полиморфные типы

К пяти псевдотипам, представляющим особый интерес, относятся anyelement, anyarray, anynonarray, anyenum и anyrange, которые в совокупности называются полиморфными типами. Любая функция, объявленная с использованием этих типов, называется полиморфной функцией. Полиморфная функция может работать со многими различными типами данных, с определенным типом (ами) данных, определяемыми типами данных, фактически переданными ей в конкретном вызове .

Полиморфные аргументы и результаты связаны друг с другом и разрешаются в конкретный тип данных при разборе запроса, вызывающего полиморфную функцию. Каждой позиции (либо аргумент, либо возвращаемое значение), объявленной как anyelement, разрешено иметь любой конкретный фактический тип данных, , но в любом данном вызове они должны быть одного и того же фактического типа .

Итак, насколько я понимаю, это то, что с полиморфной функцией удаление атрибута foo невозможно, и ваша функция выглядит таковой, поскольку она использует псевдотип anyarray.

1
pirho 29 Май 2019 в 16:58