我有一个巨大的单词表,我正在上面运行 LIKE 查询:
create table words
(
id int,
word varchar
)
它的工作时间很长。索引没有多大帮助,所以我尝试按
word
列对其进行分区:
create table words
(
id int,
word varchar
) partition by RANGE (word);
CREATE TABLE words_1 PARTITION OF words
FOR VALUES FROM ('a') TO ('n');
CREATE TABLE words_2 PARTITION OF words
FOR VALUES FROM ('n') TO ('z');
注意:实际上我计划为每个字母创建 1 个分区。为了简单起见,仅使用其中 2 个。
因此,分区似乎可以与相等和 gt/lt 运算符一起使用:
explain
select * from words where word = 'abc'
Seq Scan on words_1 words (cost=0.00..25.88 rows=6 width=36)
Filter: ((word)::text = 'abc'::text)
explain
select * from words where word >= 'nth'
Seq Scan on words_2 words (cost=0.00..25.88 rows=423 width=36)
Filter: ((word)::text >= 'nth'::text)
但是在 LIKE 查询上,它会继续扫描两个分区:
explain
select * from words where word LIKE 'abc%'
Append (cost=0.00..51.81 rows=12 width=36)
-> Seq Scan on words_1 (cost=0.00..25.88 rows=6 width=36)
Filter: ((word)::text ~~ 'abc'::text)
-> Seq Scan on words_2 (cost=0.00..25.88 rows=6 width=36)
Filter: ((word)::text ~~ 'abc'::text)
有没有办法让分区对 LIKE 查询起作用?
也许还有其他方法可以实现我想要的吗?
我无法复制你的结果。您的查询使用索引,即使只有少数行。确保您的表已被分析并使用
explain analyze
运行查询。
改进的表设计将删除
id
并使用该单词作为主键(假设它们是唯一的)。我添加了大约 1000 个单词并分析了表格。
create table words ( word text primary key );
copy words(word) from '/Users/schwern/tmp/words.txt';
analyze words;
您的所有查询都执行仅索引扫描。
默认的 B-Tree 索引可以进行精确匹配 (
words = 'this'
)、尾随通配符 (words like 'this%'
) 和排序。我们可以使用 trigram ops 进一步添加 Gist 索引来改进这一点。
create index word_gin_idx on words using gist(word gist_trgm_ops);
现在诸如word like '%this%'
之类的查询将使用Gist索引。
您可以以同样的方式手动重写查询
word like 'abc%'
有时会被重写:
explain analyze
select * from words where word >='abc' and word <'abd'
但这只能保证在 C 排序规则中给出相同的答案。顺便说一句,您应该使用 EXPLAIN ANALYZE 检查分区修剪。分区修剪可能仅在运行时发生,在这种情况下,所有分区仍显示在计划 EXPLAIN 计划中。 (但运行时修剪不是这里的情况,我查了一下)