我正在开发一个express.js项目,使用Sequelize作为orm,使用postgres作为引擎。
我正在努力提高执行许多操作的慢速选择查询的性能,其中之一是将仓库名称的首字母缩略词与使用 ilike 的搜索词进行比较,因此如果搜索词是“STW”,名为“超级测试仓库”的仓库将匹配。查询如下所示:
SELECT field1, field2, ...
FROM package p
LEFT OUTER JOIN warehouse w ON p.warehouse_id = w.id
-- many other joins
WHERE
regexp_replace(w.name, '([a-z ])+', '', 'g') ILIKE '%search term%'
-- many other conditions
问题是,我正在做的主要改进是创建索引(gin 为所有所做的事情
ilike '%something%'
),并重构查询以便能够使用它们。但 regexp_replace()
函数不适用于索引。另一个需要注意的重要事项是,此解决方案仅在名称中的所有单词都正确大写的情况下才有效,这是我们大多数数据的情况,但不是全部,因此如果有一个名为“Super TEST Warehouse”的仓库,或“超级测试仓库”,将无法正常工作。
我想到的一个简单的解决方案就是这样做(假设搜索文本是“STW”):
w.name ILIKE 'S% T% W%' OR w.name ILIKE '% S% T% W%'
它适用于一般情况,并且使用我创建的杜松子酒索引。但问题是它允许中间有其他词。所以“STW”也会匹配“Super Fake Test Warehouse”。
是否有任何条件可以正确匹配首字母缩略词,并使用索引?
您是否建议为此使用生成的列(假设我改进现有的 regexp_replace,或使用其他东西)?这样做有什么缺点吗?理想情况下,我不想仅为此功能更改表,但如果我们无能为力,这可能是一个很好的解决方案。
还有其他解决办法吗?
理想情况下,我不想仅为此功能更改表
因此创建一个表达式索引:
CREATE INDEX foo ON package (upper(regexp_replace(name, '\W*?\m(.).*?\M', '\1', 'g')));
该表达式可靠地提取大写首字母缩略词(字符串中尾随的非单词字符除外):
SELECT upper(regexp_replace('SUPER ware hOUse', '\W*?\m(.).*?\M', '\1', 'g')); -- SWH
所涉及的函数是不可变的,因此适合表达式索引。
重复查询中的表达式以引入索引:
SELECT *
FROM package p
WHERE upper(regexp_replace(name, '\W*?\m(.).*?\M', '\1', 'g')) = 'SWH';
不过,我更愿意添加一个生成的列。查询更简单,速度更快,但每行仅添加很少的字节。参见: