使用连接和 LIKE 查询性能

Posted

技术标签:

【中文标题】使用连接和 LIKE 查询性能【英文标题】:Query performance with concatenation and LIKE 【发布时间】:2015-06-18 09:49:51 【问题描述】:

谁能解释这 3 个查询之间的性能差异?

concat()函数:

explain analyze 
select * from person 
where (concat(last_name, ' ', first_name, ' ', middle_name) like '%Ива%');

Seq Scan on person  (cost=0.00..4.86 rows=1 width=15293) (actual time=0.032..0.140 rows=6 loops=1)
  Filter: (pg_catalog.concat(last_name, ' ', first_name, ' ', middle_name) ~~ '%Ива%'::text)
Total runtime: 0.178 ms

|| 的SQL 标准连接:

explain analyze 
select * from person 
where ((last_name || ' ' || first_name || ' ' || middle_name) like '%Ива%');

Seq Scan on person  (cost=0.00..5.28 rows=1 width=15293) (actual time=0.023..0.080 rows=6 loops=1)
  Filter: ((((((last_name)::text || ' '::text) || (first_name)::text) || ' '::text) || (middle_name)::text) ~~ '%Ива%'::text)
Total runtime: 0.121 ms

单独搜索字段:

explain analyze 
select * from person 
where (last_name like '%Ива%') or (first_name like '%Ива%') or (middle_name like '%Ива%');

Seq Scan on person  (cost=0.00..5.00 rows=1 width=15293) (actual time=0.018..0.060 rows=6 loops=1)
  Filter: (((last_name)::text ~~ '%Ива%'::text) OR ((first_name)::text ~~ '%Ива%'::text) OR ((middle_name)::text ~~ '%Ива%'::text))
Total runtime: 0.097 ms

为什么concat() 条件最慢,为什么几个like 条件更快?

【问题讨论】:

你的问题到底是什么? 为什么pg.concat 一个最慢,而几个like 更快? @ma3a 在你的情况下这是一个开销 对只有 6 行的表进行测试并不意味着很多。而且这个查询是如此之快,以至于你的计算机的任何一点屁都可能导致时间上的差异。 @ma3a:这有什么不同吗?它仍然是一个几乎是空的表......创建一百万条记录并再次运行您的测试。无论如何,在一毫秒内运行的顺序扫描永远不会“慢”。 【参考方案1】:

虽然不是一个具体的答案,但以下内容可能会帮助您得出一些结论:

    调用concat 连接三个字符串,或使用|| 运算符,导致postgres 必须分配一个新缓冲区来保存连接的字符串,然后将字符复制到其中。这必须为每一行完成。然后缓冲区必须在最后被释放。

    如果您将三个条件组合在一起,postgres 可能只需要评估其中一个或两个条件来决定是否必须包含该行。

    与对concat 的函数调用相比,使用|| 运算符的表达式评估可能更有效,或者更容易优化。如果发现内部运算符有一些特殊情况处理,我不会感到惊讶。

    正如 cmets 中所述,您的样本太小而无法得出正确的结论。在几分之一毫秒的水平上,其他噪声因素可能会扭曲结果。

【讨论】:

“几百毫秒”,测试用例更糟糕,所有测试都在一毫秒内跑完,最慢的测试只用了0.178毫秒。【参考方案2】:

到目前为止,您观察到的内容很有趣,但并不重要。连接字符串的开销很小。

这些表达式之间的更重要的区别不会显示在没有索引的最小测试用例中。

前两个例子不是sargable(除非你建立了一个定制的表达式索引):

where concat(last_name, ' ', first_name, ' ', middle_name) like '%Ива%'
where (last_name || ' ' || first_name || ' ' || middle_name) like '%Ива%'

虽然这个是:

where last_name like '%Ива%' or first_name like '%Ива%' or middle_name like '%Ива%'

也就是说,它可以使用一个简单的三元组索引来产生很好的效果(列的顺序在 GIN 索引中并不重要):

CREATE INDEX some_idx ON person USING gin (first_name  gin_trgm_ops
                                         , middle_name gin_trgm_ops
                                         , last_name   gin_trgm_ops);

说明:

PostgreSQL LIKE query performance variations

如果可能为 NULL,则测试不正确

concat() 通常比使用|| 的简单字符串连接稍微贵一些。这也是不同:如果任何输入字符串为 NULL,则在第二种情况下连接的结果也是 NULL,但在第一种情况下不是,因为 concat() 只是忽略 NULL 值 - 但你' 仍然会在结果中得到一个无用的空格字符。

详细解释:

Combine two columns and add into one new column

如果您正在寻找一种简洁、优雅的表达方式(大约相同的成本),请改用concat_ws()

concat_ws( ' ', last_name, first_name, middle_name)

【讨论】:

【参考方案3】:

这个查询在每一行调用函数都有开销

explain analyze 
select * from person 
where (concat(last_name, ' ', first_name, ' ', middle_name) like '%Ива%');

这个查询更快,因为没有执行额外的操作

explain analyze 
select * from person 
where (last_name like '%Ива%') or (first_name like '%Ива%') or (middle_name like '%Ива%');

【讨论】:

以上是关于使用连接和 LIKE 查询性能的主要内容,如果未能解决你的问题,请参考以下文章

MySQL连接查询使用like?

VB连接ACCESS数据库,使用 LIKE 通配符问题

java连接数据库的模糊查询

数据库面试系列大纲

将多条查询结果作为like查询条件

表连接查询与where后使用子查询的性能分析。