【发布时间】:2021-02-18 19:00:09
【问题描述】:
我有一个 Postgres 查询,我们在其中设置了多个索引,其中一个在我们有 GIN 索引的文本字段上。我基于the pg_trgm documentation 对此的理解是,它仅适用于搜索字符串由字母数字文本组成的情况。测试证实了这一点,并在具有数千万条记录的数据库中进行了如下操作:
SELECT * FROM my_table WHERE target_field LIKE '%foo%'
我在很多地方读到过,任何不是字母数字字符串的东西在三元组搜索中都被视为一个单独的词,所以像下面这样的东西也很有效:
SELECT * FROM my_table WHERE target_field LIKE '%foo & bar%'
但是,有人运行的搜索实际上只是连续三个问号,并触发了全表扫描。出于某种原因,当查询中单独使用多个与号或问号时,它们的处理方式与放置在实际字母数字字符旁边或中间的单个字符不同。
我所做的研究表明,这可能是某些数据库驱动程序处理问号的方式,有时将其解释为需要提供的参数,但随后会因为找不到参数和触发器而感到困惑表扫描。我真的不相信这是事实。我可能倾向于相信它会抛出错误而不是完成查询,但无论如何运行它似乎是一个设计缺陷。
更有意义的是,问号不是字母数字字符,因此它的处理方式不同。在某些技术中,常见的符号如 & 被认为是字母数字,但我认为 Postgres 不是这种情况。事实上,文档建议在基于 GIN 的索引中将非字母数字字符视为单词边界。
奇怪的是我可以搜索%foo & bar%,这似乎工作正常。我什至可以搜索%&%,它会很快返回,尽管不是我想要的结果。但是,如果我将(例如)其中三个像这样放在一起:%&&&%,它会触发全表扫描。
运行各种实验后,我看到了以下内容:
-
%%:使用索引 -
%&%:使用索引 -
%?%:使用索引 -
%foo & bar%:使用索引 -
%foo ? bar%:使用索引 -
%foo && bar%:使用索引 -
%foo ?? bar%:使用索引 -
%&&%:触发全表扫描 -
%??%:触发全表扫描 -
%foo&bar%:使用索引,但不返回结果
我认为所有这些都是有道理的,直到您到达 #8 和 #9。如果和号是单词边界,#10 不应该返回结果吗?
任何人都解释了为什么多个连续的标点字符与单个标点字符的处理方式不同?
【问题讨论】:
-
请问'&'有什么用?
-
& 符号只是存储在该字段中的文本的一部分,并且数据库中的某些条目包含& 符号或问号。我不认为任何人两者都有,但如果他们有,可能就不相关了。
-
这不是真的。如果它是一个 btree 索引,你会是正确的,但它是一个 Trigram 索引。我一开始就这么说了。在 Trigram Indexes 上阅读此内容:scoutapm.com/blog/…
-
这是 PostgreSQL 11.8。如果需要,我可能会升级,但存在后勤挑战,所以这不是一夜之间的事情。我正在考虑简单地从查询中删除任何非字母数字字符,用空格替换它们,删除双空格,然后将它们重新组合成一个字符串以运行查询。但我更愿意在做那种事情之前了解潜在的问题。
-
不确定是不是你的情况,但这里有关于三元索引的很好解释:stackoverflow.com/a/57685243/593144
标签: postgresql indexing