Create dynamic table from function in PostgreSQL

后端 未结 3 1660
死守一世寂寞
死守一世寂寞 2021-01-06 12:14

I have table data,

select * from tbltaxamount ;
 id |   taxname   | taxinfoid | taxvalue | taxamt | zoneid | invoiceid | transid 
----+-------------+--------         


        
3条回答
  •  离开以前
    2021-01-06 13:06

    Your solution is a viable way. I largely rewrote your plpgsql function for simplification / performance / readability / security.

    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;
    

    This works for PostgreSQL 9.1 or later.

    For pg 8.4 or later replace

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

    with:

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

    For versions even older than that create an aggregate function like this:

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

    And then write:

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

    Also:

    DROP TABLE IF EXISTS tmptable;
    

    The clause "IF EXISTS" was introduced with version 8.2.
    If you should use a version even older than that you should you can:

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

    Upgrade!

    Have a look at the versioning policy of the PostgreSQL project. Version 8.0.1 is an especially buggy version. I would strongly advise you to upgrade. If you can't upgrade to a newer major version, at least upgrade to the latest point-release for security reasons, 8.0.26 in your case. This can be done in place, without changing anything else.

提交回复
热议问题