这是我的(极其简化的)产品表和一些测试数据。
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 子句等。另一方面,有大量的查询需要完成,如按品牌的产品、特色产品、产品按标题、按标签、按范围或价格等。
我不想在每次执行查询时重复和维护复杂的产品列列表,所以我目前的方法是在两个任务中打破查询过程:
- 将查询封装在 type 的函数中
select_products_by_xxx()
,该函数返回product_id
正确选择和排序的数组。 - 将所有产品列的复杂性封装在一个
list_products()
以 aproduct_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()
函数不尊重数组内的顺序。
显然,我可以在 中包含一个order by
子句list_products()
,但请记住,排序必须由select_products_by_xxx()
函数确定以保持list_products()
唯一性。
任何想法?
编辑
@adamkg 解决方案简单且有效:添加一个通用的 order by 子句,如下所示:
order by array_position(tid, product_id);
但是,这意味着要订购两次产品:首先是 inside select_products_by_xxx()
,然后是 inside 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
函数重构为 returnsetof int
而不是int[]
. 但是,集合不能作为函数参数传递。