如何使用主查询中的左连接和子查询中的内部连接优化MySQL SELECT查询?

如何使用主查询中的左连接和子查询中的内部连接优化MySQL SELECT查询?,mysql,query-optimization,Mysql,Query Optimization,考虑使用三个表: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、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 |
+-----+------+------+------+------+

是的,Mysql派生表不使用索引,并且在查询 大型数据库是一个问题

可以做的第一件事是避免这种情况,并使用联接从不同的表收集数据,如果联接是在索引列上完成的 然后它将非常快,并将忽略完整的表扫描。现在,如果atall JOIN不起作用,那么您就别无选择 将子查询与已撤销的表一起使用,另一种方法是对子查询数据使用临时表,并对该临时表进行索引

为了说明这一点,让我们创建表并给出索引,并在不使用临时表的情况下完成explain for查询 和临时桌子

请注意,当前会话/连接将存在临时表

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
因此,根据查看的结果集,可以优化查询以避免使用派生表。如果可以使用
只需连接表,然后在索引列上使用连接。

一种可能的方法是使用一对左连接,然后根据是否在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

这是我在网站关闭前想到的

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);

我想它和原作一样。您需要尝试一下,看看是否更好。

如果您有MySQL 5.6+,您可以尝试启用此查询。很抱歉,问题中的示例存在问题,现已修复。是否存在无法简化为此模式的左连接和内部连接的组合?例如,如果a上的每条记录都有B和C上的多个记录组合,则可能会变得混乱。我已经测试了您的解决方案当派生表很大时,但我不确定它与我的查询相比会如何!是的,理想情况下,上述解决方案适用于相对较大的数据量,当我们别无选择,只能使用派生表时。性能主要取决于查询,我想您没有更多的答案来优化当前查询。但问题是每个查询都是不同的,在构建查询之前很难预测某些内容。许多人所做的是识别需要派生表的复杂查询,然后将此技术用于rest use索引和一些查询优化。