我不擅长postgres功能。你能救我吗? 说,我有这个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)
更新:
@a_horse_with_no_name,期望值为:
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
答案 0 :(得分:7)
@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);
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();
结果:
name | round | result
-----+-------+---------------
A | 1 | 0.90430333812
A | 2 | 0.653458283632
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');
结果:
name | result1 | result2
-----+---------------+---------------
A | 0.90430333812 | 0.653458283632
答案 1 :(得分:4)
我猜您正在寻找聚合“产品”功能。您可以在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文档中的“用户定义的聚合”。上面的例子我借用了 here。还有其他stackoverflow响应显示other methods来执行此操作。