Табличные функции — потрясающая вещь. Многие базы данных поддерживают их так или иначе, как и PostgreSQL. В PostgreSQL (почти) все является таблицей. Например, мы можем написать:
|
1
2
3
4
5
6
7
|
CREATE OR REPLACE FUNCTION f_1 (v1 INTEGER, v2 OUT INTEGER)AS $$BEGIN v2 := v1;END$$ LANGUAGE plpgsql; |
… и хотите верьте, хотите нет, но это стол! Мы можем написать:
|
1
|
select * from f_1(1); |
И выше вернется:
|
1
2
3
4
5
|
+----+| v2 |+----+| 1 |+----+ |
Это интуитивно понятно, если подумать. Мы просто выдвигаем одну запись с одним столбцом. Если бы мы хотели две колонки, мы могли бы написать:
|
1
2
3
4
5
6
7
8
|
CREATE OR REPLACE FUNCTION f_2 (v1 INTEGER, v2 OUT INTEGER, v3 OUT INTEGER)AS $$BEGIN v2 := v1; v3 := v1 + 1;END$$ LANGUAGE plpgsql; |
… а потом:
|
1
|
select * from f_2(1); |
И выше вернется:
|
1
2
3
4
5
|
+----+----+| v2 | v3 |+----+----+| 1 | 2 |+----+----+ |
Это полезно, но это только отдельные записи. Что, если мы хотим создать целую таблицу? Это просто, просто измените свои функции, чтобы они фактически возвращали типы TABLE , вместо использования параметров OUT :
|
01
02
03
04
05
06
07
08
09
10
11
12
|
CREATE OR REPLACE FUNCTION f_3 (v1 INTEGER)RETURNS TABLE(v2 INTEGER, v3 INTEGER)AS $$BEGIN RETURN QUERY SELECT * FROM ( VALUES(v1, v1 + 1), (v1 * 2, (v1 + 1) * 2) ) t(a, b);END$$ LANGUAGE plpgsql; |
Выбирая из приведенной выше очень полезной функции, мы получим таблицу примерно так:
|
1
|
select * from f_3(1); |
И выше вернется:
|
1
2
3
4
5
6
|
+----+----+| v2 | v3 |+----+----+| 1 | 2 || 2 | 4 |+----+----+ |
И мы можем LATERAL объединить эту функцию с другими таблицами, если захотим:
|
1
2
|
select *from book, lateral f_3(book.id) |
… которые могут привести, например:
|
1
2
3
4
5
6
7
8
|
+----+--------------+----+----+| id | title | v2 | v3 |+----+--------------+----+----+| 1 | 1984 | 1 | 2 || 1 | 1984 | 2 | 4 || 2 | Animal Farm | 2 | 4 || 2 | Animal Farm | 4 | 6 |+----+--------------+----+----+ |
На самом деле, кажется, что ключевое слово LATERAL является необязательным в этом случае, по крайней мере для PostgreSQL.
Табличные функции очень мощные!
Обнаружение табличных функций
С точки зрения обратного инжиниринга схемы jOOQ все может стать немного сложнее, как видно из этого вопроса переполнения стека . PostgreSQL работает с параметрами OUT очень похожим образом с типами возвращаемых данных TABLE . Это можно увидеть в следующем запросе к INFORMATION_SCHEMA :
|
1
2
3
4
|
SELECT r.routine_name, r.data_type, p.parameter_name, p.data_typeFROM information_schema.routines rJOIN information_schema.parameters pUSING (specific_catalog, specific_schema, specific_name); |
… и вывод:
|
01
02
03
04
05
06
07
08
09
10
|
routine_name | data_type | parameter_name | data_type-------------+-----------+----------------+----------f_1 | integer | v1 | integerf_1 | integer | v2 | integerf_2 | record | v1 | integerf_2 | record | v2 | integerf_2 | record | v3 | integerf_3 | record | v1 | integerf_3 | record | v2 | integerf_3 | record | v3 | integer |
Как видите, с этой точки зрения результаты действительно неразличимы. К счастью, мы также можем присоединиться к таблице pg_catalog.pg_proc , которая содержит соответствующий флаг, чтобы указать, возвращает ли функция набор или нет:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
SELECT r.routine_name, r.data_type, p.parameter_name, p.data_type, pg_p.proretsetFROM information_schema.routines rJOIN information_schema.parameters pUSING (specific_catalog, specific_schema, specific_name)JOIN pg_namespace pg_nON r.specific_schema = pg_n.nspnameJOIN pg_proc pg_pON pg_p.pronamespace = pg_n.oidAND pg_p.proname = r.routine_nameORDER BY routine_name, parameter_name; |
Теперь мы получаем:
|
01
02
03
04
05
06
07
08
09
10
|
routine_name | data_type | parameter_name | data_type | proretset-------------+-----------+----------------+-----------+----------f_1 | integer | v1 | integer | ff_1 | integer | v2 | integer | ff_2 | record | v1 | integer | ff_2 | record | v2 | integer | ff_2 | record | v3 | integer | ff_3 | record | v1 | integer | tf_3 | record | v2 | integer | tf_3 | record | v3 | integer | t |
Мы можем видеть, что f_3 является единственной функцией, фактически возвращающей набор записей, в отличие от f_1 и f_2 , которые возвращают только одну запись.
Теперь удалите все те параметры, которые не являются параметрами OUT , и у вас есть тип таблицы:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
SELECT r.routine_name, p.parameter_name, p.data_type, row_number() OVER ( PARTITION BY r.specific_name ORDER BY p.ordinal_position ) AS ordinal_positionFROM information_schema.routines rJOIN information_schema.parameters pUSING (specific_catalog, specific_schema, specific_name)JOIN pg_namespace pg_nON r.specific_schema = pg_n.nspnameJOIN pg_proc pg_pON pg_p.pronamespace = pg_n.oidAND pg_p.proname = r.routine_nameWHERE pg_p.proretsetAND p.parameter_mode = 'OUT'ORDER BY routine_name, parameter_name; |
Что даст нам:
|
1
2
3
4
|
routine_name | parameter_name | data_type | position |-------------+----------------+-----------+----------+f_3 | v2 | integer | 1 |f_3 | v3 | integer | 2 | |
Как запустить такие запросы в jOOQ?
После того, как приведенный выше код сгенерирован, вы можете легко вызвать табличную функцию в любом запросе jOOQ . Рассмотрим снова пример BOOK (на SQL):
|
1
2
|
select *from book, lateral f_3(book.id) |
… и с JOOQ:
|
1
2
3
4
|
DSL.using(configuration) .select() .from(BOOK, lateral(F_3.call(BOOK.ID))) .fetch(); |
Возвращенные записи затем содержат значения для:
|
1
2
|
record.getValue(F_3.V2);record.getValue(F_3.V3); |
Все эти типы безопасности доступны только в следующем выпуске jOOQ 3.5, бесплатно! (Табличные функции SQL Server, Oracle и HSQLDB уже поддерживаются!)
| Ссылка: | Табличные функции PostgreSQL от нашего партнера по JCG Лукаса Эдера из блога JAVA, SQL и JOOQ . |
