如何在生成的表定义未知的情况下生成旋转的 CROSS JOIN?

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)

例如,如果该函数是乘法,我将如何生成如下所示的(乘法)表,

1..12的常用乘法表

所有这些(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**,我得到

ERROR:  a column definition list is required for functions returning "record"
LINE 1: SELECT * FROM crosstab(
Run Code Online (Sandbox Code Playgroud)

作为参考,使用上面的例子用的名字,这是更多的东西像什么tablefunccrosstab()欲望。

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示例中表格的内容和大小进行假设。因此,如果,

  1. 表的长度未定义,
  2. 然后交叉连接表示一个未定义维度的立方体(因为上面的原因),
  3. 类别名称(交叉表用语)在表中

在没有“列定义列表”的情况下,我们在 PostgreSQL 中能做的最好的事情是什么来生成那种表示?

Erw*_*ter 14

简单案例,静态SQL

对于简单情况的非动态解决方案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 个参数,因为根据定义没有缺失值。看:

(您通过在以后的编辑中替换foobar修复了问题中的交叉表查询。这也修复了查询,但继续使用来自 的名称foo。)

未知返回类型,动态 SQL

列名和类型不能是动态的。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