Function 带循环的PostgreSQL函数

Function 带循环的PostgreSQL函数,function,postgresql,aggregate-functions,plpgsql,Function,Postgresql,Aggregate Functions,Plpgsql,我不擅长博士后的工作。你能帮我吗? 比如,我有一个db: name | round |position | val ----------------------------------- A | 1 | 1 | 0.5 A | 1 | 2 | 3.4 A | 1 | 3 | 2.2 A | 1 | 4 | 3.8 A | 2

我不擅长博士后的工作。你能帮我吗?
比如,我有一个db:

name    | round   |position | val
-----------------------------------
A       | 1       | 1       | 0.5
A       | 1       | 2       | 3.4
A       | 1       | 3       | 2.2
A       | 1       | 4       | 3.8
A       | 2       | 1       | 0.5
A       | 2       | 2       | 32.3
A       | 2       | 3       | 2.21
A       | 2       | 4       | 0.8
我想写一个Postgres函数,它可以从
position=1
循环到
position=4
,并计算相应的值。我可以在python中使用psycopg2实现这一点:

import psycopg2
import psycopg2.extras

conn = psycopg2.connect("host='localhost' dbname='mydb' user='user' password='pass'")
CURSOR = conn.cursor(cursor_factory=psycopg2.extras.DictCursor)
cmd = """SELECT name, round, position, val from mytable"""
CURSOR.execute(cmd)
rows = CURSOR.fetchall()

dict = {}
for row in rows:
    indx = row['round']
    try:
        dict[indx] *= (1-row['val']/100)
    except:
        dict[indx] = (1-row['val']/100)
    if row['position'] == 4:
        if indx == 1:
            result1 = dict[indx]
        elif indx == 2:
            result2 = dict[indx]
print result1, result2
如何在Postgres中直接执行相同的操作,使其返回一个
(name,result1,result2)

更新:
@一匹没有名字的马,预期值为:

result1 = (1 - 0.5/100) * (1 - 3.4/100) * (1 - 2.2/100) * (1 - 3.8/100) = 0.9043
result2 = (1 - 0.5/100) * (1 - 32.3/100) * (1 - 2.21/100) * (1 - 0.8/100) = 0.6535

我猜您正在寻找一个聚合“乘积”函数。您可以在Postgresql和Oracle中创建自己的聚合函数

    CREATE TABLE mytable(name varchar(32), round int, position int, val decimal);

    INSERT INTO mytable VALUES('A', 1, 1, 0.5);
    INSERT INTO mytable VALUES('A', 1, 2, 3.4);
    INSERT INTO mytable VALUES('A', 1, 3, 2.2);
    INSERT INTO mytable VALUES('A', 1, 4, 3.8);

    INSERT INTO mytable VALUES('A', 2, 1, 0.5);
    INSERT INTO mytable VALUES('A', 2, 2, 32.3);
    INSERT INTO mytable VALUES('A', 2, 3, 2.21);
    INSERT INTO mytable VALUES('A', 2, 4, 0.8);

    CREATE AGGREGATE product(double precision) (SFUNC=float8mul, STYPE=double precision, INITCOND=1);

    SELECT name, round, product(1-val/100) AS result
      FROM mytable
      GROUP BY name, round;

     name | round |     result
    ------+-------+----------------
     A    |     2 | 0.653458283632
     A    |     1 |  0.90430333812
    (2 rows)  
请参阅Postgresql文档中的“用户定义聚合”。上面的例子是我借鉴的
. 其他stackoverflow响应也显示了这一点。

@Glenn为您提供了一个带有聚合函数的非常优雅的解决方案。但要回答您的问题,plpgsql函数可以如下所示:

测试设置:

CREATE TEMP TABLE mytable (
  name  text
, round int
, position int
, val double precision);

INSERT INTO mytable VALUES
 ('A', 1, 1, 0.5)
,('A', 1, 2, 3.4)
,('A', 1, 3, 2.2)
,('A', 1, 4, 3.8)
,('A', 2, 1, 0.5)
,('A', 2, 2, 32.3)
,('A', 2, 3, 2.21)
,('A', 2, 4, 0.8);
泛型函数 结果:

name | round |结果
-----+-------+---------------
A | 1 | 0.90430333812
A | 2 | 0.653458283632
根据问题的具体功能 结果:

name | result1 | result2
-----+---------------+---------------
A | 0.90430333812 | 0.653458283632

这是“四舍五入”中每个值的一个总和吗?你能告诉我们你的样本数据的预期输出吗?@a_horse_,没有名字嗨,请看更新部分:D谢谢你;但是从外观上看,您可能正在寻找PostgreSQL的窗口函数。有关更多信息,请参阅。
CREATE OR REPLACE FUNCTION f_grp_prod()
  RETURNS TABLE (
    name text
  , round int
  , result double precision) AS
$BODY$
DECLARE
    r mytable%ROWTYPE;
BEGIN
    -- init vars
    name    := 'A';     -- we happen to know initial value
    round   := 1;       -- we happen to know initial value
    result  := 1;

FOR r IN
    SELECT *
    FROM mytable m
    ORDER BY m.name, m.round
LOOP
    IF (r.name, r.round) <> (name, round) THEN  -- return result before round
        RETURN NEXT;
        name    := r.name;
        round   := r.round;
        result  := 1;
    END IF;

    result := result * (1 - r.val/100);
END LOOP;

RETURN NEXT;    -- return final result

END;
$BODY$ LANGUAGE plpgsql STABLE;
SELECT * FROM f_grp_prod();
CREATE OR REPLACE FUNCTION f_grp_prod(text)
  RETURNS TABLE (
    name text
  , result1 double precision
  , result2 double precision) AS
$BODY$
DECLARE
    r      mytable%ROWTYPE;
    _round integer;
BEGIN
    -- init vars
    name    := $1;
    result2 := 1;       -- abuse result2 as temp var for convenience

FOR r IN
    SELECT *
    FROM   mytable m
    WHERE  m.name = name
    ORDER  BY m.round
LOOP
    IF r.round <> _round THEN   -- save result1 before 2nd round
        result1 := result2;
        result2 := 1;
    END IF;

    result2 := result2 * (1 - r.val/100);
    _round  := r.round;
END LOOP;

RETURN NEXT;

END;
$BODY$      LANGUAGE plpgsql STABLE;
SELECT * FROM f_grp_prod('A');