SQL select 动态计数 "BETWEEN" 条件基于加入 table
SQL select with dynamic count of "BETWEEN" conditions based on joined table
我想在我的宠物项目中添加一个信使,但我在编写数据库查询时遇到困难。我将 MySQL 用于此服务,并将 Hibernate 作为 ORM。几乎所有查询都是用 HQL 编写的,但原则上我可以使用原生查询。
Messenger 可以包含群组对话。除了写消息,用户还可以进入对话、离开对话、清除个人消息历史记录。用户在通话中看到所有消息,但他也可以清除历史记录,只看到最后一次清除后的消息。
下面我描述了两个对这项任务很重要的 table 的简化结构。
留言table:
ID
text
timestamp
1
first_msg
1609459200
2
second_msg
1609545600
Member_event table:
id
user_id
type
timestamp
1
1
1
1609459100
2
1
3
1609459300
3
1
2
1609459400
4
1
1
1609545500
where type:
1 - user entered the chat,
2 - user leaved the chat,
3 - user cleared his own history of messages in the chat
是否可以一次请求读取用户可用的所有聊天消息?
我不知道如何动态检查条件:WHERE 消息的时间戳位于所有“进入-离开”周期之间和最后一次“进入”之后(如果没有离开但仅在最后一次历史记录清除之后)。如果存在。
我不明白,你如何在没有额外的 FOREIGN_KEY
的情况下将 Member_event table 匹配到 Message_table。您是否正在尝试通过时间戳分配用户可用的消息?
如果是这样,试试这个:
SELECT * FROM MESSAGE_TABLE m
WHERE m.TIMESTAMP BETWEEN
(SELECT TOP 1 TIMESTAMP FROM MEMBER_EVENT_TABLE WHERE type = 1 ORDER BY TIMESTAMP DESC)
AND (SELECT TOP 1 TIMESTAMP FROM MEMBER_EVENT_TABLE WHERE type != 1 ORDER BY TIMESTAMP DESC)
这至少应该显示加入和 clean/leave
之间的最后消息
我认为您可以继续执行以下步骤:
- 合并两个表并按时间戳顺序考虑记录
- 使用 window 函数来确定最近的 1 或 2 类型是否为 1。我们可以使用 运行 求和,其中类型 1 加一,类型 2 减一(而 3没什么)。使用另一个 window 函数,您可以确定后面是否还有类型 3。当该行属于必须收集的区间时,这两个信息的组合可以转换为 1,否则为 0。
- 筛选之前的结果,只获取消息记录,并且只获取计算为1的那些。
这里是查询:
with unified as (
select id, text, timestamp, null as type
from message
union
select id, null, timestamp, type
from member_event
where user_id = 1),
validated as (
select unified.*,
sum(case type when 1 then 1 when 2 then -1 else 0 end)
over (order by timestamp
rows unbounded preceding) *
min(case type when 3 then 0 else 1 end)
over (order by timestamp
rows between current row and unbounded following) valid
from unified
order by timestamp)
select id, text, timestamp
from validated
where type is null and valid = 1
order by timestamp
我想在我的宠物项目中添加一个信使,但我在编写数据库查询时遇到困难。我将 MySQL 用于此服务,并将 Hibernate 作为 ORM。几乎所有查询都是用 HQL 编写的,但原则上我可以使用原生查询。
Messenger 可以包含群组对话。除了写消息,用户还可以进入对话、离开对话、清除个人消息历史记录。用户在通话中看到所有消息,但他也可以清除历史记录,只看到最后一次清除后的消息。
下面我描述了两个对这项任务很重要的 table 的简化结构。
留言table:
ID | text | timestamp |
---|---|---|
1 | first_msg | 1609459200 |
2 | second_msg | 1609545600 |
Member_event table:
id | user_id | type | timestamp |
---|---|---|---|
1 | 1 | 1 | 1609459100 |
2 | 1 | 3 | 1609459300 |
3 | 1 | 2 | 1609459400 |
4 | 1 | 1 | 1609545500 |
where type:
1 - user entered the chat,
2 - user leaved the chat,
3 - user cleared his own history of messages in the chat
是否可以一次请求读取用户可用的所有聊天消息?
我不知道如何动态检查条件:WHERE 消息的时间戳位于所有“进入-离开”周期之间和最后一次“进入”之后(如果没有离开但仅在最后一次历史记录清除之后)。如果存在。
我不明白,你如何在没有额外的 FOREIGN_KEY
的情况下将 Member_event table 匹配到 Message_table。您是否正在尝试通过时间戳分配用户可用的消息?
如果是这样,试试这个:
SELECT * FROM MESSAGE_TABLE m
WHERE m.TIMESTAMP BETWEEN
(SELECT TOP 1 TIMESTAMP FROM MEMBER_EVENT_TABLE WHERE type = 1 ORDER BY TIMESTAMP DESC)
AND (SELECT TOP 1 TIMESTAMP FROM MEMBER_EVENT_TABLE WHERE type != 1 ORDER BY TIMESTAMP DESC)
这至少应该显示加入和 clean/leave
之间的最后消息我认为您可以继续执行以下步骤:
- 合并两个表并按时间戳顺序考虑记录
- 使用 window 函数来确定最近的 1 或 2 类型是否为 1。我们可以使用 运行 求和,其中类型 1 加一,类型 2 减一(而 3没什么)。使用另一个 window 函数,您可以确定后面是否还有类型 3。当该行属于必须收集的区间时,这两个信息的组合可以转换为 1,否则为 0。
- 筛选之前的结果,只获取消息记录,并且只获取计算为1的那些。
这里是查询:
with unified as (
select id, text, timestamp, null as type
from message
union
select id, null, timestamp, type
from member_event
where user_id = 1),
validated as (
select unified.*,
sum(case type when 1 then 1 when 2 then -1 else 0 end)
over (order by timestamp
rows unbounded preceding) *
min(case type when 3 then 0 else 1 end)
over (order by timestamp
rows between current row and unbounded following) valid
from unified
order by timestamp)
select id, text, timestamp
from validated
where type is null and valid = 1
order by timestamp