mysql:选择最后10条消息,并为每条消息选择最后3条消息

时间:2011-02-23 18:41:51

标签: mysql sql aggregate-functions greatest-n-per-group

为简单起见,我们使用一些示例数据

将消息表删除到最小值
message_id  reply_to    createdate
1           0           123
2           0           124
3           0           123
4           1           154
5           1           165

reply_to是message_id,消息是对

的回复

所以我正在寻找一个sql语句/过程/函数/其他表设计,让我选择最后10条消息,对于每个最后3个回复,我不介意更改表结构甚至保持某种类型最近3个回复的记录

只选择最后10条消息

SELECT * FROM message ORDER BY createdate LIMIT 10;

并且对于每条消息,回复都是

SELECT * FROM message WHERE reply_to = :message_id: ORDER BY createdate LIMIT 3;

到目前为止我的尝试是:

  • 作为回复的消息表上的三重外连接
  • 普通连接,但mysql不允许连接限制
  • 使用HAVING COUNT(DISTINCT reply_to)< = 3,但当前最后评估HAVING

我无法获得其中任何一个工作

我的上一个选项atm是有一个单独的表来跟踪每条消息的最后3个回复

message_reply: message_id, r_1, r_2, r_3

然后更新该表使用触发器 所以消息表中的新行是一个回复更新message_reply表

UPDATE message_reply SET r_3 = r_2, r_2 = r_1, r_1 = NEW.reply_to WHERE message_id = NEW.message_id

然后我可以在消息表中查询这些记录

任何人都有更好的建议,甚至是有效的SQL语句?

感谢

修改

添加了EXPLAIN结果

id  select_type     table   type    possible_keys   key     key_len     ref     rows    Extra
1   PRIMARY     <derived4>  ALL     NULL    NULL    NULL    NULL    3    
1   PRIMARY     <derived2>  ALL     NULL    NULL    NULL    NULL    10  Using where; Using join buffer
1   PRIMARY     r   eq_ref  PRIMARY,message_id,message_id_2     PRIMARY     4   func    1    
4   DERIVED     NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
5   UNION   NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
6   UNION   NULL    NULL    NULL    NULL    NULL    NULL    NULL    No tables used
NULL    UNION RESULT    <union4,5,6>    ALL     NULL    NULL    NULL    NULL    NULL     
2   DERIVED     m   ALL     NULL    NULL    NULL    NULL    299727   
3   DEPENDENT SUBQUERY  r   ref     reply_to,reply_to_2     reply_to_2  4   testv4.m.message_id     29973    

编辑2:

我尝试了message_reply表方法,这也是我做的

构建表格:

message_reply: message_id, r_1, r_2, r_3

构建触发器:

DELIMITER |
CREATE TRIGGER i_message AFTER INSERT ON message
  FOR EACH ROW BEGIN
    IF NEW.reply_to THEN
        INSERT INTO message_replies (message_id, r_1) VALUES (NEW.reply_to, NEW.message_id)
        ON DUPLICATE KEY UPDATE r_3 = r_2, r_2 = r_1, r_1 = NEW.message_id;
    ELSE
        INSERT INTO message_replies (message_id) VALUES (NEW.message_id);
    END IF;
  END;
|
DELIMITER ;

并选择消息:

SELECT m.*,r1.*,r2.*,r3.* FROM message_replies mr
LEFT JOIN message m ON m.message_id = mr.message_id
LEFT JOIN message r1 ON r1.message_id = mr.r_1
LEFT JOIN message r2 ON r2.message_id = mr.r_2
LEFT JOIN message r3 ON r3.message_id = mr.r_3

当然触发器对我进行预处理,这是最快的方式。

使用几组100k插件进行测试,以查看触发器的性能损失 如果没有tirgger,处理100k行需要花费0.4秒的时间 插入的总时间约为12秒(在myIsam表上)

3 个答案:

答案 0 :(得分:1)

一个工作示例:

编辑 - (参见早期查询的修订版)

全表创建和解释计划
注意:表“datetable”只包含大约10年的所有日期。它仅用于生成行。

drop table if exists messages;
create table messages (
   message_id int primary key, reply_to int, createdate datetime, index(reply_to));

insert into messages 
select @n:=@n+1, floor((100000 - @n) / 10), a.thedate
from (select @n:=0) n
cross join datetable a
cross join datetable b
limit 1000000;

以上生成1m消息,以及一些有效的回复。查询:

select m1.message_id, m1.reply_to, m1.createdate, N.N, r.*
from
(
    select m.*, (
         select group_concat(r.message_id order by createdate)
          from messages r
        where r.reply_to = m.message_id) replies
     from messages m
     order by m.message_id
    limit 10
) m1
inner join ( # this union-all query controls how many replies per message
    select 1 N union all
     select 2 union all
     select 3) N
  on (m1.replies is null and N=1) or (N <= length(m1.replies)-length(replace(m1.replies,',','')))
left join messages r
  on r.message_id = substring_index(substring_index(m1.replies, ',', N), ',', -1)

时间:0.078秒

解释计划

id     select_type         table        type      possible_keys    key      key_len ref                rows    Extra
1      PRIMARY             <derived4>   ALL      (NULL)            (NULL)   (NULL)  (NULL)             3    
1      PRIMARY             <derived2>   ALL      (NULL)            (NULL)   (NULL)  (NULL)             10      Using where
1      PRIMARY             r            eq_ref   PRIMARY           PRIMARY  4       func               1    
4      DERIVED             (NULL)       (NULL)   (NULL)            (NULL)   (NULL)  (NULL)             (NULL)  No tables used
5      UNION               (NULL)       (NULL)   (NULL)            (NULL)   (NULL)  (NULL)             (NULL)  No tables used
6      UNION               (NULL)       (NULL)   (NULL)            (NULL)   (NULL)  (NULL)             (NULL)  No tables used
(NULL) UNION RESULT        <union4,5,6> ALL      (NULL)            (NULL)   (NULL)  (NULL)             (NULL)    
2      DERIVED             m            index    (NULL)            PRIMARY  4       (NULL)             1000301    
3      DEPENDENT SUBQUERY  r            ref      reply_to          reply_to 5       test.m.message_id  5       Using where

答案 1 :(得分:0)

我建议您构建额外的表,并根据需要使用尽可能多的步骤。有时为了想象答案,您需要额外的步骤。最后,您可以将SQL编译为一个嵌套语句。

答案 2 :(得分:0)

注意:此答案为OMG评论的比较提供了有用的信息,因此即使需要删除它,请暂时搁置一段时间。

  OMG:检查mysql和“maximum-n-per-group”标签的配对 - 请求很常见。   天啊:然后访问问题并礼貌地告知,如果没有回答。

我按照你的指示OMG,这是我从中得到的 https://stackoverflow.com/questions/tagged/greatest-n-per-group+mysql

  1. SQL - Give me 3 hits for each type only
  2. mySQL Returning the top 5 of each category
  3. MySQL SELECT n records base on GROUP BY
  4. 你可能误解了这个问题,因为从结果的第一页看起来最相似的3(其中2个是我的答案),这些问题涉及整个表的单个维度(每个类别的前n个)。解决方案总是在按类别排序的表格中提供row_number ALL 记录。

    将此问题与针对问题域 top-n-category -> top-m-per-category 的此问题提供的优化答案进行比较,您会发现此问题与其他问题不同。

    不需要visit the questions and courteously inform if not answer,因为

    1. 这些问题的答案有效
    2. 此问题的答案有效