Eva*_*oll 19 postgresql pivot dynamic-sql
给定两个带有名称和值的未定义行数的表,我将如何显示CROSS JOIN
函数对其值的透视。
CREATE TEMP TABLE foo AS
SELECT x::text AS name, x::int
FROM generate_series(1,10) AS t(x);
CREATE TEMP TABLE bar AS
SELECT x::text AS name, x::int
FROM generate_series(1,5) AS t(x);
Run Code Online (Sandbox Code Playgroud)
例如,如果该函数是乘法,我将如何生成如下所示的(乘法)表,
所有这些(arg1,arg2,result)
行都可以用
SELECT foo.name AS arg1, bar.name AS arg2, foo.x*bar.x AS result
FROM foo
CROSS JOIN bar;
Run Code Online (Sandbox Code Playgroud)
所以这只是一个演示问题,我希望这也适用于自定义名称——该名称不仅仅是CAST
文本的参数,而是设置在表格中,
CREATE TEMP TABLE foo AS
SELECT chr(x+64) AS name, x::int
FROM generate_series(1,10) AS t(x);
CREATE TEMP TABLE bar AS
SELECT chr(x+72) AS name, x::int
FROM generate_series(1,5) AS t(x);
Run Code Online (Sandbox Code Playgroud)
我认为这可以通过具有动态返回类型的 CROSSTAB 轻松实现。
SELECT * FROM crosstab(
'
SELECT foo.x AS arg1, bar.x AS arg2, foo.x*bar.x
FROM foo
CROSS JOIN bar
', 'SELECT DISTINCT name FROM bar'
) AS **MAGIC**
Run Code Online (Sandbox Code Playgroud)
但是,没有**MAGIC**
,我得到
Run Code Online (Sandbox Code Playgroud)ERROR: a column definition list is required for functions returning "record" LINE 1: SELECT * FROM crosstab(
作为参考,使用上面的例子用的名字,这是更多的东西像什么tablefunc
的crosstab()
欲望。
SELECT * FROM crosstab(
'
SELECT foo.x AS arg1, bar.x AS arg2, foo.x*bar.x
FROM foo
CROSS JOIN bar
'
) AS t(row int, i int, j int, k int, l int, m int);
Run Code Online (Sandbox Code Playgroud)
但是,现在我们回到对bar
示例中表格的内容和大小进行假设。因此,如果,
在没有“列定义列表”的情况下,我们在 PostgreSQL 中能做的最好的事情是什么来生成那种表示?
Erw*_*ter 14
对于简单情况的非动态解决方案crosstab()
:
SELECT * FROM crosstab(
'SELECT b.x, f.name, f.x * b.x AS prod
FROM foo f, bar b
ORDER BY 1, 2'
) AS ct (x int, "A" int, "B" int, "C" int, "D" int, "E" int
, "F" int, "G" int, "H" int, "I" int, "J" int);
Run Code Online (Sandbox Code Playgroud)
我按foo.name
而非 对结果列进行排序foo.x
。两者碰巧并行排序,但这只是简单的设置。为您的案例选择正确的排序顺序。第二列的实际值与此查询无关( 的 1 参数形式crosstab()
)。
我们甚至不需要crosstab()
2 个参数,因为根据定义没有缺失值。看:
(您通过在以后的编辑中替换foo
为bar
修复了问题中的交叉表查询。这也修复了查询,但继续使用来自 的名称foo
。)
列名和类型不能是动态的。SQL 要求在调用时知道结果列的数量、名称和类型。通过显式声明或系统目录中的信息(这就是发生的情况SELECT * FROM tbl
:Postgres 查找注册的表定义。)
您希望 Postgres 从用户表中的数据派生结果列。 不会发生。
无论哪种方式,您都需要两次往返服务器。要么创建一个游标,然后遍历它。或者您创建一个临时表,然后从中进行选择。或者您注册一个类型并在调用中使用它。
或者您只需在一个步骤中生成查询并在下一步中执行它:
SELECT $$SELECT * FROM crosstab(
'SELECT b.x, f.name, f.x * b.x AS prod
FROM foo f, bar b
ORDER BY 1, 2'
) AS ct (x int, $$
|| string_agg(quote_ident(name), ' int, ' ORDER BY name) || ' int)'
FROM foo;
Run Code Online (Sandbox Code Playgroud)
这会动态生成上面的查询。在下一步中执行它。
我使用美元引号 ( $$
) 来保持对嵌套引号的简单处理。看:
quote_ident()
对于转义其他非法列名(并可能防止 SQL 注入)至关重要。
有关的:
Dan*_*ité 11
在没有“列定义列表”的情况下,我们在 PostgreSQL 中能做的最好的事情是什么来生成那种表示?
如果您将此视为呈现问题,则可以考虑使用查询后呈现功能。
较新版本的psql
(9.6) 带有\crosstabview
,在没有 SQL 支持的情况下以交叉表表示形式显示结果(因为 SQL 无法直接生成此结果,如@Erwin 的回答中所述:SQL 要求在调用时知道结果列的数量、名称和类型)
例如,您的第一个查询给出:
SELECT foo.name AS arg1, bar.name AS arg2, foo.x*bar.x AS result
FROM foo
CROSS JOIN bar
\crosstabview
arg1 | 1 | 2 | 3 | 4 | 5
------+----+----+----+----+----
1 | 1 | 2 | 3 | 4 | 5
2 | 2 | 4 | 6 | 8 | 10
3 | 3 | 6 | 9 | 12 | 15
4 | 4 | 8 | 12 | 16 | 20
5 | 5 | 10 | 15 | 20 | 25
6 | 6 | 12 | 18 | 24 | 30
7 | 7 | 14 | 21 | 28 | 35
8 | 8 | 16 | 24 | 32 | 40
9 | 9 | 18 | 27 | 36 | 45
10 | 10 | 20 | 30 | 40 | 50
(10 rows)
Run Code Online (Sandbox Code Playgroud)
带有 ASCII 列名称的第二个示例给出:
SELECT foo.name AS arg1, bar.name AS arg2, foo.x*bar.x
FROM foo
CROSS JOIN bar
\crosstabview
arg1 | I | J | K | L | M
------+----+----+----+----+----
A | 1 | 2 | 3 | 4 | 5
B | 2 | 4 | 6 | 8 | 10
C | 3 | 6 | 9 | 12 | 15
D | 4 | 8 | 12 | 16 | 20
E | 5 | 10 | 15 | 20 | 25
F | 6 | 12 | 18 | 24 | 30
G | 7 | 14 | 21 | 28 | 35
H | 8 | 16 | 24 | 32 | 40
I | 9 | 18 | 27 | 36 | 45
J | 10 | 20 | 30 | 40 | 50
(10 rows)
Run Code Online (Sandbox Code Playgroud)
有关更多信息,请参阅psql 手册和https://wiki.postgresql.org/wiki/Crosstabview。
归档时间: |
|
查看次数: |
6629 次 |
最近记录: |