【问题标题】:SQL select with dynamic count of "BETWEEN" conditions based on joined table基于连接表的“BETWEEN”条件动态计数的 SQL 选择
【发布时间】:2021-04-15 06:31:15
【问题描述】:

我想在我的宠物项目中添加一个信使,但我在编写数据库查询时遇到了困难。我将 MySQL 用于此服务,并将 Hibernate 作为 ORM。几乎所有查询都是用 HQL 编写的,但原则上我可以使用原生查询。

Messenger 可以包含群组对话。除了写消息,用户还可以进入、离开、清除个人消息历史。用户在对话中看到所有消息,但他也可以清除历史记录,只看到上次清除后的消息。

下面我描述了对这项任务很重要的两个表的简化结构。

消息表:

ID text timestamp
1 first_msg 1609459200
2 second_msg 1609545600

会员事件表:

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 消息的时间戳在所有“进入-离开”周期之间,如果没有离开,则在最后一次“进入”之后,但仅在最后一次历史清除之后。如果存在。

【问题讨论】:

  • 你的 MySQL 版本是什么?
  • @trincot MySQL 的版本是 8.0.20
  • 究竟什么时候进入-离开被读取?如果最后一个事件是 2?好像第 4 行在 member_event 中不存在?那么历史清除不重要?
  • 除非事件类型为 2,否则与在上次活动时及之后阅读所有消息有何不同?

标签: mysql sql join hql between


【解决方案1】:

我不明白,如果没有额外的FOREIGN_KEY,您如何将 Member_event 表与 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)

这至少应该显示加入和清理/离开之间的最后一条消息

【讨论】:

  • 还有很多其他字段,例如 chat_id 等。我为问题简化了表格。
  • 我在选择时犯了一个小错误。刚刚修补它:D
  • 问题是理论上用户可以离开聊天,跳过一定数量的消息,然后重新加入聊天。
  • 很难通过 SQL 检查以前分配的消息。可能使用 WHILE 循环,但我不知道如何正确地做到这一点:)
  • 第 100 万次我确信,有多少编程语言以及开发人员是否知道如何正常使用数据库并不重要。这当然是关于我的。 :-)
【解决方案2】:

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

  1. 取两个表的并集并按时间戳顺序考虑记录
  2. 使用窗口函数来确定最近的 1 或 2 类型是 1。我们可以使用运行总和,其中类型 1 加 1,类型 2 减 1(而 3 对此没有任何作用)。使用另一个窗口函数,您可以确定是否还有类型 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

【讨论】:

  • 这回答了您的问题吗?能否给点意见?
  • 嗨@trincot!我来晚了,但是是的,感谢您的回答和这些窗口功能,我重新设计了我的应用程序的聊天服务。
猜你喜欢
  • 2021-02-20
  • 2020-04-18
  • 2021-08-23
  • 2023-03-30
  • 2019-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多