PostgreSQL:将寄存器的值作为多行获取

时间:2015-12-03 05:17:51

标签: sql postgresql pivot row crosstab

使用PostgreSQL 9.3,我正在创建一个Jasper报告模板来制作pdf报告。我想创建具有多个列的不同表的报告,所有列都具有相同的模板。解决方案可以是将register的值作为列名称和每个id的值对。

例如,如果我有一个像这样的表:

ga('require', 'ecommerce')

我想把注册表记在:

id | Column1          | Column2     | Column3
-------------------------------------------------
1  | Register1C1      | Register1C2 | Register1C3

值列的数据类型可能会有所不同! 可能吗?我怎么能这样做?

2 个答案:

答案 0 :(得分:0)

SELECT id
    ,unnest(string_to_array('col1,col2,col3', ',')) col_name
    ,unnest(string_to_array(col1 || ',' || col2 || ',' || col3, ',')) val
FROM t

尝试以下方法:

我的示例表名称为t,以获取n列名称,您可以使用此查询

select string_agg(column_name,',') cols from information_schema.columns where
table_name='t' and column_name<>'id'

此查询将选择除id列以外的表中的所有列。如果要指定架构名称,请在中使用table_schema='your_schema_name' 子句

动态创建选择查询

SELECT 'select id,unnest(string_to_array(''' || cols || ''','','')) col_name,unnest(string_to_array(' || cols1 || ','','')) val from t'
FROM (
        SELECT string_agg(column_name, ',') cols  -- here we'll get all the columns in table t
            ,string_agg(column_name, '||'',''||') cols1 
        FROM information_schema.columns
        WHERE table_name = 't'
            AND column_name <> 'id'
) tb;

使用以下plpgsql函数动态创建SELECT id,unnest(string_to_array('....')) col_name,unnest(string_to_array(.., ',')) val FROM t并执行。

    CREATE OR replace FUNCTION fn ()
RETURNS TABLE (
        id INT
        ,columname TEXT
        ,columnvalues TEXT
        ) AS $$

DECLARE qry TEXT;
BEGIN
    SELECT 'select id,unnest(string_to_array(''' || cols || ''','','')) col_name,unnest(string_to_array(' || cols1 || ','','')) val from t'
    INTO qry
    FROM (
        SELECT string_agg(column_name, ',') cols
            ,string_agg(column_name, '||'',''||') cols1
        FROM information_schema.columns
        WHERE table_name = 't'
            AND column_name <> 'id'
        ) tb;

    RETURN QUERY
    EXECUTE format(qry);
END;$$
LANGUAGE plpgsql

将此功能称为select * from fn()

答案 1 :(得分:0)

如果所有列共享相同的数据类型,则不必强制执行行的顺序:

SELECT t.id, v.*
FROM   tbl t, LATERAL (
   VALUES
     ('col1', col1)
   , ('col2', col2)
   , ('col3', col3)
      -- etc.
   ) v(col, val);

关于LATERAL(需要Postgres 9.3 或更高版本):

将其与VALUES表达式结合使用:

对于不同的数据类型,公分母为text,因为每种类型都可以转换为text。另外,订单强制执行:

SELECT t.id, v.col, v.val
FROM   tbl t, LATERAL (
   VALUES
     (1, 'col1', col1::text)
   , (2, 'col2', col2::text)
   , (3, 'col3', col3::text)
     -- etc.
   ) v(rank, col, val)
ORDER  BY t.id, v.rank;

在Postgres 9.4 或更高版本中,对多个数组使用新的unexst():

SELECT t.id, v.*
FROM   tbl t, unnest('{col1,col2,col3}'::text[]
               , ARRAY[col1,col2,col3]) v(col, val);
           --  , ARRAY[col1::text,col2::text,col3::text]) v(col, val);

不同数据类型的注释替代方案。

Postgres 9.4的完全自动化:

上面的查询可以方便地自动生成一组动态列:

CREATE OR REPLACE FUNCTION f_transpose (_tbl regclass, VARIADIC _cols text[])
  RETURNS TABLE (id int, col text, val text) AS
$func$
BEGIN
   RETURN QUERY EXECUTE format(
     'SELECT t.id, v.* FROM %s t, unnest($1, ARRAY[%s]) v'
   , _tbl, array_to_string(_cols, '::text,') || '::text'))
-- , _tbl, array_to_string(_cols, ',')))  -- simple alternative for only text
   USING _cols;
END
$func$  LANGUAGE plpgsql;

调用 - 使用表名和任意数量的列名,任何数据类型:

SELECT * FROM f_transpose('table_name', 'column1', 'column2', 'column3');

弱点:列名列表对SQL注入是安全的。您可以从pg_attribute收集列名称。例如: