0

我有一张看起来像这样的桌子

CREATE TABLE foo (id, name, category)
AS VALUES
  ( 1, 'name1.1', 'cat1.1.1'),
  ( 1, 'name1.2', 'cat1.1.1'),
  ( 1, 'name1.3', 'cat1.2.1'),
  ( 2, 'name2.1', 'cat2.1.1'),
  ( 2, 'name2.2', 'cat2.1.1'),
  ( 3, 'name3.1', 'cat3.1.1')
;

我试图得到一个看起来像这样的结果,

ID 名称1 名称2 名称3
1 名称1.1 名称1.2 名称1.3
2 名称2.1 名称2.2
3 名称3.1
4

2 回答 2

0

我会将所有名称聚合到一个数组中,然后将数组元素提取为列:

select id, 
       names[1] as name1,
       names[2] as name2,
       names[3] as name3
from (
   select id, 
          array_agg(name order by name) as names
   from foo
   group by id
) t   

如果名称可能包含类似的内容name10.11,那么列的顺序将不是数字,因为 string'10'低于 string '2'。如果您希望订单反映数字,排序会变得更加复杂:

array_agg(name order by string_to_array(replace(name, 'name', ''), '.')::int[]) as names

这将删除name前缀并将数字转换为整数数组,然后正确排序。另一种选择是删除不是数字或点的所有内容:

array_agg(name order by string_to_array(regexp_replace(name, '[^0-9.]', '', 'g'), '.')::int[]) as names
于 2022-01-11T09:08:42.453 回答
0

首先,您需要添加提供此功能的模块。tablefunc

CREATE EXTENSION tablefunc;

现在你需要这样的查询,

SELECT *
FROM crosstab(
    -- here we normalize this into `id | cat | value`
    -- where the category is [1-3]
    $$SELECT id, RIGHT(name,1)::int AS cat, name AS value FROM foo ORDER BY 1,2;$$,
    -- For just the cat 1,2,3
    $$VALUES (1),(2),(3);$$
) AS ct(id text, name1 text, name2 text, name3 text);

这将返回这个,

 id |  name1  |  name2  |  name3  
----+---------+---------+---------
 1  | name1.1 | name1.2 | name1.3
 2  | name2.1 | name2.2 | 
 3  | name3.1 |         | 
(3 rows)

请注意这里的大问题,您的类别实际上是您的数据的函数RIGHT(name,1)::int。那可能是你的坚持。相反,您提供的实际数据category似乎可以完全忽略,除非我遗漏了什么。

另请注意,我在两个地方对类别名称进行了硬编码,

$$VALUES (1),(2),(3);$$

和,

ct(id text, name1 text, name2 text, name3 text);

这是必需的,因为 PostgreSQL 不允许您在运行命令时返回结果集未知的查询。这将仅支持[name1 - name3]如果您希望它真正动态,则程序的范围会扩大很多,并且在这个问题上是题外话。

于 2022-01-11T04:48:44.023 回答