使用动态目标列进行查询

Posted

技术标签:

【中文标题】使用动态目标列进行查询【英文标题】:Query with dynamic target columns 【发布时间】:2018-05-29 00:22:07 【问题描述】:

我正在尝试在 Postgres 中构建一个使用 3 个数据表的视图。我不确定这是否可能,并且在谷歌上搜索了一下,但没有找到任何结论性的东西。这就是我想要做的:

我有一个项目名称表 - 假设有 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

然后我有第三个表链接上述两个:

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

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

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     |       |

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

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

【问题讨论】:

与其为格式化道歉,不如直接把东西格式化一下? 如何在此处格式化的友好说明:***.com/editing-help 您的问题看起来并没有那么糟糕,但我们已经看到很多更糟了。 谢谢,注意! 【参考方案1】:

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

PostgreSQL Crosstab Query

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

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 动态构建交叉表查询,然后在下一步执行。

代码示例的相关答案:

Execute a dynamic crosstab query Dynamic alternative to pivot with CASE and GROUP BY

替代方法是返回一个数组或文档类型(jsonxml、...),包含元素的动态列表。

【讨论】:

非常感谢这个 Erwin - 这很简单而且内容丰富。而且我可以看到如何按照您所说的进行“两次往返”或返回一些 json 或 xml 将是要走的路。我将尝试通过将列名返回给我的应用程序来实现这一点,按照上面的格式将它们解析为字段名,并运行包含解析后的列名的新查询。 好的 - 我尝试了一下,并提出了一个初始格式查询,根据上面的示例构建两组列名:select string_agg( concat('(''', name, ''')'), ', ' order by name ) as fruit_columns from fruits union select concat( 'name text, ', string_agg( concat(name, ' text'), ', ' order by name ) ) as fruit_columns from fruits 然后将其传递给我的应用程序并使用根据您上面的答案构建完整的查询。从那里我可以得到我所追求的确切格式!

以上是关于使用动态目标列进行查询的主要内容,如果未能解决你的问题,请参考以下文章

查询值和目标字段的数量不同 - C# 脚本任务 SSIS - 使用动态列将 SQL Proc 的结果导出到 Excel

Mysql查询根据两列动态将行转换为列

使用 php 变量创建动态 mysql 查询

Expression表达式树动态查询

使用动态列 Oracle SQL 查询的数据

sql学习系列-行转列问题 动态列展示