如何使用多个 LIKE 运算符和使用索引

Posted

技术标签:

【中文标题】如何使用多个 LIKE 运算符和使用索引【英文标题】:How to use many LIKE operators and use index 【发布时间】:2018-05-02 11:21:25 【问题描述】:

在我的查询中,我想找到与许多 LIKE 运算符之一匹配的行。我知道 3 种方法,但只有其中一种可以使用 index.html

让我们从表格开始:

CREATE TABLE dir (
    id BIGSERIAL PRIMARY KEY,
    path TEXT NOT NULL
);

CREATE INDEX path_idx ON dir(path TEXT_pattern_ops);

插入示例数据后我可以这样做:

EXPLAIN ANALYZE 
SELECT id, path FROM dir
   WHERE path LIKE 'A%'
      OR path LIKE 'B%'
      OR path LIKE 'C%';

以上查询正确使用索引。

第二种方式:

EXPLAIN ANALYZE 
SELECT id, path FROM dir
  WHERE path LIKE ANY(ARRAY['A%', 'B%', 'C%']::TEXT[]);

此查询将不使用索引。 我知道的最后一种方法:

CREATE TABLE patterns (pattern) AS VALUES
('A%'),
('B%'),
('C%');

EXPLAIN ANALYZE 
SELECT id, path FROM dir
  JOIN patterns ON (dir.path LIKE patterns.pattern);

与上一个查询一样,此查询不会使用索引。

对于那些想要使用这些查询的人来说,这里是 SQL Fiddle:http://sqlfiddle.com/#!17/24031/2

问题: 使用path LIKE X OR path LIKE Y 的查询对于许多模式是完全不可读的(模式的数量可能从几到几百或几千不等),而且我担心大型查询可能解析缓慢或甚至达到 1GB 的查询长度限制(某些模式可能有很长的前缀)。

问题: 是否有任何 oder 方法返回相同的结果,不需要将所有模式直接放入查询中(例如在这个带有连接的选项中)?

【问题讨论】:

我使用的是 Postgres 9.6,但如果需要可以升级。 关于前缀,请问您遇到的业务问题需要这么长的前缀吗?例如,如果一个前缀是 1000 个字符,那为什么不能只是 500 个字符,让它做同样的事情呢? 在我原来的问题目录表中包含文件系统上目录的路径。通过此查询,我想查找所有子目录,但仅针对某些满足给定条件的目录。所以我现在要做的是创建查询以从数据库中获取这些目录,使用 SqlAlchemy 创建 SQL:为每个已创建的目录创建路径 LIKE X。这对我来说似乎是不必要的步骤,唯一阻止我改变的是所有其他方法都不使用索引。 我会研究 ltree。将路径“转换”到 ltree 需要额外的努力,但您将拥有舒适的运算符和索引支持 不幸的是 ltree 的字符集非常有限。在我的情况下,路径列可以包含任何 UTF-8 字符。 【参考方案1】:

您可以创建一个三元组索引来支持您的查询。

为此,您需要pg_trgm 扩展;以超级用户身份运行以下命令:

CREATE EXTENSION pg_trgm;

然后就可以创建GIN索引了:

CREATE INDEX ON dir USING gin (path gin_trgm_ops);

此索引可以与您的第二种和第三种方法一起使用,因此它应该可以为您解决问题。

对于您示例中的短模式,索引不会很有效。

您也可以使用 GiST 索引,它可能会更小,但搜索速度会更慢。

请注意,您也可以将该索引与以 % 开头的模式一起使用。

【讨论】:

我相信 OP 正在询问他是否可以让查询分析器在模式隐式来自连接表时使用索引。像 - 如果你把数组放到一个表中并且出现join array_table on path LIKE ANY(pattern_array); seq scan。所以他问你是否可以在查询中隐藏模式列表并使用索引。如果我理解 OP 当然是对的 :) 真;我已经改写了答案(索引也可以在这种情况下使用)。 谢谢。我会很感激你的评论,为什么加入任何(数组)有效而加入类似的模式却没有。当然,如果你有时间的话。我相信很多人都会觉得它非常有趣,尤其是我 我真的很惊讶这行得通。为什么创建的索引类型存在差异?为什么在我的情况下(btree 索引)规划器没有选择使用索引并且使用 trgm 索引它使用它。这对我来说完全违反直觉。 我相信这是我问题的正确答案。不幸的是,我之前在 GIST 索引方面的经验非常糟糕 - 结果表明,在 autovacuum 期间,索引按逻辑顺序遍历,这会转化为随机磁盘访问,这使得它在我的情况下无法使用(索引几乎 1TB 大)。你知道它也适用于 GIN 索引吗?

以上是关于如何使用多个 LIKE 运算符和使用索引的主要内容,如果未能解决你的问题,请参考以下文章

前缀为“%”的 LIKE 运算符中的索引丢失

如何查询多个关键字的like

如何在 MySQL 中使用 LIKE 和部分匹配的 VARCHAR 字段的索引?

关于哪些操作索引效果不佳或未使用

在oracle中如何结合使用('')和like

如何使用比较运算符过滤 SQLite 中的数据 [重复]