使用串联和LIKE查询性能

Yeg*_*dov 3 sql postgresql concatenation pattern-matching postgresql-performance

有人可以解释这三个查询之间的性能差异吗?

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

分别搜索字段:

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
Run Code Online (Sandbox Code Playgroud)

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

har*_*mic 5

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

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

  2. 在将三个条件进行“或”运算的情况下,postgres可能只需要评估其中一个或两个就可以决定是否必须包含该行。

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

  4. 正如评论中所提到的,您的样本太小而无法得出正确的结论。在几分之一毫秒的水平上,其他噪声因素会使结果失真。


Erw*_*ter 5

到目前为止,您所观察到的情况很有趣,但连接字符串的成本开销很小。

\n

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

\n

前两个示例不可控制(除非您构建定制的表达式索引):

\n
where concat(last_name, \' \', first_name, \' \', middle_name) like \'%\xd0\x98\xd0\xb2\xd0\xb0%\'\nwhere (last_name || \' \' || first_name || \' \' || middle_name) like \'%\xd0\x98\xd0\xb2\xd0\xb0%\'\n
Run Code Online (Sandbox Code Playgroud)\n

虽然这个是:

\n
where last_name like \'%\xd0\x98\xd0\xb2\xd0\xb0%\' or first_name like \'%\xd0\x98\xd0\xb2\xd0\xb0%\' or middle_name like \'%\xd0\x98\xd0\xb2\xd0\xb0%\'\n
Run Code Online (Sandbox Code Playgroud)\n

即,它可以使用普通的 trigram 索引来达到很好的效果(列的顺序在 GIN 索引中并不重要):

\n
CREATE INDEX some_idx ON person USING gin (first_name  gin_trgm_ops\n                                         , middle_name gin_trgm_ops\n                                         , last_name   gin_trgm_ops);\n
Run Code Online (Sandbox Code Playgroud)\n

指示:

\n\n

错误的测试(如果null可能)

\n

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

\n

详细解释:

\n\n

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

\n
concat_ws( \' \', last_name, first_name, middle_name)\n
Run Code Online (Sandbox Code Playgroud)\n