在 Postgres 中使用动态基础进行累积添加
Cumulative adding with dynamic base in Postgres
我在 Postgres 中有以下场景(我正在使用 9.4.1)。
我有一个 table 这种格式:
create table test(
id serial,
val numeric not null,
created timestamp not null default(current_timestamp),
fk integer not null
);
然后我得到的是另一个 table 中的一个 threshold numeric
字段,它应该用于标记 test
的每一行。对于每个 >= threshold
的值,我希望将该记录标记为 true
但如果它是 true
它应该在那时将后续计数重置为 0,例如
数据集:
insert into test(val, created, fk)
(100, now() + interval '10 minutes', 5),
(25, now() + interval '20 minutes', 5),
(30, now() + interval '30 minutes', 5),
(45, now() + interval '40 minutes', 5),
(10, now() + interval '50 minutes', 5);
阈值为 50 我希望输出为:
100 -> true (as 100 > 50) [reset]
25 -> false (as 25 < 50)
30 -> true (as 25 + 30 > 50) [reset]
45 -> false (as 45 < 50)
10 -> true (as 45 + 10 > 50)
是否可以在单个 SQL 查询中执行此操作?到目前为止,我已经尝试使用 window function.
select t.*,
sum(t.val) over (
partition by t.fk order by t.created
) as threshold_met
from test t
where t.fk = 5;
如您所见,我已经达到累积频率的地步,并怀疑 rows between x preceding and current row
的调整可能正是我正在寻找的。我只是不知道如何执行重置,即将上面的 x
设置为适当的值。
Create your own aggregate function,可作为window函数使用。
专门的聚合函数
这比人们想象的要容易:
CREATE OR REPLACE FUNCTION f_sum_cap50 (numeric, numeric)
RETURNS numeric LANGUAGE sql AS
'SELECT CASE WHEN > 50 THEN 0 ELSE END + ';
CREATE AGGREGATE sum_cap50 (numeric) (
sfunc = f_sum_cap50
, stype = numeric
, initcond = 0
);
然后:
SELECT *, sum_cap50(val) OVER (PARTITION BY fk
ORDER BY created) > 50 AS threshold_met
FROM test
WHERE fk = 5;
结果完全符合要求。
通用聚合函数
使其适用于任何阈值和任何(数字)数据类型,以及允许NULL
值:
CREATE OR REPLACE FUNCTION f_sum_cap (anyelement, anyelement, anyelement)
RETURNS anyelement
LANGUAGE sql STRICT AS
$$SELECT CASE WHEN > THEN '0' ELSE END + ;$$;
CREATE AGGREGATE sum_cap (anyelement, anyelement) (
sfunc = f_sum_cap
, stype = anyelement
, initcond = '0'
);
然后,调用任何数字类型的限制,比如 110:
SELECT *
, sum_cap(val, '110') OVER (PARTITION BY fk
ORDER BY created) AS capped_at_110
, sum_cap(val, '110') OVER (PARTITION BY fk
ORDER BY created) > 110 AS threshold_met
FROM test
WHERE fk = 5;
说明
在您的情况下,我们不必防御 NULL
值,因为 val
定义为 NOT NULL
。如果可以涉及 NULL
,则将 f_sum_cap()
定义为 STRICT
并且它有效,因为 (per documentation):
If the state transition function is declared "strict", then it cannot
be called with null inputs. With such a transition function, aggregate
execution behaves as follows. Rows with any null input values are
ignored (the function is not called and the previous state value is
retained) [...]
函数和集合都多了一个参数。对于 polymorphic 变体,它可以是硬编码数据类型或与前导参数相同的多态类型。
关于多态函数:
- Initial array in function to aggregate multi-dimensional array
请注意使用 无类型字符串文字 ,而不是数字文字,后者默认为 integer
!
我在 Postgres 中有以下场景(我正在使用 9.4.1)。
我有一个 table 这种格式:
create table test(
id serial,
val numeric not null,
created timestamp not null default(current_timestamp),
fk integer not null
);
然后我得到的是另一个 table 中的一个 threshold numeric
字段,它应该用于标记 test
的每一行。对于每个 >= threshold
的值,我希望将该记录标记为 true
但如果它是 true
它应该在那时将后续计数重置为 0,例如
数据集:
insert into test(val, created, fk)
(100, now() + interval '10 minutes', 5),
(25, now() + interval '20 minutes', 5),
(30, now() + interval '30 minutes', 5),
(45, now() + interval '40 minutes', 5),
(10, now() + interval '50 minutes', 5);
阈值为 50 我希望输出为:
100 -> true (as 100 > 50) [reset]
25 -> false (as 25 < 50)
30 -> true (as 25 + 30 > 50) [reset]
45 -> false (as 45 < 50)
10 -> true (as 45 + 10 > 50)
是否可以在单个 SQL 查询中执行此操作?到目前为止,我已经尝试使用 window function.
select t.*,
sum(t.val) over (
partition by t.fk order by t.created
) as threshold_met
from test t
where t.fk = 5;
如您所见,我已经达到累积频率的地步,并怀疑 rows between x preceding and current row
的调整可能正是我正在寻找的。我只是不知道如何执行重置,即将上面的 x
设置为适当的值。
Create your own aggregate function,可作为window函数使用。
专门的聚合函数
这比人们想象的要容易:
CREATE OR REPLACE FUNCTION f_sum_cap50 (numeric, numeric)
RETURNS numeric LANGUAGE sql AS
'SELECT CASE WHEN > 50 THEN 0 ELSE END + ';
CREATE AGGREGATE sum_cap50 (numeric) (
sfunc = f_sum_cap50
, stype = numeric
, initcond = 0
);
然后:
SELECT *, sum_cap50(val) OVER (PARTITION BY fk
ORDER BY created) > 50 AS threshold_met
FROM test
WHERE fk = 5;
结果完全符合要求。
通用聚合函数
使其适用于任何阈值和任何(数字)数据类型,以及允许NULL
值:
CREATE OR REPLACE FUNCTION f_sum_cap (anyelement, anyelement, anyelement)
RETURNS anyelement
LANGUAGE sql STRICT AS
$$SELECT CASE WHEN > THEN '0' ELSE END + ;$$;
CREATE AGGREGATE sum_cap (anyelement, anyelement) (
sfunc = f_sum_cap
, stype = anyelement
, initcond = '0'
);
然后,调用任何数字类型的限制,比如 110:
SELECT *
, sum_cap(val, '110') OVER (PARTITION BY fk
ORDER BY created) AS capped_at_110
, sum_cap(val, '110') OVER (PARTITION BY fk
ORDER BY created) > 110 AS threshold_met
FROM test
WHERE fk = 5;
说明
在您的情况下,我们不必防御 NULL
值,因为 val
定义为 NOT NULL
。如果可以涉及 NULL
,则将 f_sum_cap()
定义为 STRICT
并且它有效,因为 (per documentation):
If the state transition function is declared "strict", then it cannot be called with null inputs. With such a transition function, aggregate execution behaves as follows. Rows with any null input values are ignored (the function is not called and the previous state value is retained) [...]
函数和集合都多了一个参数。对于 polymorphic 变体,它可以是硬编码数据类型或与前导参数相同的多态类型。
关于多态函数:
- Initial array in function to aggregate multi-dimensional array
请注意使用 无类型字符串文字 ,而不是数字文字,后者默认为 integer
!