如何创建PostgreSQL的自定义窗口的功能? (运行平均实施例)(How to create

2019-08-03 17:47发布

我真的想更好地了解什么是参与创建工作在PostgreSQL中窗口一个UDF。 我做了一些搜索有关如何在通用的UDF创建,但还没有找到如何做一个工作在窗口的例子。

为此,我希望有人愿意分享如何写一个UDF代码(可在C,PL / SQL或任何PostgreSQL支持的程序语言)计算的数字在窗口中运行的平均值。 我知道有办法通过与窗口语法采用标准平均聚合函数做这个(语法之间行,我相信),我只是要求这个功能,因为我觉得这也是一个不错的简单的例子。 另外,我觉得如果有平均功能的窗口版本,则数据库可以保持运行总和和观察计数,并在每次迭代不会总结行几乎相同的套。

Answer 1:

你必须寻找到PostgreSQL源代码的PostgreSQL /文件src / backend / utils的/ ADT / windowfuncs.c和PostgreSQL / src目录/后端/执行/ nodeWindowAgg.c

有没有好的文档:( - 全功能窗口功能只能在C或PL / V8可以实现 - 也有其他语言的API。

http://www.pgcon.org/2009/schedule/track/Version%208.4/128.en.html从PostgreSQL中实现的作者的介绍。

我发现只有一个非核心实现- http://api.pgxn.org/src/kmeans/kmeans-1.1.0/

http://pgxn.org/dist/plv8/1.3.0/doc/plv8.html



Answer 2:

根据文档 “ 的其他窗函数可以由用户来添加。另外,任何内建或用户定义的正常聚集体功能可作为一个窗口函数。”(见第4.2.8)。 这为我工作计算股票分割的调整:

CREATE OR REPLACE FUNCTION prod(float8, float8) RETURNS float8
  AS 'SELECT $1 * $2;'
  LANGUAGE SQL IMMUTABLE STRICT;

CREATE AGGREGATE prods ( float8 ) (
  SFUNC = prod,
  STYPE = float8,
  INITCOND = 1.0
);

create or replace view demo.price_adjusted as
  select id, vd,
    prods(sdiv) OVER (PARTITION by id ORDER BY vd DESC ROWS UNBOUNDED PRECEDING) as adjf,
    rawprice * prods(sdiv) OVER (PARTITION by id ORDER BY vd DESC ROWS UNBOUNDED PRECEDING) as price
  from demo.prices_raw left outer join demo.adjustments using (id,vd);

下面是这两个表的模式:

CREATE TABLE demo.prices_raw (
  id VARCHAR(30),
  vd DATE,
  rawprice float8 );

CREATE TABLE demo.adjustments (
  id VARCHAR(30),
  vd DATE,
  sdiv float);


Answer 3:

PL / R提供这样的功能。 见这里的一些例子。 这就是说,我不知道它(目前)满足您的“养[和]运行总和和观察计数和[不]和[明]了几乎相同的设置在每次迭代行的”要求(见这里 ) 。



Answer 4:

与表开始

 payments
+------------------------------+
| customer_id | amount | item  |
| 5           | 10     | book  |
| 5           | 71     | mouse |
| 7           | 13     | cover |
| 7           | 22     | cable |
| 7           | 19     | book  |
+------------------------------+
SELECT customer_id, 
    AVG(amount) OVER (PARTITION BY customer_id) AS avg_amount,   
    item, 
FROM payments`

我们得到

+----------------------------------+
| customer_id | avg_amount | item  |
| 5           | 40.5       | book  |
| 5           | 40.5       | mouse |
| 7           | 18         | cover |
| 7           | 18         | cable |
| 7           | 18         | book  |
+----------------------------------+

AVG是一个聚合函数,它可以作为一个窗口函数。 然而,并非所有的窗函数聚合函数。 聚合函数是不复杂的窗函数。

在上面的查询,我们不要用内置AVG功能和使用我们自己的实现。 不一样的,只是由用户来实现。 上面的查询变为:

SELECT customer_id, 
    my_avg(amount) OVER (PARTITION BY customer_id) AS avg_amount,   
    item, 
FROM payments`

从以前的查询唯一不同的是, AVG已经替换为my_avg 。 现在,我们需要实现我们的自定义功能。

关于如何计算平均

总结所有的元素,然后通过元素的数量鸿沟。 对于customer_id 7,这将是(13 + 22 + 19) / 3 = 18 。 我们可以devide它:

  • 一步一步积累 - 的总和。
  • 最后的操作 - 师。

在聚合函数如何得到的结果

平均计算中的步骤。 只有最后一个值是必要的。 开始为0的初始值。

  1. 订阅13.计算中间/累加和,这是13。
  2. 订阅22.计算累加和,需要前面的总和加上该元素: 13 + 22 = 35
  3. 订阅19.计算累加和,需要前面的总和加上该元素: 35 + 19 = 54 。 这是需要由元件(3)的数目被划分总。
  4. 步骤3的结果被馈送到另一个功能,它知道如何通过元件的数目来划分累加和

这里发生了什么,就是国家开始的初始值为0的每一步改变,然后传递给下一个步骤。

国家只要有数据步骤之间传播。 当所有数据被消耗状态变为一个最终功能(终端操作)。 我们希望国家包含所需蓄电池以及终端操作的所有信息。

在计算平均值的特定情况下,终端操作需要知道蓄电池多少元素,因为它需要通过划分工作。 出于这个原因,该状态需要同时包括累加和与元件的数量。

我们需要一个元组将包含两个。 预定义POINT PostgreSQL类型救援。 点(5,89)是指具有89的值的初始状态将是一个点(0,0)5种元素的累积总和。

蓄电装置处于什么叫做状态函数来实现。 终端操作在什么所谓的最终功能实现。

当定义一个自定义的聚合函数,我们需要指定:

  • 聚集函数名和返回类型
  • 初始状态
  • 该基础设施将步骤之间和到最后的功能传递状态的种类
  • 状态函数 - 知道如何进行积累步骤
  • 最终的功能 - 知道如何进行终端操作。 并不总是需要(例如SUM的自定义实现累计总和的终值是结果。)

下面是自定义聚集函数的定义。

CREATE AGGREGATE my_avg (NUMERIC) ( -- NUMERIC is what the function returns
    initcond = '(0,0)', -- this is the initial state of type POINT
    stype = POINT, -- this is the type of the state that will be passed between steps
    sfunc = my_acc, -- this is the function that knows how to compute a new average from existing average and new element. Takes in the state (type POINT) and an element for the step (type NUMERIC)
    finalfunc my_final_func -- returns the result for the aggregate function. Takes in the state of type POINT (like all other steps) and returns the result as what the aggregate function returns - NUMERIC 
);

唯一剩下的就是定义两个函数my_accmy_final_func

CREATE FUNCTION my_acc (state POINT, elem_for_step NUMERIC) -- performs accumulated sum
RETURNS POINT
LANGUAGE SQL
AS $$
    -- state[0] is the number of elements, state[1] is the accumulated sum
    SELECT POINT(state[0]+1, state[1] + elem_for_step);
$$;

CREATE FUNCTION my_final_func (POINT) -- performs devision and returns final value
RETURNS NUMERIC
LANGUAGE SQL
AS $$
    -- $1[1] is the sum, $1[0] is the number of elements
    SELECT ($1[1]/$1[0])::NUMERIC;
$$;

现在,该功能可CREATE AGGREGATE上面定义的成功运行。 现在,我们已经聚集定义的基础上,查询my_avg ,而不是内置AVG可以运行:

SELECT customer_id, 
    my_avg(amount) OVER (PARTITION BY customer_id) AS avg_amount,    
    item, 
FROM payments`

结果与你使用内置的时候得到什么相同AVG

PostgreSQL文档表明用户被限制为实现用户定义的集合函数:

除了这些[预先定义的窗]的功能,任何内建或用户定义的通用或统计集合(即,不是有序集或假想集聚集体)可以被用作一个窗函数;

我怀疑ordered-set or hypothetical-set aggregates是指:

  • 返回的值是相同的所有其他行(例如AVGSUM 。相比之下RANK所有行组中根据更复杂的标准将返回不同的值)
  • 这是没有意义的ORDER分区时,因为值是所有行相同反正BY。 相反,我们要ORDER BY当使用RANK()

查询:

SELECT customer_id, item, rank() OVER (PARTITION BY customer_id ORDER BY amount desc) FROM payments;

几何平均数

以下是用户定义的聚合函数,我发现没有内建聚集,可能是一些有用的。

国家函数计算方面的自然对数的平均值。

最终的功能提出了不断e到任何蓄电池提供。

CREATE OR REPLACE FUNCTION sum_of_log(state POINT, curr_val NUMERIC)
RETURNS POINT
LANGUAGE SQL
AS $$
    SELECT POINT(state[0] + 1,
        (state[1] * state[0]+ LN(curr_val))/(state[0] + 1));
$$;

CREATE OR REPLACE FUNCTION e_to_avg_of_log(POINT)
RETURNS NUMERIC
LANGUAGE SQL
AS $$
    select exp($1[1])::NUMERIC;
$$;

CREATE AGGREGATE geo_mean (NUMBER)
(
    stype = NUMBER,
    initcond = '(0,0)', -- represent POINT value
    sfunc = sum_of_log,
    finalfunc = e_to_avg_of_log
);


文章来源: How to create a custom windowing function for PostgreSQL? (Running Average Example)