【问题标题】:Optimal Postgres text index for LIKE query?LIKE 查询的最佳 Postgres 文本索引?
【发布时间】:2016-06-23 04:50:31
【问题描述】:

使用 Postgres 9.5,我有一个表 addresses

CREATE TABLE addresses (
    id        integer PRIMARY KEY,
    address   text
);

在该表中,我有 750 万行。示例:

1, "1600 Pennsylvania Avenue NW, Washington, DC, 20500"

我在我的应用程序中使用此表进行自动建议搜索,因此我需要使用这种类型的查询:

SELECT * FROM addresses WHERE address LIKE '123 Main St%';

我创建了这个索引:

CREATE INDEX address_idx ON addresses (address);

但问题是它需要大约 1 秒,这太慢了。

这是查询计划:

EXPLAIN SELECT * FROM addresses WHERE address LIKE '123 Main St%';
----
Seq Scan on addresses  (cost=0.00..161309.76 rows=740 width=41)
  Filter: (address ~~ '123 Main St%'::text)

我尝试创建几种类型的 gin 索引,但它们要么没有效果,要么使查询变慢。我不确定我是否正确使用它们。

关于如何创建针对此类查询优化的索引有什么想法吗?


编辑

目前发现的最佳解决方案是使用文本范围扫描:

SELECT *
FROM addresses
WHERE address >= '123 Main St' AND
      address <= concat('123 Main St', 'z');

【问题讨论】:

  • 这总是前缀搜索吗?然后你可以试试 WHERE address BETWEEN '123 Main St' AND '123 Main Su'。这应该会对索引产生范围扫描。
  • 索引对于这个查询应该可以正常工作。也许与文本类型不兼容,以某种方式阻止了索引的使用。
  • @Thilo 谢谢!使用 BETWEEN 产生与 LIKE 查询相同的结果,并将时间减少到 13 毫秒。要回答您的问题,是的,这将始终是前缀搜索。我唯一不喜欢这种方法的是我必须想出字母表中的下一个字母或下一个数字,而不是使用通配符。有没有其他方法可以生成范围扫描而无需编写那种逻辑?
  • 理想情况下,查询规划器应该检测到前缀搜索并将其转换为 BETWEEN 查询。即使没有,最坏的情况也应该是索引扫描(而不是表扫描)——在这种情况下,这不会更快。

标签: sql postgresql indexing sql-like gwt-gin


【解决方案1】:

这是对between 方法的详细说明,太长了,无法评论。

如果您使用的是标准 ASCII 字符,则可以使用波浪号:

SELECT *
FROM addresses
WHERE address >= '123 Main St' AND
      address <= concat('123 Main St', '~');

波浪号的 ASCII 值比其他字符大。

我确实注意到 Postgres 也应该将索引用于 LIKE 查询。我的猜测是问题与类型的兼容性有关。也许如果您将模式转换为varchar(),Postgres 将使用该索引。

【讨论】:

  • 感谢您的回复。确实很聪明,但我遇到了这个查询的麻烦:ERROR: argument of AND must be type boolean, not type text - on line 4。顺便说一句,我正在使用标准的 ASCII 字符。您能否详细说明将模式转换为 varchar() 的含义?你是说专栏吗?
  • @Tyler 。 . .这可能是由于运算符优先级。
  • 删除双管道并添加波浪号,即“123 Main St~”,解决了错误但不返回任何结果。我还尝试了“123 Main S~”,但没有成功。参考 ASCII 表,似乎 'z' 是我可以用来返回任何结果的最大 ASCII 值字符。编辑: concat('123 Main St', 'z') 有效。
  • @Tyler 。 . .有趣的。波浪号在其他情况下对我有用。不过,至少 z 有效。
  • 感谢您的帮助。我认为你的 concat 建议的范围扫描是最好的解决方案!
【解决方案2】:

你可以尝试三件事:

  1. 如果您的数据库位于“C”区域设置(您可以在psql 提示符下使用\l 检查),那么常规的Btree 索引应该有助于优化LIKE 'abc%' 类型的查询。
  2. 如果没有,您可以在创建Btree 索引时尝试使用合适的运算符类。例如CREATE INDEX tbl_col_text_pattern_ops_idx ON tbl(col text_pattern_ops);
  3. 如果这不起作用,您也可以尝试使用GiST / GIN,更多详细信息请参见here

如果您想了解更多信息,请阅读 Erwin 的 StackOverflow 答案 here,其中详细说明了不同 Postgres 索引如何与 LIKE / ILIKE 一起使用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-04
    • 1970-01-01
    相关资源
    最近更新 更多