PLpgSQL (или ANSI SQL?) Условный расчет по столбцу - PullRequest
1 голос
/ 16 января 2012

Я хочу написать хранимую процедуру, которая выполняет условный расчет для столбца.В идеале реализация SP должна быть независимой от БД - если это возможно.Если не базовая база данных - это PostgreSQL (v8.4), так что она имеет приоритет.

Запрашиваемые базовые таблицы выглядят так:

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
             );

Я хочу написать SP, который возвращаетследующий набор результатов таблицы:

имя_обработки, имя_группы foo, averaged_cost

, где усредненная стоимость рассчитывается по-разному, в зависимости от того,флаг строки * is_male * установлен в true или false .

Для целей этого вопроса давайте предположим, что если для флага is_male установлено значение true, то усредненная стоимость рассчитывается как SUM значений стоимости для группировки, и еслидля флага is_male установлено значение false, тогда значение стоимости вычисляется как AVERAGE значений стоимости для группировки.

(очевидно) данные группируются по trmt_id, foo_grp_id (и is_male?).

У меня есть приблизительное представление о том, как писать SQL, если не было условного теста для флага is_male.Тем не менее, я мог бы с некоторой помощью написать 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, чтобыреализовать условный расчет в столбце результатов

Ответы [ 2 ]

2 голосов
/ 16 января 2012

Может выглядеть так:

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 может быть немного быстрее, потому что план запроса кэшируется.

  • Я пропустил пользовательский составной тип. Вы можете сделать это проще с RETURNS TABLE.

  • Как правило, я бы рекомендовал использовать тип данных text вместо varchar(n). Облегчает вашу жизнь.

  • Будьте осторожны, чтобы не использовать имена параметра RETURN без указания таблицы (tbl.col) в теле функции, иначе вы создадите конфликт имен. Вот почему я прокомментировал псевдонимы.

  • Я исправил предложение GROUP BY. Оригинал не работал. (Так же как и в ответе Кена.)

1 голос
/ 16 января 2012

Вы должны быть в состоянии использовать оператор 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, но вышесказанное должно, по крайней мере, привести вас в правильном направлении.

...