使用 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 数组进行列表推导的主要内容,如果未能解决你的问题,请参考以下文章