更喜欢vs iLIKE

use*_*833 32 postgresql performance pattern-matching database-performance

以下两个查询组件的性能如何比较?

更喜欢

... LOWER(description) LIKE '%abcde%' ...
Run Code Online (Sandbox Code Playgroud)

我喜欢

... description iLIKE '%abcde%' ...
Run Code Online (Sandbox Code Playgroud)

use*_*833 25

根据我的测试(每个查询中的十个),LOWER LIKE大约17%快于iLIKE.

说明

我创建了一百万行包含一些随机混合文本数据:

require 'securerandom'
inserts = []
1000000.times do |i|
        inserts << "(1, 'fake', '#{SecureRandom.urlsafe_base64(64)}')"
end
sql = "insert into books (user_id, title, description) values #{inserts.join(', ')}"
ActiveRecord::Base.connection.execute(sql)
Run Code Online (Sandbox Code Playgroud)

验证行数:

my_test_db=# select count(id) from books ;
  count  
---------
 1000009
Run Code Online (Sandbox Code Playgroud)

(是的,我从其他测试中增加了9行 - 不是问题.)

示例查询和结果:

my_test_db=# SELECT "books".* FROM "books" WHERE "books"."published" = 'f'
my_test_db=# and (LOWER(description) LIKE '%abcde%') ;
   id    | user_id | title |                                      description                                       | published 
---------+---------+-------+----------------------------------------------------------------------------------------+------
 1232322 |       1 | fake  | 5WRGr7oCKABcdehqPKsUqV8ji61rsNGS1TX6pW5LJKrspOI_ttLNbaSyRz1BwTGQxp3OaxW7Xl6fzVpCu9y3fA | f
 1487103 |       1 | fake  | J6q0VkZ8-UlxIMZ_MFU_wsz_8MP3ZBQvkUo8-2INiDIp7yCZYoXqRyp1Lg7JyOwfsIVdpPIKNt1uLeaBCdelPQ | f
 1817819 |       1 | fake  | YubxlSkJOvmQo1hkk5pA1q2mMK6T7cOdcU3ADUKZO8s3otEAbCdEcmm72IOxiBdaXSrw20Nq2Lb383lq230wYg | f
Run Code Online (Sandbox Code Playgroud)

LOWER LIKE的结果

my_test_db=# EXPLAIN ANALYZE SELECT "books".* FROM "books" WHERE "books"."published" = 'f' and (LOWER(description) LIKE '%abcde%') ;
                                                   QUERY PLAN                                                   
----------------------------------------------------------------------------------------------------------------
 Seq Scan on books  (cost=0.00..32420.14 rows=1600 width=117) (actual time=938.627..4114.038 rows=3 loops=1)
   Filter: ((NOT published) AND (lower(description) ~~ '%abcde%'::text))
   Rows Removed by Filter: 1000006
 Total runtime: 4114.098 ms
Run Code Online (Sandbox Code Playgroud)

iLIKE的结果

my_test_db=# EXPLAIN ANALYZE SELECT "books".* FROM "books" WHERE "books"."published" = 'f' and (description iLIKE '%abcde%') ;
                                                   QUERY PLAN                                                   
----------------------------------------------------------------------------------------------------------------
 Seq Scan on books  (cost=0.00..29920.11 rows=100 width=117) (actual time=1147.612..4986.771 rows=3 loops=1)
   Filter: ((NOT published) AND (description ~~* '%abcde%'::text))
   Rows Removed by Filter: 1000006
 Total runtime: 4986.831 ms
Run Code Online (Sandbox Code Playgroud)

数据库信息披露

Postgres版本:

my_test_db=# select version();
                                                                                 version
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 PostgreSQL 9.2.4 on x86_64-apple-darwin12.4.0, compiled by i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.11.00), 64-bit
Run Code Online (Sandbox Code Playgroud)

整理设置:

my_test_db=# select datcollate from pg_database where datname = 'my_test_db';
 datcollate  
-------------
 en_CA.UTF-8
Run Code Online (Sandbox Code Playgroud)

表定义:

my_test_db=# \d books 
                                      Table "public.books"
   Column    |            Type             |                       Modifiers
-------------+-----------------------------+-------------------------------------------------------
 id          | integer                     | not null default nextval('books_id_seq'::regclass)
 user_id     | integer                     | not null
 title       | character varying(255)      | not null
 description | text                        | not null default ''::text
 published   | boolean                     | not null default false
Indexes:
    "books_pkey" PRIMARY KEY, btree (id)
Run Code Online (Sandbox Code Playgroud)

  • 您的测试用例是片面的,数据中只有大写字母。此外,在实际应用中,您将使用指数进行操作,这会改变整个评估。并且 * 基本 * 细节未公开:Postgres 版本、您的排序规则设置、您的确切表定义。 (3认同)
  • +1现在好多了。我不会说“ x比y快17%”,因为这仅适用于您的特定测试用例。字符串的长度也很重要,顺便说一句。 (2认同)
  • 不,不过不确定。我经常看到更接近的结果。对 Postgres 9.1 中的真实表进行了快速测试,该表具有 1.05 万行和真实“描述”,COLLATON de_AT.UTF-8,OS Debian Linux。LOWER / LIKE 快约 2%。 (2认同)

Erw*_*ter 24

答案取决于许多因素,如Postgres版本,编码和语言环境 - LC_COLLATE特别是.

裸表达式lower(description) LIKE '%abc%'通常比它快一点description ILIKE '%abc%',并且要么比等效的正则表达式快一点:description ~* 'abc'.这对于顺序扫描非常重要,其中必须针对每个测试行评估表达式.

但是对于你在答案中展示的大表,肯定会使用索引.对于任意模式(不仅是左锚定),我建议使用附加模块的trigram索引pg_trgm.然后我们讨论毫秒而不是秒,并且上述表达式之间的差异无效.

GIN和GiST索引(使用gin_trgm_opsgist_trgm_ops运算符类)支持LIKE(~~), ILIKE(~~*)~,~*(以及一些更多变体).使用trigram GIN索引description(通常大于GiST,但读取速度更快),您的查询将使用description ILIKE 'case_insensitive_pattern'.

有关:

Postgres中模式匹配的基础知识:

使用上述trigram索引时,使用它通常更实用:

description ILIKE '%abc%'
Run Code Online (Sandbox Code Playgroud)

或者使用不区分大小写的regexp运算符(没有%通配符):

description ~* 'abc'
Run Code Online (Sandbox Code Playgroud)

索引(description)不支持lower(description)类似的查询:

lower(description) LIKE '%abc%'
Run Code Online (Sandbox Code Playgroud)

反之亦然.

对于lower(description) 独占谓词,表达式索引是稍微好一点的选择.

在所有其他情况下,在一个索引(description)是优选的,因为它支持两者区分大小写和不敏感的谓词.