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
Post a Comment