迭代PostgreSQL记录。如何引用下一行的数据?

时间:2013-03-30 02:40:47

标签: sql postgresql plpgsql window-functions

我是PostgreSQL的新手,在这里编写函数很难实现。所以我希望有人能帮助我知道如何做我想做的事。

我有一张股票价格和日期表。我想计算每个条目前一天的变化百分比。对于数据的最早日期,将不会有前一天,因此输入可以简单地为零。有人可以查看我的功能并帮助我 a)如何引用下一行的数据和
b)帮我清理一下?

我知道WITH语句可能不应该高于IF语句。不过从逻辑上讲,这是我到目前为止所考虑的方式,也就是我如何编写它。如果您可以建议它应该如何看,那么也会非常感激。

CREATE FUNCTION percentage_change_func(asset_histories) RETURNS 
numeric LANGUAGE sql IMMUTABLE AS $func$

DECLARE
r asset_histories%rowtype
BEGIN   
WITH twodaysdata AS (SELECT date,price,asset_symbol FROM asset_histories 
           WHERE asset_symbol = $1.asset_symbol 
           AND asset_histories.date <= $1.date 
           ORDER BY date DESC LIMIT 2), 
         numberofrecords AS (SELECT count(*) FROM twodaysdata) 

IF numberofrecords = 2 THEN
        RETURN r.price / (r+1).price - 1  <---How do I reference r + 1??/
ELSE
        RETURN NIL
ENDIF
END
$func$

PostgreSQL 9.2。

2 个答案:

答案 0 :(得分:8)

  

我想计算每一天的前一天的变化百分比   条目。

一般来说,在开始提问之前,你需要学习基础知识 阅读关于CREATE FUNCTIONPL/pgSQLSQL functions的优秀手册。

为什么这个例子是无意义的主要观点

  • 首先,您不能像您一样提交标识符。标识符不能在纯SQL中参数化。你需要dynamic SQL 当然,根据您的要求,您实际上并不需要这样做。只涉及一个表。尝试参数化是没有意义的。

  • 不要使用类型名称作为标识符。我使用_date代替date作为参数名称,并将您的表格列重命名为asset_date。相应地ALTER你的表定义。

  • 从表中获取数据的函数永远不能是IMMUTABLERead the manual.

  • 您正在以无意义的方式将SQL语法与plpgsql元素混合。 WITHSELECT语句的一部分,不能与LOOPIF等plpgsql控件结构混合使用。

正常功能

正确的功能可能看起来像这样(多种方式之一):

CREATE FUNCTION percentage_change_func(_asset_symbol text)
  RETURNS TABLE(asset_date date, price numeric, pct_change numeric) AS
$func$
DECLARE
   last_price numeric;
BEGIN

FOR asset_date, price IN
   SELECT a.asset_date, a.price
   FROM   asset_histories a
   WHERE  a.asset_symbol = _asset_symbol 
   ORDER  BY a.asset_date  -- traverse ascending
LOOP
   pct_change := price / last_price; -- NULL if last_price is NULL
   RETURN NEXT;
   last_price := price;
END LOOP;

END
$func$ LANGUAGE plpgsql STABLE

性能不应该那么糟糕,但这只是毫无意义的复杂化。

正确的解决方案:普通查询

最简单(也可能是最快)的方法是使用窗口函数lag()

SELECT asset_date, price
      ,price / lag(price) OVER (ORDER BY asset_date) AS pct_change
FROM   asset_histories
WHERE  asset_symbol = _asset_symbol 
ORDER  BY asset_date;

标准差

根据您之后的评论,您希望计算标准偏差等统计数字 PostgreSQL中有专门的aggregate functions for statistics

答案 1 :(得分:0)

简单的事情,比如计算per_change,可以在view内完成,这也可以带来更快的结果

create view view_stock_details AS ( SELECT 
    date, 
    price, 
    symbol, 
    pervious_day_close, 
    (price-pervious_day_close)/pervious_day_close*100 as per_change 
FROM (
    SELECT
        date,
        price,
        symbol,
        ( SELECT price FROM asset_histories t WHERE t.symbol = outers.symbol AND t.date < outers.date limit 1 ) as pervious_day_close
    FROM 
        asset_histories as outers
    ); 

要查看股票详细信息,您可以使用

SELECT 
    *
FROM
    view_stock_details
WHERE
    date = '2012-01-03' 
    AND symbol = 'A'