【发布时间】:2020-09-05 18:02:11
【问题描述】:
我目前正在学习 UDF,并在下面编写了 PostgreSQL UDF 来计算平均偏差 (MAD)。它是任何窗口的平均值和当前值之间的平均绝对差。 在 python pandas/numpy 中,要找到 MAD,我们可以这样写:
series_mad = abs(series - series.mean()).mean()
其中 series 是一组数字,而 series_mad 是表示该系列的 MAD 的单个数值。
我正在尝试使用 Windows 和 UDF 在 PostgreSQL 中编写此代码。到目前为止,这就是我所拥有的:
CREATE TYPE misc_tuple AS (
arr_store numeric[],
ma_period integer
);
CREATE OR REPLACE FUNCTION mad_func(prev misc_tuple, curr numeric, ma_period integer)
RETURNS misc_tuple AS $$
BEGIN
IF curr is null THEN
RETURN (null::numeric[], -1);
ELSEIF prev.arr_store is null THEN
RETURN (ARRAY[curr]::numeric[], ma_period);
ELSE
-- accumulate new values in array
prev.arr_store := array_append(prev.arr_store, curr);
RETURN prev;
END IF;
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION mad_final(prev misc_tuple)
RETURNS numeric AS $$
DECLARE
total_len integer;
count numeric;
mad_val numeric;
mean_val numeric;
BEGIN
count := 0;
mad_val := 0;
mean_val := 0;
total_len := array_length(prev.arr_store, 1);
-- first loop to find the mean of the series
FOR i IN greatest(1,total_len-prev.ma_period+1)..total_len
LOOP
mean_val := mean_val + prev.arr_store[i];
count := count + 1;
END LOOP;
mean_val := mean_val/NULLIF(count,0);
-- second loop to subtract mean from each value
FOR i IN greatest(1,total_len-prev.ma_period+1)..total_len
LOOP
mad_val := mad_val + abs(prev.arr_store[i]-mean_val);
END LOOP;
RETURN mad_val/NULLIF(count, 0);
END;
$$ LANGUAGE plpgsql;
CREATE OR REPLACE AGGREGATE mad(numeric, integer) (
SFUNC = mad_func,
STYPE = misc_tuple,
FINALFUNC = mad_final
);
这是我测试性能的方式:
-- find rolling 12-period MAD
SELECT x,
mad(x, 12) OVER (ROWS 12-1 PRECEDING)
FROM generate_series(0,1000000) as g(x);
目前,在我的桌面(i5 4670、3.4 GHz、16 GB RAM)上大约需要 45-50 秒。我仍在学习 UDF,所以我不确定我还能对我的函数做些什么来让它更快。我还有其他一些类似的 UDF - 但不使用数组的 UDF 在相同的 1m 行上花费
我可以对此 UDF 进行任何更改以使其更快吗?
【问题讨论】:
-
请以文本形式提供样本测试日期、无图片和预期结果。
-
@Belayer 我的帖子中没有图片。问题不在于查询的结果,而在于性能。
-
我的要求是测试数据和预期结果。请求不是关于问题中的内容,而是问题中没有的内容。将其发布为文本。另外描述您要解决的问题;不仅仅是代码。
-
@Belayer 很抱歉,但我不确定我还能如何澄清。不需要测试数据,
generate_series已经为您完成了。我试图解决的问题是加快我在帖子中提到的查询。我还提到了 UDF 计算的内容以及我对代码为何缓慢的假设。因为我只是在学习 UDF,所以我不确定还有哪些其他技术可以用来加速我的代码并寻求建议——这就是问题所在。预期结果与我的代码输出的结果相同,但运行时间要快得多。
标签: sql postgresql user-defined-functions window-functions