为什么我使用LIKE的查询执行seq扫描?

时间:2018-03-08 08:51:57

标签: sql postgresql indexing

我有一张220万行的表。

Hash

该位置是文件的完整路径,但我需要使用该文件所在文件夹的名称进行查询。该文件夹名称在表格中是唯一的。

为了避免在开头 Table "public.index" Column | Type | Modifiers -----------+-----------------------------+----------------------------------------------------- fid | integer | not null default nextval('index_fid_seq'::regclass) location | character varying | Indexes: "index_pkey" PRIMARY KEY, btree (fid) "location_index" btree (location text_pattern_ops) ,我会搜索我知道的完整路径:

%

解释分析:

select fid from index where location like '/path/to/folder/%'

问题不在于如何制定解决方法,因为 我找到了我的情况:

创建 QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ Seq Scan on index (cost=0.00..120223.34 rows=217 width=4) (actual time=1181.701..1181.701 rows=0 loops=1) Filter: ((location)::text ~~ '/path/to/folder/%'::text) Rows Removed by Filter: 2166034 Planning time: 0.954 ms Execution time: 1181.748 ms (5 rows)

foldername_index

我可以成功使用folder_name查询:

create index on index (substring(location, '(?<=/path/to/)[^\/]*');

我已关注PostgreSQL FAQ

  

使用LIKE或〜等通配符运算符时,索引只能是   在某些情况下使用:

     

搜索字符串的开头必须锚定到字符串的开头,即

     

LIKE模式不能以%或_开头。

     

搜索字符串不能以字符类开头,例如[A-E]。

在我的查询中并非如此。

  

在initdb期间必须使用C语言环境,因为在非C语言环境中排序通常与LIKE的行为不匹配。您可以创建一个特殊的text_pattern_ops索引,在这种情况下可以使用,但请注意它只对LIKE索引有用。

我有C语言环境:

explain analyze select fid from index where substring(location, '(?<=/path/to/)[^\/]*') = 'foldername';

    QUERY PLAN                                                                                  
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on index  (cost=600.49..31524.74 rows=10830 width=12) (actual time=0.030..0.030 rows=1 loops=1)
   Recheck Cond: ("substring"((location)::text, '(?<=/path/to/)[^\/]*'::text) = 'folder_name'::text)
   Heap Blocks: exact=1
   ->  Bitmap Index Scan on foldername_index  (cost=0.00..597.78 rows=10830 width=0) (actual time=0.023..0.023 rows=1 loops=1)
         Index Cond: ("substring"((location)::text, '(?<=/path/to/)[^\/]*'::text) = 'folder_name'::text)
 Planning time: 0.115 ms
 Execution time: 0.059 ms
(7 rows)

我还在Stack Overflow上遵循此great answer的说明,这就是我使用# show LC_COLLATE; lc_collate ------------ C (1 row) 并没有改变任何内容的原因。不幸的是,我无法安装新模块。

所以:为什么我的查询执行seq扫描?

1 个答案:

答案 0 :(得分:3)

我通过一遍又一遍地思考它找到了解决方案。虽然对某些人来说可能是显而易见的,但它可能对其他人有所帮助:

/path/to/folder实际上是/the_path/to/folder/(路径中有下划线)。但_是SQL中的通配符(如%)。

select fid from index where location like '/the_path/to/folder/%'

使用seq扫描,因为索引无法过滤任何行,因为直到下划线的部分对于所有行都是相同的。

select fid from index where location like '/the\_path/to/folder/%'

使用索引扫描。