sql - 在 SELECT 语句中调用函数时的性能

标签 sql performance postgresql string-formatting plpgsql

我有一个查询,我需要调用 SQL 函数来格式化查询中的特定列。所需的格式与格式化电话号码非常相似,即。将 1234567890 更改为 (123)456-7890

我读到从 select 语句调用函数可能会成为性能 killer ,这在我的情况中有所反射(reflect),查询花费的时间增加了两倍多,我认为该函数不会花费这么多时间更长。该函数以线性时间运行,但确实使用了 SQL 循环。为了解数据库的大小,此特定查询返回大约 220,000 行。在不调用函数的情况下运行与运行调用函数时,查询的运行时间从 < 3 秒变为 > 9 秒。需要格式化的列未在连接条件或 where 子句中编制索引或使用。

这里的性能下降是预期的还是我可以做些什么来改善它?

这是有问题的功能:

CREATE OR REPLACE FUNCTION fn(bigint)
  RETURNS character varying LANGUAGE plpgsql AS
$BODY$
DECLARE
   v_chars varchar[];
   v_ret   varchar;
   v_length int4;
   v_count  int4;
BEGIN
   if ($1 isnull or $1 = 0) then
      return null;
   end if;

   v_chars  := regexp_split_to_array($1::varchar,'');
   v_ret    := '';
   v_length := array_upper (v_chars,1);
   v_count  := 0;

   for v_index in 1..11   loop
      v_count := v_count + 1;

      if (v_index <= v_length) then
         v_ret := v_chars[v_length - (v_index - 1)] || v_ret;
      else
         v_ret := '0' || v_ret;
      end if;

      if (v_count <= 6 and (v_count % 2) = 0) then
         v_ret := '.' || v_ret;
      end if;
   end loop;

   return v_ret;
END
$BODY$

最佳答案

这取决于函数的细节。要了解纯函数调用 的成本,请创建虚拟函数,例如:

CREATE FUNCTION f_bare_plpgsql(text)
  RETURNS text LANGUAGE plpgsql IMMUTABLE AS
$BODY$
BEGIN
   RETURN $1;
END
$BODY$;

CREATE FUNCTION f_bare_sql(text)
  RETURNS text LANGUAGE sql IMMUTABLE AS
$BODY$
   SELECT $1;
$BODY$;

然后再次尝试您的查询。
如果那时您想知道为什么您的功能很慢,请将其添加到您的问题中。


更新问题的解决方案

您的功能在很多地方都可以改进,但是有一个更激进的解决方案:

SELECT to_char(12345678901, '00000"."00"."00"."00')

显然要快很多倍。更多关于 to_char() in the manual 的信息。
考虑以下演示:

WITH x(n) AS (
   VALUES  (1::bigint), (12), (123), (1234), (12345), (123456), (1234567)
          ,(12345678), (123456789), (1234567890), (12345678901), (123456789012)
   )
SELECT n, x.fn(n), to_char(n, '00000"."00"."00"."00')
FROM x

      n       |       fn       |     to_char
--------------+----------------+-----------------
            1 | 00000.00.00.01 |  00000.00.00.01
           12 | 00000.00.00.12 |  00000.00.00.12
          123 | 00000.00.01.23 |  00000.00.01.23
         1234 | 00000.00.12.34 |  00000.00.12.34
        12345 | 00000.01.23.45 |  00000.01.23.45
       123456 | 00000.12.34.56 |  00000.12.34.56
      1234567 | 00001.23.45.67 |  00001.23.45.67
     12345678 | 00012.34.56.78 |  00012.34.56.78
    123456789 | 00123.45.67.89 |  00123.45.67.89
   1234567890 | 01234.56.78.90 |  01234.56.78.90
  12345678901 | 12345.67.89.01 |  12345.67.89.01
 123456789012 | 23456.78.90.12 |  #####.##.##.##
如您所见,

to_char() 只准备最多 11 位十进制数字。
如果需要,可以轻松扩展。

关于sql - 在 SELECT 语句中调用函数时的性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12378958/

相关文章:

java - 时区未显示

php - 用户名已被使用

php - 在 URL 中公开类和方法名称是否不安全?

haskell - 在以线性时间运行的 Haskell 中实现反向

postgresql - postgres 使用数据库密码或用户密码

postgresql - 使用函数结果作为 PostgreSQL 查询中的列

sql - Django限制分组查询的方法

sql - 无法根据“不在哪里”进行过滤

php - MySQLi 查询在没有任何结果时给出错误

java - 向 Java 应用程序添加许多框架 - 会产生任何负面影响吗?