PostgreSQL:将寄存器的值获取为多行
PostgreSQL: Get values of a register as multiple rows
我正在使用 PostgreSQL 9.3 创建一个 Jasper 报告模板来生成 pdf 报告。我想创建不同 table 的报告,其中包含多个列,并且都使用相同的模板。一种解决方案是将寄存器的值作为列名和每个 id 的值对获取。
例如,如果我有一个 table 像:
id | Column1 | Column2 | Column3
-------------------------------------------------
1 | Register1C1 | Register1C2 | Register1C3
我想注册为:
Id | ColumnName | Value
-----------------------------
1 | Column1 | Register1C1
1 | Column2 | Register1C2
1 | Column3 | Register1C3
值列的数据类型可以变化!
可能吗?我该怎么做?
SELECT id
,unnest(string_to_array('col1,col2,col3', ',')) col_name
,unnest(string_to_array(col1 || ',' || col2 || ',' || col3, ',')) val
FROM t
试试下面的方法:
我的示例 table 名称是 t
,要获取 n
列名称,您可以使用此查询
select string_agg(column_name,',') cols from information_schema.columns where
table_name='t' and column_name<>'id'
此查询将 selects 您的 table 中的所有列,除了 id
column.If 您要指定架构名称然后在 [= 中使用 table_schema='your_schema_name'
33=]where 子句
动态创建select查询
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()
一样调用这个函数
如果您的所有列共享相同的数据类型并且不必强制执行行顺序:
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 或更高版本中对多个数组使用新的 unnest():
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(, 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;
调用 - 使用 table 名称和任意数量的列名称,任何数据类型:
SELECT * FROM f_transpose('table_name', 'column1', 'column2', 'column3');
弱点:列名列表不是安全的SQL注入。您可以改为从 pg_attribute
收集列名。示例:
- How to perform the same aggregation on every column, without listing the columns?
我正在使用 PostgreSQL 9.3 创建一个 Jasper 报告模板来生成 pdf 报告。我想创建不同 table 的报告,其中包含多个列,并且都使用相同的模板。一种解决方案是将寄存器的值作为列名和每个 id 的值对获取。
例如,如果我有一个 table 像:
id | Column1 | Column2 | Column3
-------------------------------------------------
1 | Register1C1 | Register1C2 | Register1C3
我想注册为:
Id | ColumnName | Value
-----------------------------
1 | Column1 | Register1C1
1 | Column2 | Register1C2
1 | Column3 | Register1C3
值列的数据类型可以变化!
可能吗?我该怎么做?
SELECT id
,unnest(string_to_array('col1,col2,col3', ',')) col_name
,unnest(string_to_array(col1 || ',' || col2 || ',' || col3, ',')) val
FROM t
试试下面的方法:
我的示例 table 名称是 t
,要获取 n
列名称,您可以使用此查询
select string_agg(column_name,',') cols from information_schema.columns where
table_name='t' and column_name<>'id'
此查询将 selects 您的 table 中的所有列,除了 id
column.If 您要指定架构名称然后在 [= 中使用 table_schema='your_schema_name'
33=]where 子句
动态创建select查询
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()
如果您的所有列共享相同的数据类型并且不必强制执行行顺序:
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 或更高版本中对多个数组使用新的 unnest():
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(, 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;
调用 - 使用 table 名称和任意数量的列名称,任何数据类型:
SELECT * FROM f_transpose('table_name', 'column1', 'column2', 'column3');
弱点:列名列表不是安全的SQL注入。您可以改为从 pg_attribute
收集列名。示例:
- How to perform the same aggregation on every column, without listing the columns?