【发布时间】:2020-11-26 18:42:59
【问题描述】:
这是我的(极其简化的)产品表和一些测试数据。
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 子句等。另一方面,有大量的查询需要完成,如按品牌分类的产品、特色产品、产品名称、标签、范围或价格等。
我不想在每次执行查询时重复和维护复杂的产品列列表,因此,我目前的方法是在两个任务中打破查询流程:
- 将查询封装在
select_products_by_xxx()类型的函数中,返回product_id数组,正确选择和排序。 - 将所有产品列复杂性封装在一个独特的函数
list_products()中,该函数以product_id array作为参数。 - 执行
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() 函数不遵守数组内的顺序。
显然,我可以在list_products() 中包含一个order by 子句,但请记住,排序必须由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子句和问题本身,似乎正在list_products()内部完成对表product的完整扫描。由于可能有数千种产品,更好的方法是扫描传递的数组。 -
xxx函数可以重构为返回setof int而不是int[]。但是,集合不能作为函数参数传递。
【问题讨论】:
标签: arrays postgresql sql-order-by postgresql-12 array-agg