create table account_match
(
id int(11) unsigned auto_increment primary key,
smaller_account_id int(11) unsigned not null,
bigger_account_id int(11) unsigned not null,
match_origin varchar(50) null,
created timestamp default CURRENT_TIMESTAMP not null,
constraint account_match_account_id_fk
foreign key (smaller_account_id) references account (id)
on delete cascade,
constraint account_match_account_id_fk_2
foreign key (bigger_account_id) references account (id)
on delete cascade
create index account_match_smaller_account_id_bigger_account_id_index
on account_match (smaller_account_id, bigger_account_id);
);
create table private_message
(
id int(11) unsigned auto_increment primary key,
sender_account_id int(11) unsigned not null,
receiver_account_id int(11) unsigned null,
message text null,
viewed tinyint(1) default 0 not null,
created timestamp default CURRENT_TIMESTAMP not null,
constraint private_message_account_id_fk
foreign key (sender_account_id) references account (id)
on delete cascade,
constraint private_message_account_id_fk_2
foreign key (receiver_account_id) references account (id)
on delete cascade
);
create index private_message_receiver_account_id_sender_account_id_index
on private_message (receiver_account_id, sender_account_id);
create index private_message_sender_account_id_receiver_account_id_index
on private_message (sender_account_id, receiver_account_id);
SELECT account_match.*
FROM (
SELECT account_match.id
FROM account_match
WHERE id IN (SELECT id
FROM account_match
WHERE smaller_account_id = 1
UNION ALL
SELECT id
FROM account_match
WHERE bigger_account_id = 1)
AND NOT EXISTS(SELECT TRUE
FROM private_message
WHERE sender_account_id = smaller_account_id
AND receiver_account_id = bigger_account_id)
AND NOT EXISTS(SELECT TRUE
FROM private_message
WHERE sender_account_id = bigger_account_id
AND receiver_account_id = smaller_account_id)
) match_ids
JOIN account_match using (id)
ORDER BY id DESC
LIMIT 20
1 PRIMARY account_match index PRIMARY PRIMARY 4 20 100 Backward index scan
1 PRIMARY account_match eq_ref PRIMARY PRIMARY 4 miliar.account_match.id 1 100 Using where
6 DEPENDENT SUBQUERY private_message ref private_message_receiver_account_id_sender_account_id_index,private_message_sender_account_id_receiver_account_id_index private_message_sender_account_id_receiver_account_id_index 9 miliar.account_match.bigger_account_id,miliar.account_match.smaller_account_id 1 100 Using index
5 DEPENDENT SUBQUERY private_message ref private_message_receiver_account_id_sender_account_id_index,private_message_sender_account_id_receiver_account_id_index private_message_sender_account_id_receiver_account_id_index 9 miliar.account_match.smaller_account_id,miliar.account_match.bigger_account_id 1 100 Using index
3 DEPENDENT SUBQUERY account_match eq_ref PRIMARY,account_match_smaller_account_id_bigger_account_id_index PRIMARY 4 func 1 26.57 Using where
4 DEPENDENT UNION account_match eq_ref PRIMARY,account_match_smaller_account_id_bigger_account_id_index,account_match_account_id_fk_2 PRIMARY 4 func 1 5 Using where
查询的重点是获取ID为1的帐户的匹配项(当然,其他时间也将是其他ID),两个用户都不会互相发消息。主查询(子查询)在不到20毫秒的时间内完成,但是一旦我添加了ORDER BY ID DESC,它的时间就会增加到20秒。子查询返回了大约710k行,但是大小无关紧要,因为我试图按主键排序吗?有什么办法可以解决这个速度?
答案 0 :(得分:0)
只需尝试以下操作即可查看(因为我发现您的大多数代码似乎已过时)
以下内容无效
SELECT *
FROM account_match
WHERE (smaller_account_id = 1 or bigger_account_id = 1)
AND EXISTS(SELECT TRUE
FROM private_message
WHERE (sender_account_id != bigger_account_id or receiver_account_id != smaller_account_id)
AND (sender_account_id != smaller_account_id or receiver_account_id != bigger_account_id))
ORDER BY id DESC
LIMIT 20
尝试一下
-- Create a Temp table and get data from the faster query into that.
CREATE TEMPORARY TABLE a_m_temp
SELECT account_match.id
FROM account_match
WHERE id IN (SELECT id
FROM account_match
WHERE smaller_account_id = 1
UNION ALL
SELECT id
FROM account_match
WHERE bigger_account_id = 1)
AND NOT EXISTS(SELECT TRUE
FROM private_message
WHERE sender_account_id = smaller_account_id
AND receiver_account_id = bigger_account_id)
AND NOT EXISTS(SELECT TRUE
FROM private_message
WHERE sender_account_id = bigger_account_id
AND receiver_account_id = smaller_account_id)
-- Inner Join the temp. table to the main
Select a.* from
account_match a
INNER JOIN a_m_temp t
ON a.id = t.id
Order by a.id
Limit 20
答案 1 :(得分:0)
“没关系,因为我正在尝试按主键权限进行排序” –否。优化程序可能会在复杂的WHERE
子句上工作,然后根据以下条件对结果进行排序ORDER BY
。只有使用INDEX
才能完全进行过滤,它才会考虑是否也可以进行排序和LIMIT
。
此外,子查询还隐藏了IN
子句,该子句在逻辑上是无序的。那就是隐藏在“派生”表(FROM
子句中的子查询)中。
在更高版本的MySQL中,优化器将考虑为派生表创建索引。我在您的EXPLAIN
中看不到;将由<auto-key>
表示。
与此同时,我不知道为什么您要有额外的桌子和较小/较大的桌子。
我认为优化将涉及摆脱IN ( SELECT ... )
,也许是这样:
FROM account_match
WHERE id IN (SELECT id
FROM account_match
WHERE smaller_account_id = 1
UNION ALL
SELECT id
FROM account_match
WHERE bigger_account_id = 1)
WHERE NOT EXISTS ( ... )
AND NOT EXISTS ( ... )
->
FROM ( SELECT id
FROM account_match
WHERE smaller_account_id = 1
UNION ALL
SELECT id
FROM account_match
WHERE bigger_account_id = 1 ) AS x
JOIN account_match USING(id)
WHERE NOT EXISTS ( ... )
AND NOT EXISTS ( ... )
此外,account_match
需要一个索引以 bigger_account_id
开头,因为UNION
的两半需要不同的索引(在这种情况下)。
在使用多个表时,请限定(最好使用短别名)所有列名。例如,我需要找出这些表在WHERE sender_account_id = smaller_account_id
中。