PostgreSQL中使用“similar to”的查询性能

ruoxqz4g  于 2023-08-04  发布在  PostgreSQL
关注(0)|答案(4)|浏览(206)

我需要根据特定列中的某些值从表中检索某些行,在示例中名为columnX

select *
from tableName 
where columnX similar to ('%A%|%B%|%C%|%1%|%2%|%3%')

字符串
因此,如果columnX包含指定的值(A,B,C,1,2,3)中的至少一个,我将保留该行。
我找不到比使用类似于更好的方法。问题是对于一个超过一百万行的表,查询所需的时间太长。
我试着索引它:

create index tableName_columnX_idx on tableName (columnX) 
where columnX similar to ('%A%|%B%|%C%|%1%|%2%|%3%')


但是,如果条件是可变的(值可以不是A、B、C、1、2、3),则需要为每个条件使用不同的索引。
对于这个问题有没有更好的解决办法?
编辑:谢谢大家的反馈。看起来我已经达到了这一点,也许是因为一个设计错误(主题我已经张贴在一个separated question)。

ftf50wuq

ftf50wuq1#

如果你只想搜索一个字符值的列表,那么把每个字符串拆分成一个字符数组,并对数组进行索引:

CREATE INDEX
        ix_tablename_columnxlist
ON      tableName
USING   GIN((REGEXP_SPLIT_TO_ARRAY(columnX, '')))

字符串
然后搜索索引:

SELECT  *
FROM    tableName
WHERE   REGEXP_SPLIT_TO_ARRAY(columnX, '') && ARRAY['A', 'B', 'C', '1', '2', '3']

xmq68pz9

xmq68pz92#

我同意QuassnoiGIN index是最快和最简单的-除非写性能或磁盘空间是问题,因为它占用了大量的空间,并增加了INSERTUPDATEDELETE的成本。
我的补充回答是由你的声明引发的:
我找不到比使用similar to更好的方法。
如果这就是你发现的,那么你的搜索还没有结束。SIMILAR TO是 * 完全浪费时间 *。从字面上看,Postgres只是为了遵守(奇怪的)SQL标准而包含它。检查EXPLAIN ANALYZE的查询输出,您会发现SIMILAR TO已被正则表达式替换。
在内部,每个SIMILAR TO表达式都被重写为正则表达式。因此,对于每一个SIMILAR TO表达式,至少有一个正则表达式匹配快一点。EXPLAIN ANALYZE为您翻译,如果您不确定。你不会在手册中找到这一点,PostgreSQL并不承诺这样做,但我还没有看到一个例外。
进一步阅读:

ctrmrzij

ctrmrzij3#

我认为这是一个数据建模问题。您似乎将text字段用作集合,存储单个字符代码以标识集合中存在的值。
如果是这样,我想使用以下方法之一重新构建此表:

  • 标准关系规范化。删除columnX,并将其替换为一个新表,该表具有指向tableName(id)的外键引用和一个charcode列,该列每行包含一个来自旧columnX的字符,如CREATE TABLE tablename_columnx_set(tablename_id integer not null references tablename(id), charcode "char", primary key (tablename_id, charcode))。然后可以使用普通的SQL子查询、连接等相当有效地搜索columnX中的键。如果您的应用程序无法科普这种变化,您可以始终保留columnX并使用触发器维护侧表。
  • columnX转换为具有虚拟值的hstore关键帧。然后可以使用hstore操作符,如columnX ?| ARRAY['A','B','C']columnX的hstore上的GiST索引应该为这些操作提供相当可靠的性能。
  • 如果你的表的变化率很低,你可以支付GIN索引的费用,那么就拆分成一个数组as recommended by Quassnoi;
  • columnX转换为整数数组,使用intarray和Intarray GiST索引。有一个Map表的代码,以整数或转换的应用程序。

时间允许的话,我会跟进每个演示。制作虚拟数据是一件痛苦的事情,所以这取决于其他情况。

oymdgrw7

oymdgrw74#

我会把这个作为答案,因为它可能会在未来指导其他人:为什么不使用6列haveAhaveB ~ have3,并执行6部分OR查询?还是使用位掩码?
如果有太多的属性而无法为每个属性分配一列,我可以尝试创建一个“属性”表:

(fkey, attr) VALUES (1, 'A'), (1, 'B'), (2, '3')

字符串
让DBMS去操心优化的事。

相关问题