我有一个查询,我需要调用 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/