使用 PostgreSQL 数组进行列表推导

Posted

技术标签:

【中文标题】使用 PostgreSQL 数组进行列表推导【英文标题】:List comprehensions with PostgreSQL arrays 【发布时间】:2014-08-26 22:51:14 【问题描述】:

我有一个名为 composite_author 的 PostgreSQL 表,在帖子字段中有一个用户类型的数组。

select * from composite_author

|id |name |posts                                        |
|---|-----|---------------------------------------------|
|1  |john |"(2,1,\"new post\")","(1,1,\"first post\")"|
|2  |edgar|"(3,2,\"hello world\")"|                   |

posts 列中的用户类型有列名 id、author_id、post_text。

我想编写一个查询,该查询生成的结果包含 id 和 name 列,以及一个包含仅表示每个帖子文本的字符串数组的列。理想的输出如下:

|id |name |posts                                        |
|---|-----|---------------------------------------------|
|1  |john |"new post","first post"                    |
|2  |edgar|"hello world"                              |

执行其他类型的操作也很不错,例如返回仅包含帖子 ID 和帖子文本的元组数组,或根据某些标准过滤数组的元素。本质上,我有点喜欢我的选择表现得像 python 中的列表推导,或者 C# 中的简单 linq 语句。

此类查询的语法是什么?

【问题讨论】:

【参考方案1】:

给定用户类型post as

create type post as (
    id int,
    author_id int,
    post_text text
);

还有一个composite_author

create table composite_author (
    id int,
    name text,
    posts post[]
);
insert into composite_author (id, name, posts) values
(1, 'john', '"(2,1,\"new post\")","(1,1,\"first post\")"'),
(2, 'edgar', '"(3,2,\"hello world\")"');

那么基本查询就是

select id, name, (p).id as post_id, (p).author_id, (p).post_text
from (
    select id, name, unnest(posts) as p
    from composite_author
) s;
 id | name  | post_id | author_id |  post_text  
----+-------+---------+-----------+-------------
  1 | john  |       2 |         1 | new post
  1 | john  |       1 |         1 | first post
  2 | edgar |       3 |         2 | hello world

它可以变成一个视图

create view view_composite_author as
select id, name, (p).id as post_id, (p).author_id, (p).post_text
from (
    select id, name, unnest(posts) as p
    from composite_author
) s;

那么基本查询就变得简单多了

select *
from view_composite_author;

字符串数组:

select id, name, array_agg(post_text)
from view_composite_author
group by id, name;
 id | name  |         array_agg         
----+-------+---------------------------
  1 | john  | "new post","first post"
  2 | edgar | "hello world"

包含 post_id 和 post_text 的元组数组

select array_agg((post_id, post_text))
from view_composite_author;
                            array_agg                            
-----------------------------------------------------------------
 "(2,\"new post\")","(1,\"first post\")","(3,\"hello world\")"

过滤

select array_agg((post_id, post_text))
from view_composite_author
where author_id = 1;
                 array_agg                 
-------------------------------------------
 "(2,\"new post\")","(1,\"first post\")"

【讨论】:

【参考方案2】:

如果可能的话,我强烈建议对这些数据进行大量规范化,因为像这样将所有内容混合在一起可能会受到很大限制。

但是,根据当前数据,您可以尝试以下方法:

create table foo
(
  id integer,
  name text,
  posts text[]
);

insert into foo (id, name, posts)
  values 
      (1, 'john',  '"(2,1,\"new post\")", "(1,1,\"first post\")"'),
      (2, 'edgar', '"(3,2,\"hello world\")"');

with indiv_strs AS
(
select id, name, unnest(posts) as post
from foo
)
select id, name, unnest(regexp_matches(post, E'\"(.+)\"')) as filtered_post
from indiv_strs;

这会产生如下输出:

1   john    new post
1   john    first post
2   edgar   hello world

sqlfiddle

此时,由于事情更加规范化,您可以根据需要使用常规查询进行混合和匹配。 (您可以先将上述查询的结果放在temp table 中。)

【讨论】:

以上是关于使用 PostgreSQL 数组进行列表推导的主要内容,如果未能解决你的问题,请参考以下文章

使用列表推导对数据框列表中的数据框进行编号

使用python列表推导式进行99乘法表

python之列表推导和生成器表达式

如何在 PostgreSQL 8.0.2 中将列表转换为数组

python 列表推导式

2.2.2 生成器推导式