问题描述
我目前有四个表需要使用左连接进行连接。 大约有 400_000(40 万)条数据记录(未来还会增长)。
除了左连接之外,还必须能够高效执行以下内容:
- 使用列采购.mandant_id、采购.公司代码、采购_mandant_names.name、公司代码.名称、采购.sequence_number、采购.年、采购.月进行全文搜索。
- 全文搜索还必须使用部分单词并忽略大小写。
- 找到了一些值(用于计算有多少页)。
- 还有一个偏移量、限制(或替代方法,其工作原理基本相同)来为每次搜索指定一个固定的页码(没有游标功能,我们不知道最后找到的 ID 是什么)。
- 我们也不知道下一个页码是否是下一个更高的数字。基本上,可以立即提供更多页数。
我自己有几个想法来优化这个需求。
全文搜索:
- 一个单独的表,其中全文搜索的列连接在一个文本字段 (
p.client_id::text || ' '::text || pmn.name::text
) 中,另外还包含用于购买的 id 和 gin 索引 (gin_trgm_ops) - 一个完全不同的数据库系统,比如用于全文搜索的 sonic 或 elasticsearch,但我不知道如何最好地同步数据并在程序中查询。
- 我发现了一些类似 zombodb 的东西。 不幸的是,它的缺点是删除的记录不会从 elasticsearch 中删除。 而且 zombodb 仅适用于 linux 服务器,我更喜欢并使用 windows。
偏移、限制:
- 将搜索限制为purchase.id(从左连接中选择purchase.id)并获取这些id之后的所有数据(如果这确实导致优化不确定)
- 添加另一个始终包含当前行号和索引的列,然后在删除时也必须完全重建。不幸的是,它在一般情况下并没有真正的帮助,因为对这些数据进行过滤后,行号当然不再领先。
- 这同样适用于 id,但删除时也可能在 id 中出现间隙。
计数
可以添加列或表来提供帮助。 只要保留所有数据,也可以更改结构。
如果 postgres 没有所需的功能,也可以更改数据库系统。
- 关系
- 开源
- 可与 PHP 和 nodejs 连接
表格:
CREATE TABLE company_codes (
id int2 NOT NULL,name varchar(20) NOT NULL,CONSTRAINT company_codes_pk PRIMARY KEY (id)
);
CREATE TABLE purchasing_mandant_names (
id int2 NOT NULL,"name" varchar(50) NOT NULL,CONSTRAINT archiving_coding_keys_pkey PRIMARY KEY (id),CONSTRAINT purchasing_mandant_names_un UNIQUE (name)
);
CREATE TABLE purchasing (
id int4 NOT NULL GENERATED BY DEFAULT AS IDENTITY,mandant_id int2 NOT NULL,company_code int4 NOT NULL,sequence_number int8 NOT NULL,"year" int2 NOT NULL,"month" int2 NOT NULL,CONSTRAINT purchasing_check_month CHECK (((month >= 1) AND (month <= 12))),CONSTRAINT purchasing_check_year CHECK (((year >= 2000) AND (year <= 2100))),CONSTRAINT purchasing_client_plant_sequence_number_key UNIQUE (mandant_id,company_code,sequence_number),CONSTRAINT purchasing_pkey PRIMARY KEY (id)
);
ALTER TABLE purchasing ADD CONSTRAINT purchasing_company_code_fk FOREIGN KEY (company_code) REFERENCES company_codes(id);
ALTER TABLE purchasing ADD CONSTRAINT purchasing_mandant_id_fkey FOREIGN KEY (mandant_id) REFERENCES purchasing_mandant_names(id) NOT VALID;
CREATE TABLE purchasing_pages (
purchasing_id int8 NOT NULL,page_path varchar(255) NOT NULL,CONSTRAINT purchasing_pages_check_page_path CHECK (((page_path)::text ~ '^([a-zA-Z0-9\-_ ]+\/)+[a-zA-Z0-9\-_ ~]+\.[tT][iI][fF]'::text)),CONSTRAINT purchasing_pages_purchasing_id_page_path_key UNIQUE (purchasing_id,page_path)
);
ALTER TABLE purchasing_pages ADD CONSTRAINT purchasing_pages_id_fkey FOREIGN KEY (purchasing_id) REFERENCES purchasing(id) ON UPDATE CASCADE ON DELETE RESTRICT;
当前慢搜索
SELECT p.id,p.mandant_id AS mandant_code,pmn.name AS mandant_name,p.company_code,cc.name AS company_name,p.sequence_number,p.year,p.month,pp.page_path
FROM purchasing p
LEFT JOIN purchasing_pages pp ON p.id = pp.purchasing_id
LEFT JOIN purchasing_mandant_names pmn ON p.mandant_id = pmn.id
LEFT JOIN company_codes cc ON p.company_code = cc.id
where p.mandant_id::text like '%32%'
or pmn.name::text like '%32%'
or p.company_code::text like '%32%'
or cc.name::text like '%32%'
or p.sequence_number::text like '%32%'
or p.year::text like '%32%'
or p.month::text like '%32%'
order by p.id desc
offset 10 fetch first 10 rows only
解决方法
我的解决方案: (我认为会有更好的选择,但这些是我自己找到的唯一选择)
全文搜索:
我为每一列创建了一个 gin_trgm_ops。
我也对狂野速度感兴趣。不幸的是,此扩展不再受支持。
Wildspeed 还具有使用较小长度 < 3
的优势。
也许以后我会再往这个方向看,看看有没有类似的扩展。
偏移、限制:
不幸的是,应用过滤器时没有优化选项。 为了优化性能,我在应用程序本身中内置了几个选项。 如果您获得指向页面的直接链接,则必须使用偏移量、限制,因为没有其他可能性。 除了这种可能性,现在还有一个偏移量 - 前一个偏移量和最后一个 id。这样,索引至少会用于进一步导航。 但是对于直接跳转,没有 optimnierungsmöglichkeiten,因为数据也被过滤了。
计数
我直接使用了count(*) OVER() 和offset limit 搜索过滤器结合使用,所以只需要执行一个查询。这是我为此找到的唯一优化。