使用连接和LIKE查询性能

时间:2015-04-13 06:46:32

标签: sql postgresql pattern-matching concatenation postgresql-performance

有人可以解释这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条件更快?

3 个答案:

答案 0 :(得分:3)

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

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

  2. 如果你要将三个条件ORing在一起,postgres可能只需要评估其中一个或两个,以决定是否必须包含该行。

  3. ||的函数调用相比,使用concat运算符的表达式求值可能更有效,或者可能更容易优化。我不会惊讶地发现内部操作员有一些特殊的案件处理。

  4. 正如评论中所提到的,您的样本太小,无论如何都无法做出正确的结论。在几分之一毫秒的水平上,其他噪声因素可能会扭曲结果。

答案 1 :(得分: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);

说明:

如果可以为NULL则测试不正确

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

详细说明:

如果您正在寻找干净,优雅的表达方式(大致相同的费用),请改用concat_ws()

concat_ws( ' ', last_name, first_name, middle_name)

答案 2 :(得分:0)

此查询具有在每行上调用函数的开销

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 '%Ива%');