Database 从PostgreSQL中的函数创建动态表

Database 从PostgreSQL中的函数创建动态表,database,function,postgresql,dynamic,Database,Function,Postgresql,Dynamic,我有表格数据 select * from tbltaxamount ; id | taxname | taxinfoid | taxvalue | taxamt | zoneid | invoiceid | transid ----+-------------+-----------+----------+--------+--------+-----------+--------- 1 | Service Tax | 0 | 0.00 | 28.69

我有表格数据

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
可能是这样的:

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
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
);
根据您实际想要实现的目标,您可能会对可用于创建的目标感兴趣。这是一个例子


如果您坚持使用从数据派生的列名,您将使用与您相同的plpgsql函数或匿名代码块DO语句动态生成查询。

在so may尝试之后,我创建了下面的函数,用于动态创建表,并将显示如上所述的记录

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;

你的解决方案是可行的。为了简化/性能/可读性/安全性,我在很大程度上重写了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或更高版本

对于第8.4页或更高版本,更换

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

对于甚至更早的版本,请创建如下聚合函数:

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
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')
此外:

条款IF EXISTS是在版本8.2中引入的。 如果您应该使用比该版本更旧的版本,您可以:

IF EXISTS (
    SELECT *
    FROM   pg_catalog.pg_class
    WHERE  oid = 'tmptable'::regclass
    AND    relkind = 'r')
THEN
    DROP TABLE tmptable;
END IF;
*/
升级
看一看这张照片。版本8.0.1是一个特别有缺陷的版本。我强烈建议您升级。如果无法升级到较新的主要版本,出于安全原因,请至少升级到最新的point版本,即8.0.26。这可以在不更改任何其他内容的情况下就地完成。

Thanx用于回复,但税名的值可以是动态的,可以是任何文本名称,我们必须根据这一点生成记录only@BhavikAmbani:对于更通用的解决方案,请从tablefunc模块中签出交叉表函数:我使用的是旧版本的Postgresql 8.0.1,它没有交叉表功能,还有一件事我想使此函数或过程更通用,这不仅取决于在特定数据库上,如果我想返回一组记录作为函数的结果,该怎么办?@BhavikAmbani:Pg 8.0与较新版本相比相当有限。从阅读手册开始。找一套。如果需要,请发布一个新问题。请检查上述代码并建议任何最佳解决方案(如果可用)。出色的工作,非常感谢,请向我提供使用较少内置函数且更通用于任何数据库的过程或函数。上述解决方案在我的Postgresql 8.0.1中不起作用。这给出了以下错误消息。代码错误:找不到数据类型记录上下文的数组类型:SQL语句选择“CREATE TABLE tmptable invoiceid integer PRIMARY KEY”,| | SELECT col | | SELECT col | |“numeric9,2默认值0”,,“FROM SELECT LOWEREREREPLACETAXNAME,”,“|”作为tbltaxamount组中的列,按1个顺序乘以1 x | | PL/pgSQL函数f_taxamunit行12执行statement@BhavikAmbani:你可能一开始就提到了你的过时版本,为我们省去了一些麻烦。请看我修改后的答案。@BhavikAmbani:很抱歉听到这个消息。我已经在这里冒险了。PostgreSQL 8.0太老了,祝你好运!