优化多个左连接表以在多列上使用偏移量、限制和分页计数进行全文搜索
optimisation of multiple left joined tables for fulltextsearch over multiple columns with offset, limit and count for pagination
我目前有四个 table 需要使用左连接进行连接。
大约有400_000(40万)条数据记录(以后还会增加)。
除了left join之外,还必须能够高效执行以下操作:
- 使用 purchasing.mandant_id、purchasing.company_code、purchasing_mandant_names.name、company_codes.name、purchasing.sequence_number、purchasing.year、[= 列的全文搜索92=].
- 全文搜索也必须使用部分单词并忽略大小写。
- 找到了一些值(以计算有多少页)。
- 还有一个偏移量、限制(或替代方案,基本上工作相同)为每次搜索指定固定页码(没有游标功能,我们不知道最后找到的 ID 是什么)。
- 我们也不知道下一个页码是否是下一个更高的页码。基本上马上就可以给多了页数
我自己有几个想法来优化这个需求。
全文搜索:
- 一个单独的 table,其中用于全文搜索的列连接在一个文本字段中 (
p.client_id::text || ' '::text || pmn.name::text
),另外还包含用于使用 gin 索引进行购买的 ID (gin_trgm_ops )
- 一个完全不同的数据库系统,像sonic或elasticsearch用于全文搜索,但我不知道如何最好地同步数据并在程序中查询。
- 我发现了一些与 zombodb 类似的东西。
不幸的是,它的缺点是删除的记录不会从 elasticsearch 中删除。
并且 zombodb 仅适用于 linux 服务器,我更喜欢并使用 windows.
偏移,极限:
- 限制搜索到purchasing.id(select purchasing.id from left join)并获取这些ids之后的所有数据(如果这真的导致优化还不确定)
- 添加另一列,该列始终包含带有索引的当前行号,如果删除,则还必须完全重建。不幸的是,它通常并没有真正的帮助,因为对这些数据进行过滤后,行号当然不再领先。
- id也一样,只是删除时id也会出现空缺
计数
- 我暂时想不出任何优化,因为总是要过滤整个数据集。当前唯一的选择是使用 id 搜索 return 仅 id 并计算它们。
然后可以使用这些 ID 来提取所需数据的其余部分。
可以添加列或 table 来提供帮助。
只要保留所有数据,也可以更改结构。
如果postgres没有所需的功能,也可以更换数据库系统。
- 关系
- 开源
- connectable 与 php 和 nodejs
tables:
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 感兴趣。不幸的是,不再支持此扩展。
wildspeed 也有使用更短长度 < 3
的优势。
可能以后会再往这个方向看,看看有没有类似的扩展。
偏移,限制:
遗憾的是,应用过滤器时没有优化选项。
为了优化性能,我在应用程序本身中构建了几个选项。
如果直接link到一个页面,offset,limit一定要用,因为没有别的可能。
除了这种可能性之外,现在还有一个偏移量 - 先前的偏移量和最后一个 id。这样,索引至少用于进一步导航。
但只是直接跳转没有optimnierungsmöglichkeiten,因为数据也被过滤了。
计数
我直接用一个count(*) OVER()结合偏移量限制搜索过滤器,所以只需要执行一个查询。这是我为此找到的唯一优化。
我目前有四个 table 需要使用左连接进行连接。 大约有400_000(40万)条数据记录(以后还会增加)。
除了left join之外,还必须能够高效执行以下操作:
- 使用 purchasing.mandant_id、purchasing.company_code、purchasing_mandant_names.name、company_codes.name、purchasing.sequence_number、purchasing.year、[= 列的全文搜索92=].
- 全文搜索也必须使用部分单词并忽略大小写。
- 找到了一些值(以计算有多少页)。
- 还有一个偏移量、限制(或替代方案,基本上工作相同)为每次搜索指定固定页码(没有游标功能,我们不知道最后找到的 ID 是什么)。
- 我们也不知道下一个页码是否是下一个更高的页码。基本上马上就可以给多了页数
我自己有几个想法来优化这个需求。
全文搜索:
- 一个单独的 table,其中用于全文搜索的列连接在一个文本字段中 (
p.client_id::text || ' '::text || pmn.name::text
),另外还包含用于使用 gin 索引进行购买的 ID (gin_trgm_ops ) - 一个完全不同的数据库系统,像sonic或elasticsearch用于全文搜索,但我不知道如何最好地同步数据并在程序中查询。
- 我发现了一些与 zombodb 类似的东西。 不幸的是,它的缺点是删除的记录不会从 elasticsearch 中删除。 并且 zombodb 仅适用于 linux 服务器,我更喜欢并使用 windows.
偏移,极限:
- 限制搜索到purchasing.id(select purchasing.id from left join)并获取这些ids之后的所有数据(如果这真的导致优化还不确定)
- 添加另一列,该列始终包含带有索引的当前行号,如果删除,则还必须完全重建。不幸的是,它通常并没有真正的帮助,因为对这些数据进行过滤后,行号当然不再领先。
- id也一样,只是删除时id也会出现空缺
计数
- 我暂时想不出任何优化,因为总是要过滤整个数据集。当前唯一的选择是使用 id 搜索 return 仅 id 并计算它们。 然后可以使用这些 ID 来提取所需数据的其余部分。
可以添加列或 table 来提供帮助。 只要保留所有数据,也可以更改结构。
如果postgres没有所需的功能,也可以更换数据库系统。
- 关系
- 开源
- connectable 与 php 和 nodejs
tables:
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 感兴趣。不幸的是,不再支持此扩展。
wildspeed 也有使用更短长度 < 3
的优势。
可能以后会再往这个方向看,看看有没有类似的扩展。
偏移,限制:
遗憾的是,应用过滤器时没有优化选项。 为了优化性能,我在应用程序本身中构建了几个选项。 如果直接link到一个页面,offset,limit一定要用,因为没有别的可能。 除了这种可能性之外,现在还有一个偏移量 - 先前的偏移量和最后一个 id。这样,索引至少用于进一步导航。 但只是直接跳转没有optimnierungsmöglichkeiten,因为数据也被过滤了。
计数
我直接用一个count(*) OVER()结合偏移量限制搜索过滤器,所以只需要执行一个查询。这是我为此找到的唯一优化。