PostgreSQL 动态列数结果

Posted

技术标签:

【中文标题】PostgreSQL 动态列数结果【英文标题】:PostgreSQL Dynamic Number of Column Result 【发布时间】:2018-06-27 06:14:18 【问题描述】:

我有这样的桌子

id   | firstname | lastname | object | total
--------------------------------------------
 1   | John      | Doe      | obj1   | 2
 2   | Mark      | Dew      | obj3   | 1
 3   | Steve     | Foey     | obj1   | 4
 4   | John      | Doe      | obj4   | 2
 5   | Mark      | Dew      | obj2   | 5
 6   | Steve     | Foey     | obj3   | 1
 --  | --        | --       | --     | --
 20  | Mark      | Dew      | obj10  | 1

并期待这样的结果:

id   | name       | obj1 | obj2 | obj3 | obj4 | .. | obj10 |
------------------------------------------------------------
 1   | John Doe   |   2  |   0  |   0  |   2  | .. |   0   |
 2   | Mark Dew   |   0  |   5  |   1  |   0  | .. |   1   |
 3   | Steve Foey |   4  |   0  |   1  |   0  | .. |   0   |

我的问题的最佳解决方案(查询)是什么?我是 sql 新手。请帮我。谢谢。

【问题讨论】:

多次出现的人的价值观呢?它们应该相加吗?还是您想要每个 id 一行,而不是每人一行? @a_horse_with_no_name 是的。它们应该相加。 Sabari 的回答对我有用。但我仍然对交叉表感到好奇(仍然不起作用)。 在这种情况下:您希望在结果中看到哪个 id 值。 Sabari 的答案包括 group by 中的 id,因此,如果一个人对相同的 object 具有相同的 id,您只会看到总数(您的示例数据似乎并非如此)。如果您的 id=42 和 id=45 具有相同的对象,例如约翰·多?那你想看什么? 我删除了 id 和 concat firstnamelastname。我还没有尝试过与具有相同object 的人发生多次。 【参考方案1】:

这是一种新的旋转方式,希望能有所帮助

SELECT FIRSTNAME ,LASTNAME  , CASE WHEN POSITION ( 'OBJ1') IN  STR ) = 0 THEN NULL ELSE  SUBSTR( STR ,POSITION ('OBJ1' IN  STR )+5 ,1) :: INT END  AS OBJ1,
 CASE WHEN POSITION ( 'OBJ2') IN  STR ) = 0 THEN NULL ELSE  SUBSTR( STR ,POSITION ('OBJ2' IN  STR )+5 ,1) :: INT END AS OBJ2,
 CASE WHEN POSITION ( 'OBJ3') IN  STR ) = 0 THEN NULL ELSE  SUBSTR( STR ,POSITION ('OBJ3' IN  STR )+5 ,1) :: INT END AS OBJ3,
 CASE WHEN POSITION ( 'OBJ4') IN  STR ) = 0 THEN NULL ELSE  SUBSTR( STR ,POSITION ('OBJ4' IN  STR )+5 ,1) :: INT END AS OBJ4,
 CASE WHEN POSITION ( 'OBJ5') IN  STR ) = 0 THEN NULL ELSE  SUBSTR( STR ,POSITION ('OBJ5' IN  STR )+5 ,1) :: INT END AS OBJ5,
 CASE WHEN POSITION ( 'OBJ6') IN  STR ) = 0 THEN NULL ELSE  SUBSTR( STR ,POSITION ('OBJ6' IN  STR )+5 ,1) :: INT END AS OBJ6,
 CASE WHEN POSITION ( 'OBJ7') IN  STR ) = 0 THEN NULL ELSE  SUBSTR( STR ,POSITION ('OBJ7' IN  STR )+5 ,1) :: INT END AS OBJ7,
 CASE WHEN POSITION ( 'OBJ8') IN  STR ) = 0 THEN NULL ELSE  SUBSTR( STR ,POSITION ('OBJ8' IN  STR )+5 ,1) :: INT END AS OBJ8,
 CASE WHEN POSITION ( 'OBJ9') IN  STR ) = 0 THEN NULL ELSE  SUBSTR( STR ,POSITION ('OBJ9' IN  STR )+5 ,1) :: INT END AS OBJ9,
 CASE WHEN POSITION ( 'OBJ10') IN  STR ) = 0 THEN NULL ELSE  SUBSTR( STR ,POSITION ('OBJ10' IN  STR )+6 ,1) :: INT END AS OBJ10
 FROM 
( SELECT ID, FIRSTNAME ,LASTNAME  , STRING_AGG(OBJECT :: TEXT || '$' || TOTAL ::TEXT ,',') AS 
FROM YOURTABLE 
WHERE OBJECT 
GROUP BY ID, FIRSTNAME ,LASTNAME )A

【讨论】:

【参考方案2】:

在 postgresql 中,当与 filter 子句一起使用时,pivot 非常简单。

选择 名, 姓, 合并(总和(总)过滤器(其中对象='obj1'),0)作为obj1, 合并(总和(总)过滤器(其中对象='obj2'),0)作为obj2, 合并(总和(总)过滤器(其中对象='obj3'),0)作为obj3, 合并(总和(总)过滤器(其中对象='obj4'),0)作为obj4, 合并(总和(总)过滤器(其中对象='obj5'),0)作为obj5, 合并(总和(总)过滤器(其中对象='obj6'),0)作为obj6, 合并(总和(总)过滤器(其中对象='obj7'),0)作为obj7, 合并(总和(总)过滤器(其中对象='obj8'),0)作为obj8, 合并(总和(总)过滤器(其中对象='obj9'),0)作为obj9, 合并(总和(总)过滤器(其中对象='obj10'),0)作为obj10 从表 1 按名字、姓氏分组

【讨论】:

它的工作!但是如何用0 替换null 值? 我已经编辑了答案并添加了 coalesce()。这将用 0@JimJohnson 替换空值 但是.. 我有一个问题。如果我用新的object 添加新的人怎么办?让我们说'obj100'。如何在不添加where object=obj100 的情况下使该查询工作? 我认为如果您需要“obj100”作为列之一,那么您必须添加“where object=obj100”。否则,您可以将 json 类型用作列,将数据用作 (key,value) = (obj,total) .i.e. 'obj1': 5, 'obj2': 6,...'obj100': 8 for each人。

以上是关于PostgreSQL 动态列数结果的主要内容,如果未能解决你的问题,请参考以下文章

Greenplum/Postgres 8 函数动态结果集?

如何在 Postgresql 交叉表中获取动态列数

Postgresql从具有不同列数的2个表中选择多条记录

选择 Count (distinct col) 查询以显示结果中的行数和列数 - postgresql

如何在函数中返回动态列数?

使用非常大的结果集查询 Postgresql