【问题标题】:Select first N messages each user receives选择每个用户收到的前 N ​​条消息
【发布时间】:2016-07-14 20:14:56
【问题描述】:

我有一个表存储发送给用户的消息,布局如下

id (auto-incrementing) | message_id | user_id | datetime_sent

我正在尝试查找每个用户收到的前 N ​​个 message_id,但完全卡住了。我可以在每个用户的基础上轻松完成(在查询中定义用户 ID 时),但不适用于 所有 用户。

注意事项:

  • 许多用户可以获得相同的 message_id
  • 消息 ID 不是按顺序发送的(即我们可以在消息 200 之前发送消息 400)
  • 这是一个只读的 mySQL 数据库

编辑:再三考虑我删除了这个位,但由于有人好心处理它,所以又重新添加了它

最终目标是查看有多少百分比的用户打开了他们收到的前 N ​​条消息中的一条。

打开表如下所示:

user_id | message_id | datetime_opened

【问题讨论】:

  • 为什么不在数据库中插入消息日期的日期时间列?
  • @Santiago time_sent 的列实际上是一个日期时间,而不仅仅是时间。编辑帖子以澄清。此外,如注释中所述,它是一个只读数据库。
  • 您是询问所有用户的百分比,还是只询问至少收到一条消息的用户?
  • @trincot 仅收到至少一条消息的人
  • 为什么要从问题中删除第二个表?我一直在处理这个问题,但现在你的问题似乎改变了!?不再要求消息必须在前 5 次阅读中,等等?哇,我只是在这上面浪费了我的时间:-(

标签: mysql


【解决方案1】:

这是对原始问题的未经测试的答案(前 5 个有 2 个表格和条件):

SELECT DISTINCT user_id
FROM   (   
            SELECT     om.user_id,
                       om.message_id,
                       count(DISTINCT sm2.message_id) messages_before
            FROM       opened_messages om
            INNER JOIN sent_messages sm
                   ON  om.user_id = sm.user_id
                  AND  om.message_id = sm.message_id
            LEFT JOIN  sent_messages sm2
                   ON  om.user_id = sm2.user_id
                  AND  sm2.datetime_sent < sm.datetime_sent
            GROUP BY   om.user_id,
                       om.message_id
            HAVING     messages_before < 5
        ) AS base

子查询加入sm2 以计算发送给同一用户的先前消息的数量,然后having 子句确保发送的先前消息少于5 条。对于同一个用户,可能会有多条消息(最多 5 条)符合该条件,外部查询只列出符合条件的唯一用户。

【讨论】:

  • 太棒了!几乎正是我需要的。谢谢你。迫不及待想进一步研究您的答案。
【解决方案2】:

要获取前 N 条(这里是 2 条)消息,请尝试

SELECT
  user_id
  , message_id
FROM (
  SELECT
    user_id
    , message_id
    , id
    , (CASE WHEN @user_id != user_id THEN @rank := 1 ELSE @rank := @rank + 1 END) AS rank,
      (CASE WHEN @user_id != user_id THEN @user_id := user_id ELSE @user_id END) AS _
  FROM (SELECT * FROM MessageSent ORDER BY user_id, id) T
  JOIN (SELECT @cnt := 0) c
  JOIN (SELECT @user_id := 0) u
) R
WHERE rank < 3
ORDER BY user_id, id
;

它使用 RANK 替代,从@Seaux response 派生到 mysql 是否具有相当于 Oracle 的“分析功能”?

要将其扩展到您的原始问题,只需添加适当的计算:

SELECT
  COUNT(DISTINCT MO.user_id) * 100 /
   (SELECT COUNT(DISTINCT user_id)
    FROM (
      SELECT
        user_id
        , message_id
        , id
        , (CASE WHEN @user_id != user_id THEN @rank := 1 ELSE @rank := @rank + 1 END) AS rank,
          (CASE WHEN @user_id != user_id THEN @user_id := user_id ELSE @user_id END) AS _
      FROM (SELECT * FROM MessageSent ORDER BY user_id, id) T
      JOIN (SELECT @cnt := 0) c
      JOIN (SELECT @user_id := 0) u
    ) R2
    WHERE rank < 3
   ) AS percentage_who_read_one_of_the_first_messages
FROM MessageOpened MO
JOIN
   (SELECT
      user_id
      , message_id
    FROM (
      SELECT
        user_id
        , message_id
        , id
        , (CASE WHEN @user_id != user_id THEN @rank := 1 ELSE @rank := @rank + 1 END) AS rank,
          (CASE WHEN @user_id != user_id THEN @user_id := user_id ELSE @user_id END) AS _
      FROM (SELECT * FROM MessageSent ORDER BY user_id, id) T
      JOIN (SELECT @cnt := 0) c
      JOIN (SELECT @user_id := 0) u
    ) R
    WHERE rank < 3) MR
  ON MO.user_id = MR.user_id
  AND MO.message_id = MR.message_id
;

在 MySQL 中没有 CTE,并且位于只读数据库中 - 我认为无法在语句中两次使用上述查询。

查看实际操作:SQL Fiddle

如果需要调整/进一步详细信息,请发表评论。

【讨论】:

    猜你喜欢
    • 2018-12-28
    • 1970-01-01
    • 2021-06-16
    • 2010-10-09
    • 1970-01-01
    • 2019-01-17
    • 1970-01-01
    • 1970-01-01
    • 2020-02-18
    相关资源
    最近更新 更多