获取 multi-participant 个对话以及每个对话的最后一条消息
Fetch multi-participant conversations with last message for each
我正在尝试创建一个简单的聊天应用程序数据库模式,并查询对话。我当前的 table 设置如下:
CREATE TABLE chat_user (
id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
display_name VARCHAR(140),
... other user stuff ...
);
CREATE TABLE conversation (
id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
title VARCHAR(140),
created timestamp with time zone NOT NULL
);
CREATE TABLE conversation_message (
id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
conversation_id bigint NOT NULL,
sender_id bigint NOT NULL,
body TEXT NOT NULL,
created timestamp with time zone NOT NULL
);
CREATE TABLE conversation_participant (
id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
conversation_id bigint NOT NULL,
user_id bigint NOT NULL
);
所以基本上每个对话都有自己的标题和多个参与者。我想获取按对话中最新消息的日期排序的对话(因此首先显示包含最新消息的对话)。结果集应该包含对话的id,标题和参与者列表+最新消息的id,sender_id和body。
还需要获取根据对话的创建日期分页的对话(每页 20 个)
我的 table 设置是否足够有效以满足上述限制条件?在我看来,这可能会导致具有多个子查询的相当大的查询?
这回答了问题的原始版本。
您似乎想要 join
和聚合:
select cm.conversation_id, max(created)
from conversation_message cm join
conversation_participant cp
on cm.conversation_id = cp.conversation_id
where cp.user_id = ?
group by cm.conversation_id
order by max(created) desc;
您可以尝试使用 lateral join。
所以您的查询看起来像这样。
您可以获取所有需要的数据,应用限制和偏移量,并检索每个对话的最后一条消息。希望对你有帮助。
select * from conversation c
left join lateral (
select * from conversation_message cm
where cm.conversation_id=c.id
order by created desc
limit 1
) cm on true
left join conversation_participant cp on cp.id = cm.sender_id;
此处的左连接适用于没有任何消息的聊天室。
要获取对话的最新消息,有多种方法可以实现,例如自连接或 window 函数(row_number()、rank() 等)。使用 window 函数,您可以将查询写为
with cm as (
select *,
rank() over (partition by conversation_id order by created desc) as r
from conversation_message
)
select c.id,
c.title,
cm.body,
cm.created,
cm.r,
cu.display_name
from conversation as c
left join cm on c.id = cm.conversation_id and cm.r <= 1
left join chat_user cu on cu.id = cm.sender_id
在上面的查询中,我使用左联接来包含没有消息的转换,如果您只需要有消息的对话,则使用内部联接。如果每次对话更改需要 1 条以上的最新消息 cm.r <= @no
要获取每个对话的参与者列表,您可以添加新的 CTE,例如
with cm as (
select *,
rank() over (partition by conversation_id order by created desc) as r
from conversation_message
),
message_participants as (
select
m.conversation_id,
array_agg(u.display_name order by m.created desc) as participants
from chat_user as u
join conversation_message as m on u.id = m.sender_id
group by m.conversation_id
)
select c.id,
c.title,
cm.body,
cm.created,
cm.r,
cu.display_name,
cmp.participants
from conversation c
left join cm on c.id = cm.conversation_id and cm.r <= 1
left join chat_user cu on cu.id = cm.sender_id
left join message_participants cmp on c.id = cmp.conversation_id
改进
在conversation
中添加user_id
table标识谁创建了
这段对话。
Table conversation_participant
是多余的,你可以提取
来自 conversation_message
的参与者名单
简而言之: 我认为您对 normalized (3NF)
OLTP 数据库的设计是合理的。这就是您应该瞄准的目标,而不是特定用例的 JOIN
数量。您拥有的设计将满足您定义的用例和许多其他用例,我确信这些用例涉及您的这个应用程序。
详情:
您正在设计一个 OLTP 系统,其中数据保持规范化以确保数据一致性并提高 OLTP 事务的效率。
然而,这意味着您必须做的 JOIN
比 de-normalized 数据库多得多(这对 OLAP、报告、分析系统来说更重要 table)。这就是 OLTP 的本质 relational databases
.
尝试减少规范化数据库中 JOIN
的数量(即 3NF - Third normal form)意味着您将把来自不同粒度的数据合并到相同的 table 并导致重复,从而使更新变得更加困难和缓慢,最终导致数据不一致。
所以,你真的不应该以减少 JOIN
的数量为目标进行设计。相反,请确保您拥有标准化设计并避免 over-normalizing。在某些情况下,您可能希望避免编写长查询,您可以添加 VIEWS
并使用视图来编写查询以简化您的查询(但这有时会导致 sub-optimal 查询性能,因为它会带来不必要的连接)。
我正在尝试创建一个简单的聊天应用程序数据库模式,并查询对话。我当前的 table 设置如下:
CREATE TABLE chat_user (
id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
display_name VARCHAR(140),
... other user stuff ...
);
CREATE TABLE conversation (
id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
title VARCHAR(140),
created timestamp with time zone NOT NULL
);
CREATE TABLE conversation_message (
id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
conversation_id bigint NOT NULL,
sender_id bigint NOT NULL,
body TEXT NOT NULL,
created timestamp with time zone NOT NULL
);
CREATE TABLE conversation_participant (
id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
conversation_id bigint NOT NULL,
user_id bigint NOT NULL
);
所以基本上每个对话都有自己的标题和多个参与者。我想获取按对话中最新消息的日期排序的对话(因此首先显示包含最新消息的对话)。结果集应该包含对话的id,标题和参与者列表+最新消息的id,sender_id和body。
还需要获取根据对话的创建日期分页的对话(每页 20 个)
我的 table 设置是否足够有效以满足上述限制条件?在我看来,这可能会导致具有多个子查询的相当大的查询?
这回答了问题的原始版本。
您似乎想要 join
和聚合:
select cm.conversation_id, max(created)
from conversation_message cm join
conversation_participant cp
on cm.conversation_id = cp.conversation_id
where cp.user_id = ?
group by cm.conversation_id
order by max(created) desc;
您可以尝试使用 lateral join。
所以您的查询看起来像这样。 您可以获取所有需要的数据,应用限制和偏移量,并检索每个对话的最后一条消息。希望对你有帮助。
select * from conversation c
left join lateral (
select * from conversation_message cm
where cm.conversation_id=c.id
order by created desc
limit 1
) cm on true
left join conversation_participant cp on cp.id = cm.sender_id;
此处的左连接适用于没有任何消息的聊天室。
要获取对话的最新消息,有多种方法可以实现,例如自连接或 window 函数(row_number()、rank() 等)。使用 window 函数,您可以将查询写为
with cm as (
select *,
rank() over (partition by conversation_id order by created desc) as r
from conversation_message
)
select c.id,
c.title,
cm.body,
cm.created,
cm.r,
cu.display_name
from conversation as c
left join cm on c.id = cm.conversation_id and cm.r <= 1
left join chat_user cu on cu.id = cm.sender_id
在上面的查询中,我使用左联接来包含没有消息的转换,如果您只需要有消息的对话,则使用内部联接。如果每次对话更改需要 1 条以上的最新消息 cm.r <= @no
要获取每个对话的参与者列表,您可以添加新的 CTE,例如
with cm as (
select *,
rank() over (partition by conversation_id order by created desc) as r
from conversation_message
),
message_participants as (
select
m.conversation_id,
array_agg(u.display_name order by m.created desc) as participants
from chat_user as u
join conversation_message as m on u.id = m.sender_id
group by m.conversation_id
)
select c.id,
c.title,
cm.body,
cm.created,
cm.r,
cu.display_name,
cmp.participants
from conversation c
left join cm on c.id = cm.conversation_id and cm.r <= 1
left join chat_user cu on cu.id = cm.sender_id
left join message_participants cmp on c.id = cmp.conversation_id
改进
在
conversation
中添加user_id
table标识谁创建了 这段对话。Table
的参与者名单conversation_participant
是多余的,你可以提取 来自conversation_message
简而言之: 我认为您对 normalized (3NF)
OLTP 数据库的设计是合理的。这就是您应该瞄准的目标,而不是特定用例的 JOIN
数量。您拥有的设计将满足您定义的用例和许多其他用例,我确信这些用例涉及您的这个应用程序。
详情: 您正在设计一个 OLTP 系统,其中数据保持规范化以确保数据一致性并提高 OLTP 事务的效率。
然而,这意味着您必须做的 JOIN
比 de-normalized 数据库多得多(这对 OLAP、报告、分析系统来说更重要 table)。这就是 OLTP 的本质 relational databases
.
尝试减少规范化数据库中 JOIN
的数量(即 3NF - Third normal form)意味着您将把来自不同粒度的数据合并到相同的 table 并导致重复,从而使更新变得更加困难和缓慢,最终导致数据不一致。
所以,你真的不应该以减少 JOIN
的数量为目标进行设计。相反,请确保您拥有标准化设计并避免 over-normalizing。在某些情况下,您可能希望避免编写长查询,您可以添加 VIEWS
并使用视图来编写查询以简化您的查询(但这有时会导致 sub-optimal 查询性能,因为它会带来不必要的连接)。