【问题标题】:Query performance in PostgreSQL using 'similar to'使用“类似于”在 PostgreSQL 中查询性能
【发布时间】:2012-11-01 18:59:22
【问题描述】:

我需要根据特定列中的某些值从表中检索某些行,在示例中名为 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 中发布的主题)。

【问题讨论】:

  • 您是否总是要搜索单字符值?像'%ABC%|%BCD%' 这样的查询可能吗?
  • 是的,是基于单字符的搜索。
  • @FedericoCristina 实际上,columnX 是由text 字段中的单字符代码表示的。正确的?这意味着columnX 中值的顺序无关紧要。

标签: performance postgresql indexing


【解决方案1】:

如果你只想搜索单字符值的列表,则将每个字符串拆分为一个字符数组并索引该数组:

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']

【讨论】:

  • GIN 索引的维护成本可能相当高,因此它并不特别适用于插入/更新/删除率较高的表。对于这个特定的应用程序,这可能是也可能不是问题。
  • 感谢您的回答。不幸的是,这些表格在不断变化(插入和更新)
  • @FedericoCristina:不知道你的场景的细节,但是,在大多数情况下,频繁的完整扫描意味着服务器上的负载比索引更新(甚至GIN)更多。你真的尝试过这两种解决方案吗?此外,如果您考虑重新设计并能够安装扩展,您可以将标志存储在本机 INTARRAY 中,并使用更易于更新的 GIST 对其进行索引。
【解决方案2】:

我同意@Quassnoi 的观点,GIN index 是最快和最简单的 - 除非写入性能或磁盘空间是问题,因为它占用了大量空间并且消耗了 INSERTUPDATE 和 @ 的相当多的性能987654325@.

我的附加答案是由你的陈述触发的:

I can't find a better approach than using similar to.

如果这是您找到的,那么您的搜索还没有结束。 SIMILAR TO 完全是浪费时间。字面上地。 PostgreSQL 仅以符合(奇怪的)SQL 标准为特征。检查EXPLAIN ANALYZE 的输出以进行查询,您会发现SIMILAR TO 已被正则表达式替换。

在内部,每个SIMILAR TO 表达式都被重写为正则表达式。因此,对于每个SIMILAR TO 表达式,至少有一个更快的正则表达式匹配。如果您不确定,请让EXPLAIN ANALYZE 为您翻译。你不会在手册中找到这个,PostgreSQL 不承诺这样做,但我还没有看到异常。

更多详情请看related answer on dba.SE

【讨论】:

  • 感谢您的链接!使用 SIMILAR TO 是对的。也许我应该重新设计解决方案以避免这种情况。我编辑了我的问题,并引用了一个新的特定领域问题。
【解决方案3】:

这让我觉得这是一个数据建模问题。您似乎将text 字段用作集合,存储单个字符代码以识别集合中存在的值。

如果是这样,我想改造此表以使用以下方法之一:

  • 标准关系规范化。删除columnX,并将其替换为具有对tableName(id) 的外键引用和每行包含旧columnX 中的一个字符的charcode 列的新表,例如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 索引应该为这些操作提供相当可靠的性能。

  • 如果你的表更改率低,可以拆分为数组as recommended by Quassnoi,可以支付GIN索引的费用;

  • columnX 转换为整数数组,使用intarray 和intarray GiST 索引。有一个代码到整数的映射表或在应用程序中转换。

如果时间允许,我会跟进每个演示。制作虚拟数据很痛苦,所以这取决于其他情况。

【讨论】:

  • 看起来你在建模问题上是对的。我应该在那里做一些重构。我编辑了我的问题,并引用了一个新的特定领域问题。
【解决方案4】:

我会将此作为答案发布,因为它可能会在未来指导其他人:为什么不使用 6 列 haveAhaveB ~ have3 并执行 6 部分 OR 查询?还是使用位掩码?

如果有太多属性无法分别分配一列,我可能会尝试创建一个“属性”表:

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

让 DBMS 担心优化。

【讨论】:

  • 感谢您的回答。也许我应该更好地解释一下,那些值(A、B、C、1、2、3)仅用于示例。完整的选项集大约有 24 个不同的选项。
  • @FedericoCristina:更新了我的答案。
  • 我不确定最后一位是否正确。如果 columnX 具有值 ZAB 并且您正在搜索不匹配但应该匹配的 A|B
  • 哦,是的,对不起。请把它归因于约 40 小时的失眠:)
猜你喜欢
  • 2017-08-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-07
相关资源
最近更新 更多