Создать динамическую таблицу из функции в PostgreSQL - PullRequest
2 голосов
/ 12 января 2012

У меня есть данные таблицы,

select * from tbltaxamount ;
 id |   taxname   | taxinfoid | taxvalue | taxamt | zoneid | invoiceid | transid 
----+-------------+-----------+----------+--------+--------+-----------+---------
  1 | Service Tax |         0 |     0.00 |  28.69 |      2 |       119 |      -1
  2 | ABC Tax     |         0 |     0.00 |  25.78 |      2 |       119 |      -1

Теперь, как я могу получить результат, как показано ниже, используя любую функцию PostgreSQL?

invoiceid | Service Tax | ABC Tax
----------+-------------+--------
      119 |       28.69 |  25.78

Ответы [ 3 ]

5 голосов
/ 13 января 2012

Ваше решение является жизнеспособным способом. Я в значительной степени переписал вашу функцию plpgsql для упрощения / производительности / читаемости / безопасности.

CREATE OR REPLACE FUNCTION f_taxamount()
 RETURNS void AS
$BODY$
DECLARE
    rec record;
BEGIN

    DROP TABLE IF EXISTS tmptable;

    EXECUTE 'CREATE TABLE tmptable (invoiceid integer PRIMARY KEY, '
        || (
           SELECT string_agg(col || ' numeric(9,2) DEFAULT 0', ', ')
           FROM  (
              SELECT quote_ident(lower(replace(taxname,' ','_'))) AS col
              FROM   tbltaxamount
              GROUP  BY 1
              ORDER  BY 1
              ) x
           )
        || ')';

    EXECUTE '
        INSERT INTO tmptable (invoiceid)
        SELECT DISTINCT invoiceid FROM tbltaxamount';

    FOR rec IN
        SELECT taxname, taxamt, invoiceid FROM tbltaxamount ORDER BY invoiceid
    LOOP
        EXECUTE '
            UPDATE tmptable
            SET ' || quote_ident(lower(replace(rec.taxname,' ','_')))
                  || ' = '|| rec.taxamt || ' 
            WHERE invoiceid = ' || rec.invoiceid;
    END LOOP;

END;
$BODY$ LANGUAGE plpgsql;

Это работает для PostgreSQL 9.1 или новее.

Для pg 8.4 или новее заменить

SELECT string_agg(col || ' numeric(9,2) DEFAULT 0', ', ')

с:

SELECT array_to_string(array_agg(col || ' numeric(9,2) DEFAULT 0'), ', ')

Для версий даже более старых , чем те, которые создают агрегатную функцию, например:

CREATE OR REPLACE FUNCTION f_concat_comma(text, text)
  RETURNS text AS
$BODY$
BEGIN
RETURN ($1 || ', '::text) || $2;
END;
$BODY$
  LANGUAGE plpgsql IMMUTABLE;

CREATE AGGREGATE concat_comma(text) (
  SFUNC=f_concat_comma,
  STYPE=text
);

А потом напишите:

SELECT concat_comma(col || ' numeric(9,2) DEFAULT 0')

Также:

DROP TABLE IF EXISTS tmptable;

Пункт "ЕСЛИ СУЩЕСТВУЕТ" был введен с версией 8.2 .
Если вам нужна версия , даже старше , чем вы можете:

IF EXISTS (
    SELECT *
    FROM   pg_catalog.pg_class
    WHERE  oid = 'tmptable'::regclass
    AND    relkind = 'r')
THEN
    DROP TABLE tmptable;
END IF;
*/

Обновление!

Ознакомьтесь с политикой управления версиями проекта PostgreSQL . Версия 8.0.1 является особенно глючной. Я бы настоятельно посоветовал вам обновить. Если вы не можете перейти на более новую основную версию, по крайней мере, из соображений безопасности обновите ее до последней версии, 8.0.26. Это можно сделать на месте, ничего не меняя.

3 голосов
/ 13 января 2012

После стольких попыток я создал нижеприведенную функцию для создания таблицы на лету, которая будет отображать записи, как указано выше.

CREATE OR REPLACE FUNCTION taxamount() RETURNS void as $$
DECLARE
        columnNames RECORD;
    invoiceids RECORD;
BEGIN
    FOR columnNames IN  SELECT * from pg_tables where tablename = 'tmptable'
        LOOP
            DROP TABLE tmptable ;        
        END LOOP;
    CREATE TABLE tmptable (invoiceid integer PRIMARY KEY);
    FOR columnNames IN SELECT distinct(replace(taxname,' ','')) as taxnames from tbltaxamount
        LOOP
                EXECUTE 'ALTER TABLE tmptable ADD ' || columnNames.taxnames || ' numeric(9,2) DEFAULT 0';
        END LOOP;
    FOR invoiceids IN SELECT distinct(invoiceid) from tbltaxamount
    LOOP
        EXECUTE 'INSERT INTO tmptable (invoiceid) VALUES (' || invoiceids.invoiceid || ')';
    END LOOP;
    FOR invoiceids IN SELECT * from tbltaxamount
    LOOP
        EXECUTE 'UPDATE tmptable SET ' || replace(invoiceids.taxname,' ','') || ' = ' || invoiceids.taxamt  || ' WHERE invoiceid = ' || invoiceids.invoiceid;
    END LOOP ;
RETURN;
END;
$$ LANGUAGE plpgsql;
1 голос
/ 12 января 2012

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

SELECT invoiceid
      ,sum(CASE WHEN taxname = 'Service Tax' THEN taxamt ELSE 0 END) AS "Service Tax"
      ,sum(CASE WHEN taxname = 'ABC Tax'     THEN taxamt ELSE 0 END) AS "ABC Tax"
FROM   tbltaxamount 
GROUP  BY 1

В зависимости от того, чего вы на самом деле хотите достичь, вас может заинтересовать модуль tablefunc , который можно использовать для создания сводных таблиц . Вот пример .

Если вы настаиваете на именах столбцов, полученных из данных, вам придется динамически строить запрос с помощью функции plpgsql, как вы это делали, или с блоком анонимного кода (оператор DO).

...