列上的PLpgSQL(或ANSI SQL?)条件计算
我想编写一个存储过程,对列执行条件计算。理想情况下,SP的实现将与db无关——如果可能的话。如果不是,底层数据库是PostgreSQL(v8.4),因此优先 正在查询的基础表如下所示:列上的PLpgSQL(或ANSI SQL?)条件计算,sql,postgresql,stored-procedures,plpgsql,Sql,Postgresql,Stored Procedures,Plpgsql,我想编写一个存储过程,对列执行条件计算。理想情况下,SP的实现将与db无关——如果可能的话。如果不是,底层数据库是PostgreSQL(v8.4),因此优先 正在查询的基础表如下所示: CREATE TABLE treatment_def ( id PRIMARY SERIAL KEY, name VARCHAR(16) NOT NULL ); CREATE TABLE foo_group_
CREATE TABLE treatment_def ( id PRIMARY SERIAL KEY,
name VARCHAR(16) NOT NULL
);
CREATE TABLE foo_group_def ( id PRIMARY SERIAL KEY,
name VARCHAR(16) NOT NULL
);
CREATE TABLE foo ( id PRIMARY SERIAL KEY,
name VARCHAR(16) NOT NULL,
trtmt_id INT REFERENCES treatment_def(id) ON DELETE RESTRICT,
foo_grp_id INT REFERENCES foo_group_def(id) ON DELETE RESTRICT,
is_male BOOLEAN NOT NULL,
cost REAL NOT NULL
);
CREATE FUNCTION somefunc()
RETURNS TABLE (
treatment_name varchar(16)
, foo_group_name varchar(16)
, averaged_cost double precision)
AS
$BODY$
SELECT t.name -- AS treatment_name
, g.name -- AS group_name
, CASE WHEN f.is_male THEN sum(f.cost)
ELSE avg(f.cost) END -- AS averaged_cost
FROM foo f
JOIN treatment_def t ON t.id = f.trtmt_id
JOIN foo_group_def g ON g.id = f.foo_grp_id
GROUP BY 1, 2, f.is_male;
$BODY$ LANGUAGE sql;
我想编写一个返回以下“表”结果集的SP:
治疗名称、食物组名称、平均成本
如果平均成本的计算方式不同,取决于行字段*是男性*标志设置为真还是假
出于这个问题的目的,假设如果is_-male标志设置为true,则平均成本计算为分组成本值的和,如果is_-male标志设置为false,则成本值计算为分组成本值的平均
(显然)数据是按trmt_id、foo_grp_id(是否为男性?)分组的
如果在is_-male标志上没有条件测试,我大致了解了如何编写SQL。但是,在编写上面定义的SP时,我需要一些帮助
这是我的第一次尝试:
CREATE TYPE FOO_RESULT AS (treatment_name VARCHAR(16), foo_group_name VARCHAR(64), averaged_cost DOUBLE);
// Outline plpgsql (Pseudo code)
CREATE FUNCTION somefunc() RETURNS SETOF FOO_RESULT AS $$
BEGIN
RETURN QUERY SELECT t.name treatment_name, g.name group_name, averaged_cost FROM foo f
INNER JOIN treatment_def t ON t.id = f.trtmt_id
INNER JOIN foo_group_def g ON g.id = f.foo_grp_id
GROUP BY f.trtmt_id, f.foo_grp_id;
END;
$$ LANGUAGE plpgsql;
关于如何正确编写此SP以实现“结果”列中的条件计算,我将不胜感激。您应该能够使用
CASE
语句:
SELECT t.name treatment_name, g.name group_name,
CASE is_male WHEN true then SUM(cost)
ELSE AVG(cost) END AS averaged_cost
FROM foo f
INNER JOIN treatment_def t ON t.id = f.trtmt_id
INNER JOIN foo_group_def g ON g.id = f.foo_grp_id
GROUP BY 1, 2, f.is_male;
我不熟悉PLpgSQL
,因此我不确定BOOLEAN
列的确切语法,但以上内容至少可以让您从正确的方向开始。可能如下所示:
CREATE TABLE treatment_def ( id PRIMARY SERIAL KEY,
name VARCHAR(16) NOT NULL
);
CREATE TABLE foo_group_def ( id PRIMARY SERIAL KEY,
name VARCHAR(16) NOT NULL
);
CREATE TABLE foo ( id PRIMARY SERIAL KEY,
name VARCHAR(16) NOT NULL,
trtmt_id INT REFERENCES treatment_def(id) ON DELETE RESTRICT,
foo_grp_id INT REFERENCES foo_group_def(id) ON DELETE RESTRICT,
is_male BOOLEAN NOT NULL,
cost REAL NOT NULL
);
CREATE FUNCTION somefunc()
RETURNS TABLE (
treatment_name varchar(16)
, foo_group_name varchar(16)
, averaged_cost double precision)
AS
$BODY$
SELECT t.name -- AS treatment_name
, g.name -- AS group_name
, CASE WHEN f.is_male THEN sum(f.cost)
ELSE avg(f.cost) END -- AS averaged_cost
FROM foo f
JOIN treatment_def t ON t.id = f.trtmt_id
JOIN foo_group_def g ON g.id = f.foo_grp_id
GROUP BY 1, 2, f.is_male;
$BODY$ LANGUAGE sql;
要点
- 我使用了
函数,而不是sql
。你可以使用任何一个,我只是为了缩短代码plpgsql
可能会稍微快一点,因为查询计划是缓存的plpgsql
- 我跳过了自定义复合类型。你可以用更简单的方法
- 我通常建议使用而不是
。让你的生活更轻松varchar(n)
- 注意不要在函数体中使用没有表限定符(
)的返回参数的名称,否则会产生命名冲突。这就是为什么我对别名进行了注释tbl.col
- 我根据子句调整了
分组。原来的不起作用。(@Ken的答案中的答案也是如此。)