Это моя (чрезвычайно упрощенная) таблица продуктов и некоторые тестовые данные.
drop table if exists product cascade;
create table product (
product_id integer not null,
reference varchar,
price decimal(13,4),
primary key (product_id)
);
insert into product (product_id, reference, price) values
(1001, 'MX-232', 100.00),
(1011, 'AX-232', 20.00),
(1003, 'KKK 11', 11.00),
(1004, 'OXS SUPER', 0.35),
(1005, 'ROR-MOT', 200.00),
(1006, '234PPP', 30.50),
(1007, 'T555-NS', 110.25),
(1008, 'LM234-XS', 101.20),
(1009, 'MOTOR-22', 12.50),
(1010, 'MOTOR-11', 30.00),
(1002, 'XUL-XUL1', 40.00);
В реальной жизни перечисление столбцов продуктов - это обучаемая задача, полная объединений, предложений case-when-end и т. Д. c. С другой стороны, необходимо выполнить большое количество запросов, таких как продукты по брендам, избранные продукты, продукты по названию, по тегам, по диапазону или цене и т. Д. c.
Я не Я хочу повторять и поддерживать сложные списки столбцов продуктов каждый раз, когда я выполняю запрос, поэтому мой текущий подход разбивает процессы запросов на две задачи:
- инкапсулировать запрос в функции типа
select_products_by_xxx()
, которые возвращают product_id
массивы, правильно выбранные и упорядоченные. - инкапсулируют всю сложность столбца продукта в уникальную функцию
list_products()
, которая принимает product_id array
в качестве параметра. - execute
select * from list_products(select_products_by_xxx())
для получения желаемого результата для каждой функции xxx
.
Например, чтобы выбрать product_id
в обратном порядке (в случае, если это был какой-либо значимый выбор для приложения), такая функция
create or replace function select_products_by_inverse ()
returns int[]
as $$
select
array_agg(product_id order by product_id desc)
from
product;
$$ language sql;
Его можно проверить на работу как
select * from select_products_by_inverse();
select_products_by_inverse |
--------------------------------------------------------|
{1011,1010,1009,1008,1007,1006,1005,1004,1003,1002,1001}|
Чтобы инкапсулировать "листинговую" часть запроса, я использую эту функцию (опять же, чрезвычайно упрощенный и без каких-либо соединений или регистра для удобства примера).
create or replace function list_products (
tid int[]
)
returns table (
id integer,
reference varchar,
price decimal(13,4)
)
as $$
select
product_id,
reference,
price
from
product
where
product_id = any (tid);
$$ language sql;
Он работает, но не соблюдает порядок продуктов в переданном массиве.
select * from list_products(select_products_by_inverse());
id |reference|price |
----|---------|--------|
1001|MX-232 |100.0000|
1011|AX-232 | 20.0000|
1003|KKK 11 | 11.0000|
1004|OXS SUPER| 0.3500|
1005|ROR-MOT |200.0000|
1006|234PPP | 30.5000|
1007|T555-NS |110.2500|
1008|LM234-XS |101.2000|
1009|MOTOR-22 | 12.5000|
1010|MOTOR-11 | 30.0000|
1002|XUL-XUL1 | 40.0000|
Итак , проблема в том, что я передаю настраиваемый упорядоченный массив product_id
, но функция list_products()
не соблюдает порядок внутри массива.
Очевидно, я мог бы включить предложение order by
в list_products()
, но помните, что порядок должен определяться функциями select_products_by_xxx()
, чтобы list_products()
оставалось уникальным.
Есть идея?
РЕДАКТИРОВАТЬ
@ adamkg решение простое и работает: добавление универсального предложения order by следующим образом:
order by array_position(tid, product_id);
Однако это означает, что продукты нужно заказывать дважды: сначала внутри select_products_by_xxx()
а затем внутри list_products()
.
Исследование explain
дает следующий результат:
QUERY PLAN |
----------------------------------------------------------------------|
Sort (cost=290.64..290.67 rows=10 width=56) |
Sort Key: (array_position(select_products_by_inverse(), product_id))|
-> Seq Scan on product (cost=0.00..290.48 rows=10 width=56) |
Filter: (product_id = ANY (select_products_by_inverse())) |
Теперь мне интересно, есть ли какой-либо другой лучший подход к снижению затрат, сохраняя разделимость между функций.
Я вижу две многообещающие стратегии:
- Что касается предложения
explain
и самой проблемы, похоже, что внутри выполняется полное сканирование таблицы product
list_products()
. Так как продуктов могут быть тысячи, лучшим подходом было бы сканирование переданного массива. - Функции
xxx
можно отредактировать так, чтобы они возвращали setof int
вместо int[]
. Однако набор не может быть передан как параметр функции.