【问题标题】:Postgres Multiple Rows as Single RowPostgres 多行作为单行
【发布时间】:2017-01-04 04:14:47
【问题描述】:

我知道我需要为此使用子查询,但我不知道该怎么做。每个列 ID 有多个条目,但我想将它们显示为单行。这是桌子设计:

UUID | position_id | spot
-----+-------------+-----
111  |    1        | left
112  |    1        | right
113  |    3        | center
114  |    4        | right

我想输出这个数据的方式是这样的:

postion_1_left | position_1_right | postion_3_center | position_4_right
---------------+------------------+------------------+-----------------
     true      |      true        |      true        |      true

这样做的原因是我想将这些数据放入 BIRT 报告中,并且将每个 position_id 和 spot 的绝对值设置为 true 或 false 会使报告更好。报告将如下所示:

             left  | center | right
-----------+-------+--------+-----------
position 1 | yes   |  no    |  yes
position 2 | no    |  no    |  no
position 3 | no    |  yes   |  no
position 4 | no    |  no    |  yes

我想不出更好的方法来做到这一点,所以如果有人有建议,我愿意接受。否则,我将继续使用此布局,但我很难提出查询。我尝试从这样的查询开始:

SELECT (SELECT spot FROM positions_table WHERE position_id = 3 AND spot = 'left')
from positions_table
WHERE uuid = 'afb36733'

但显然那是行不通的。

【问题讨论】:

  • 尝试从select pos.id, bool_or(spot = 'left') as left from generate_series(1,4) as pos(id) left join your_table on (pos.id = position_id) group by pos.id开始

标签: postgresql birt


【解决方案1】:

因为您只是想检查是否有一个给定的spot 来自有限列表 - ('left', 'center', 'right') - 对于每个 position_id,我看到了一个非常简单的解决方案,适用于您的案例,使用 bool_or aggregation function(参见 @987654322 @):

SELECT
    pt.position_id,
    bool_or(pt.spot = 'left') AS left,
    bool_or(pt.spot = 'right') AS right,
    bool_or(pt.spot = 'center') AS center
FROM
    positions_table pt
GROUP BY
    pt.position_id
ORDER BY
    pt.position_id;

结果:

 position_id | left | right | center 
-------------+------+-------+--------
           1 | t    | t     | f
           3 | f    | f     | t
           4 | f    | t     | f
(3 rows)

然后您可以使用 CASE 扩展它以更好地格式化(或在您的表示层中这样做):

SELECT
    pt.position_id,
    CASE WHEN bool_or(pt.spot = 'left') THEN 'yes' ELSE 'no' END AS left,
    CASE WHEN bool_or(pt.spot = 'right') THEN 'yes' ELSE 'no' END AS right,
    CASE WHEN bool_or(pt.spot = 'center') THEN 'yes' ELSE 'no' END AS center
FROM
    positions_table pt
GROUP BY
    pt.position_id
ORDER BY
    pt.position_id;

结果:

 position_id | left | right | center 
-------------+------+-------+--------
           1 | yes  | yes   | no
           3 | no   | no    | yes
           4 | no   | yes   | no
(3 rows)

另一个常见的旋转选项是:

但由于它只是真/假,bool_or 在这里似乎绰绰有余。

【讨论】:

  • 这正是我想要的!谢谢
【解决方案2】:

使用generate_series() 填补position_ids 中的空白并将spots 聚合到id 的数组中:

select 
    id, 
    coalesce('left' = any(arr), false) as left,
    coalesce('center' = any(arr), false) as center,
    coalesce('right' = any(arr), false) as right
from (
    select id, array_agg(spot) arr
    from generate_series(1, 4) id
    left join positions_table on id = position_id
    group by 1
    ) s
order by 1;

 id | left | center | right 
----+------+--------+-------
  1 | t    | f      | t
  2 | f    | f      | f
  3 | f    | t      | f
  4 | f    | f      | t
(4 rows)

【讨论】:

  • 这是一个很好的解决方案。但我对 CASE 陈述的另一个回应是。感谢您的回复。
猜你喜欢
  • 2021-02-17
  • 2018-04-15
  • 2020-01-25
  • 2019-10-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-09
  • 2020-01-06
相关资源
最近更新 更多