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

之间的最后消息

我认为您可以继续执行以下步骤:

  1. 合并两个表并按时间戳顺序考虑记录
  2. 使用 window 函数来确定最近的 1 或 2 类型是否为 1。我们可以使用 运行 求和,其中类型 1 加一,类型 2 减一(而 3没什么)。使用另一个 window 函数,您可以确定后面是否还有类型 3。当该行属于必须收集的区间时,这两个信息的组合可以转换为 1,否则为 0。
  3. 筛选之前的结果,只获取消息记录,并且只获取计算为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