假设如下:
create table my_schema.user(
id serial primary key
);
create table my_schema.conversation(
id serial primary key
);
create table my_schema.conversation_message(
id serial primary key,
conversation_id bigint references my_schema.conversation(id) not null,
from_user_id bigint references my_schema.user(id) not null,
to_user_id bigint references my_schema.user(id) not null,
created_at timestamp not null
);
insert into my_schema.user values (1), (2), (3);
insert into my_schema.conversation values (50), (51);
insert into my_schema.conversation_message values
(100, 50, 1, 3, current_timestamp(0)),
(101, 51, 1, 2, current_timestamp(0)),
(102, 51, 2, 1, current_timestamp(0)),
(103, 51, 2, 1, current_timestamp(0)),
(104, 51, 1, 2, current_timestamp(0));
我需要以下结果:
listing_conversation_id | latest_message
------------------------+------------+-----------------
50 | {"id":100,"created_at":"2023-04-13T19:23:06","to_user_id":3,"from_user_id":1,"conversation_id":50}
51 | {"id":104,"created_at":"2023-04-13T19:23:06","to_user_id":2,"from_user_id":1,"conversation_id":51}
我已经能够使用以下查询检索链接到每个conversation
的所有conversation_message
:
select
c.id as listing_conversation_id,
to_jsonb(array_agg(cm.*)) as messages
-- to_json(
-- select *
-- from my_schema.conversation_message
-- limit 1
-- ) as latest_message
from my_schema.conversation c
join my_schema.conversation_message cm
on cm.conversation_id = c.id
join my_schema.user u1
on u1.id = cm.from_user_id
join my_schema.user u2
on u2.id = cm.to_user_id
where u1.id = 1
or u2.id = 1
group by c.id;
但是latest_message
显然不能是一个数组。
我如何才能做到这一点呢?每当我尝试嵌套查询时,我都会捕获语法错误,就像我在上面的查询注解部分所尝试的那样。另外,这种方法可以吗?或者我应该考虑不同的模型?
Fiddle
2条答案
按热度按时间u5rb5r591#
使用
DISTINCT ON
获取每个会话的 * 最后一条消息 *-顺序在order by
子句中定义。查询(不包括不相关的表)
e5nqia272#
假设
conversation_message.id
被用作新近度的代理,下面给出了指定的结果(这是针对最初标题的帖子,它指定了“两个用户之间的最新消息”):(As Marmite Bomber指出,
my_schema.user
和conversation
的连接是不必要的。它们是基于OP已经包含它们的假设,因为这些表中的其他列将被包含在最终查询中。)DISTINCT ON (least(u1.id, u2.id), greatest(u1.id, u2.id))
是必要的,因为问题的原始版本指定了用户对,而不是对话,并且在相同的两个用户之间可能有多个对话。外部查询按listing_conversation_id
排序,以确保结果的顺序与OP的示例匹配。一般来说,生成的ID列除了标识特定行之外不应具有任何意义。尽管在应用程序的生命周期中不太可能发生这种情况,但序列可能会循环,这样最近创建的行的ID可能小于较旧的行。如果需要合并来自其他源的数据,则使用ID值作为时间的代理也会产生问题。对于
conversation_message
,created_at
将是确定新近度的更好选择。与最后一点相关的是,可以使用
clock_timestamp()
而不是在设置脚本中使用current_timestamp
,这样每个消息都有不同的created_at
值。