sql - mysql: select the last 10 messages and for each message the last 3 replies -


for simplicity lets strip down messages table minimum, sample data

message_id  reply_to    createdate 1           0           123 2           0           124 3           0           123 4           1           154 5           1           165 

the reply_to message_id wich message reply to

so im looking sql-statement/procedure/function/other table design lets me select last 10 messages , each of last 3 replies, dont mind changing table structure or keeping sort of record last 3 replies

just selecting last 10 messages is

select * message order createdate limit 10; 

and each of messages replies are

select * message reply_to = :message_id: order createdate limit 3; 

my attempts far are:

  • a triple outer join on message table replies
  • a plain join mysql doesnt allow limits in joins
  • useing having count(distinct reply_to) <= 3, ofcourse having evaluated last

i couldnt either of working

my last option atm have separate table track last 3 replies per message

message_reply: message_id, r_1, r_2, r_3

and updateing table useing triggers new row in message table wich reply updates message_reply table

update message_reply set r_3 = r_2, r_2 = r_1, r_1 = new.reply_to message_id = new.message_id

then query message table records

anyone have better suggestion or working sql statement?

thanks

edit:

added explain results

id  select_type     table   type    possible_keys   key     key_len     ref     rows    1   primary     <derived4>      null    null    null    null    3     1   primary     <derived2>      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>        null    null    null    null    null      2   derived     m       null    null    null    null    299727    3   dependent subquery  r   ref     reply_to,reply_to_2     reply_to_2  4   testv4.m.message_id     29973     

edit 2:

well tried message_reply table method did

build table:

message_reply: message_id, r_1, r_2, r_3 

build trigger:

delimiter | create trigger i_message after insert on message   each row begin     if new.reply_to         insert 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 message_replies (message_id) values (new.message_id);     end if;   end; | delimiter ; 

and select messages:

select m.*,r1.*,r2.*,r3.* 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 

ofcourse trigger preprocessing me fastest way.

tested few more sets of 100k inserts see performance hit trigger took .4 sec longer process 100k rows did without tirgger total time insert 12 sec (on myisam tables)

a working example:

edit - (see revision earlier query)

full table creation , explain plan
note: table "datetable" contains dates 10 years. used generate rows.

drop table if exists messages; create table messages (    message_id int primary key, reply_to int, createdate datetime, index(reply_to));  insert messages  select @n:=@n+1, floor((100000 - @n) / 10), a.thedate (select @n:=0) n cross join datetable cross join datetable b limit 1000000; 

the above generates 1m messages, , valid replies. query:

select m1.message_id, m1.reply_to, m1.createdate, n.n, r.* (     select m.*, (          select group_concat(r.message_id order createdate)           messages r         r.reply_to = m.message_id) replies      messages m      order m.message_id     limit 10 ) m1 inner join ( # union-all query controls how many replies per message     select 1 n union      select 2 union      select 3) n   on (m1.replies null , 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) 

time: 0.078 sec

explain plan

id     select_type         table        type      possible_keys    key      key_len ref                rows    1      primary             <derived4>        (null)            (null)   (null)  (null)             3     1      primary             <derived2>        (null)            (null)   (null)  (null)             10      using 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>      (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 

Comments

Popular posts from this blog

linux - Mailx and Gmail nss config dir -

c# - Is it possible to remove an existing registration from Autofac container builder? -

php - Mysql PK and FK char(36) vs int(10) -