Postgres 多行作为单行

Postgres Multiple Rows as Single Row

我知道我需要为此使用子查询,但我不确定如何去做。每个列 ID 有多个条目,但我想将它们显示为一行。这是 table 设计:

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 具有绝对值并确定为真或假会使报告更好。该报告将如下所示:

             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'

但显然那是行不通的。

使用 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)

因为你只是想检查你是否有给定的 spot 有限列表 - ('left', 'center', 'right') - 对于每个 position_id,我看到一个非常简单的解决方案适合你的情况使用 bool_or aggregation function (see also on SQL Fiddle):

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)

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

但因为只有 true/false,bool_or 在这里似乎绰绰有余。