使用动态目标列进行查询

Query with dynamic target columns

我正在尝试在 Postgres 中构建一个使用 3 tables 数据的视图。我不确定这是否可行,并且在 google 上搜索了一下,但没有得出任何结论。这就是我正在尝试做的事情:

我有 table 个项目名称 - 假设有 5 个项目:

fruits

id | name
1  | banana
2  | orange
3  | pear
4  | apple
5  | grape

然后我有一个人的名单

people

id  |  name
1   |  Joe Blow
2   |  Sally Smith
3   |  John Jones
4   |  Sam Benny
5   |  Nick Stevens
6   |  Peter Sandwitch
7   |  Sarah Morgan

然后我有第三个 table 链接上面的两个:

people_fruits

person_id | fruit_id
1         | 1
1         | 2
1         | 3
1         | 4
2         | 1
2         | 3
3         | 5
6         | 3
7         | 3
7         | 4

我想做的是能够利用上面的内容动态创建一个视图,该视图将根据水果的内容更改列 table。例如,我希望视图按如下方式显示上述数据:

my_fruity_view

name            | bananna | orange | pear | apple | grape
Joe Blow        | X       | X      | X    | X     |
Sally Smith     | X       |        | X    |       |
John Jones      |         |        |      |       | X
Sam Benny       |         |        |      |       |
Nick Stevens    |         |        |      |       |
Peter Sandwitch |         |        | X    |       |
Sarah Morgan    |         |        | X    | X     |

然后如果我以后要添加水果芒果,下一次查询是 运行(没有修改),它会将其添加为一列:

my_fruity_view

name            | bananna | orange | pear | apple | grape | mango
Joe Blow        | X       | X      | X    | X     |       |
Sally Smith     | X       |        | X    |       |       |
John Jones      |         |        |      |       | X     |
Sam Benny       |         |        |      |       |       |
Nick Stevens    |         |        |      |       |       |
Peter Sandwitch |         |        | X    |       |       |
Sarah Morgan    |         |        | X    | X     |       |

这样的查询可以吗?我在堆栈溢出时看到了一些这样的事情——但它似乎是在每列的基础上完成的——但我的数据需要是动态的。

我可以通过编程实现这一点,但我更愿意将其打包到一个视图中以保持整洁。如有任何帮助,我们将不胜感激。

基本上您需要一个 数据透视表 table 或 交叉表 。附加模块 tablefunc 提供了您需要的功能。如果您不熟悉它,请先阅读此内容:

  • PostgreSQL Crosstab Query

你的案例的特殊困难:你首先需要一个连接 tables 的查询来产生正确的输入:

SELECT p.name, f.name, text 'x' AS marker -- required, logically redundant column
FROM   people             p
LEFT   JOIN people_fruits pf ON pf.person_id = p.id  -- LEFT JOIN !
LEFT   JOIN fruits        f  ON f.id = pf.fruit_id
ORDER  BY p.id, f.id;  -- seems to be the desired sort order

LEFT [OUTER] JOIN,不失人无果

在带有 两个 参数的 crosstab() 函数中使用它,如下所示:

SELECT * FROM crosstab(
    $$SELECT p.name, f.name, text 'x'
      FROM   people             p
      LEFT   JOIN people_fruits pf ON pf.person_id = p.id
      LEFT   JOIN fruits        f  ON f.id = pf.fruit_id
      ORDER  BY p.id$$   
   ,$$VALUES ('bananna'), ('orange'), ('pear'), ('apple'), ('grape')$$)
AS ct (name text, bananna text, orange text, pear text, apple text, grape text);

目标列列表中的水果顺序必须与第二个参数中的水果顺序相匹配(在您的情况下按 id 排序)。

缺少的水果获得 NULL 值。

但是,这还不是动态。 SQL 绝对不可能完全动态,这需要在调用时知道结果列。无论哪种方式,您都需要两次往返数据库服务器。您可以让 Postgres 动态构建交叉表查询,然后在下一步中执行它。

带有代码示例的相关答案:

  • Dynamic alternative to pivot with CASE and GROUP BY

替代 将是 return 数组或文档类型(jsonxml、...)包含一个动态元素列表。