[英]How to optimize a MySQL SELECT query with a LEFT JOIN in main query and an INNER JOIN in subquery?
考虑具有三个表: A
, B
和C
以及以下简单查询:
SELECT *
FROM A
LEFT JOIN (
SELECT *
FROM B
INNER JOIN C ON (B.bid = C.bfid)
) s ON (A.bfid = s.bid)
如果子查询选择的行太多,则该查询将是一个拖累,因为MySQL不会对A.bfid = s.bid
使用任何索引。 如果有多个子查询,则这种情况会变得更糟。
有谁知道如何优化这种情况?
这是表结构和示例数据:
CREATE TABLE A (aid INT, bfid INT, PRIMARY KEY (aid));
CREATE TABLE B (bid INT, PRIMARY KEY (bid));
CREATE TABLE C (cid INT, bfid int, PRIMARY KEY (cid));
INSERT INTO B VALUES (1), (2), (3);
INSERT INTO A VALUES (4, 1), (5, 2);
INSERT INTO C VALUES (6, 2), (7, 3);
有了这些,查询将输出:
+-----+------+------+------+------+
| aid | bfid | bid | cid | bfid |
+-----+------+------+------+------+
| 4 | 1 | NULL | NULL | NULL |
| 5 | 2 | 2 | 6 | 2 |
+-----+------+------+------+------+
一种可行的方法是使用一对LEFT联接,然后根据是否在C:-上找到匹配项来确定是否返回B.adid。
SELECT A.*,
IF(C.bfid IS NULL, NULL, B.bid),
IF(C.bfid IS NULL, NULL, C.cid),
IF(C.bfid IS NULL, NULL, C.bfid)
FROM A
LEFT OUTER JOIN B ON A.bfid = B.bid
LEFT OUTER JOIN C ON B.bid = C.bfid
是的,Mysql派生表不使用索引,在大数据库查询中使用索引是一个问题。
可以做的第一件事就是避免这种情况,并使用JOINS从不同的表中收集数据,如果JOIN在索引列上完成,那么它将非常快,并且将忽略全表扫描。 现在,如果完全没有JOIN达到目的,并且您别无选择,只能使用带有删除表的子查询,那么另一种选择是使用TEMPORARY表作为子查询数据并对该TEMPORARY表建立索引。
为了说明这一点,让我们创建表并提供索引,然后不使用TEMPORARY表和使用TEMPORARY表就进行查询解释。
请注意,TEMPORARY表对于当前会话/连接将存在。
create table A (aid int ,val varchar(20));
insert into A values
(1,'A'),(2,'B'),(3,'C'),(4,'D');
create table B (bid int, afid int);
insert into B values
(1,1),(2,1),(3,1),(4,3),(5,2),(6,2),(7,1),(8,2),(9,3),(10,4);
create table C (cid int , bfid int);
insert into C values
(1,1),(2,2),(3,1),(4,8),(5,6),(6,6),(7,10);
alter table A add index aid_idx (aid);
alter table B add index bid_idx (bid);
alter table B add index afid_idx (afid);
alter table C add index cid_idx (cid);
alter table C add index bfid_idx (bfid);
create TEMPORARY TABLE temp_table
select
B.afid from B
inner join C on B.bid = C.bfid ;
alter table temp_table add index dafid_idx(afid);
EXPLAIN SELECT A.*
FROM A
LEFT JOIN (
SELECT B.afid
FROM B
INNER JOIN C ON (B.bid = C.bfid)
) s ON (A.aid = s.afid);
+----+-------------+------------+-------+---------------+----------+---------+------+------+--------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+-------+---------------+----------+---------+------+------+--------------------------------+
| 1 | PRIMARY | A | ALL | NULL | NULL | NULL | NULL | 4 | |
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 7 | |
| 2 | DERIVED | C | index | bfid_idx | bfid_idx | 5 | NULL | 7 | Using index |
| 2 | DERIVED | B | ALL | bid_idx | NULL | NULL | NULL | 10 | Using where; Using join buffer |
+----+-------------+------------+-------+---------------+----------+---------+------+------+--------------------------------+
EXPLAIN
select A.* from A
LEFT JOIN temp_table on temp_table.afid = A.aid;
+----+-------------+------------+------+---------------+-----------+---------+------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+------+---------------+-----------+---------+------------+------+-------------+
| 1 | SIMPLE | A | ALL | NULL | NULL | NULL | NULL | 4 | |
| 1 | SIMPLE | temp_table | ref | dafid_idx | dafid_idx | 5 | test.A.aid | 2 | Using index |
+----+-------------+------------+------+---------------+-----------+---------+------------+------+-------------+
However this query also be pretty good without using derived tables as
select
A.* from A
LEFT JOIN B on B.afid = A.aid
INNER JOIN C on C.bfid = B.bid
因此,根据您要查看的结果集,可以优化查询以避免使用派生表。 如果可以仅通过联接表来收集结果集,则只需在索引列上使用JOIN。
这是我想到的(在网站关闭之前)
SELECT *
FROM A
LEFT JOIN
b on a.aid = b.afid
left join c on b.bid=c.bfid
where not(bfid is null and bid is not null);
我认为它的作用与原件相同。 您需要尝试一下它是否更好。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.