Mysql 如何优化包含联接和子查询的查询

Mysql 如何优化包含联接和子查询的查询,mysql,sql,join,optimization,subquery,Mysql,Sql,Join,Optimization,Subquery,我继承了下面的查询和数据库结构,我想优化它,因为它很慢。它包含连接和子查询,我读到这不是一个好计划。我尝试了各种方法来改进它,但是我被卡住了/迷路了 如果它是好的,因为它是那么好,但如果有改进它的建议,我将不胜感激 该查询从各种表格中提取数据,生成一份报告,说明供应商网站的点击次数、供应商的电话号码以及发送给供应商的电子邮件 WHERE子句使用1=1作为有时添加的条件,以按地区、县和供应商的业务类型筛选报告 代码是从mysql\u slow log复制的,用于插入所有$variables。表的结

我继承了下面的查询和数据库结构,我想优化它,因为它很慢。它包含连接和子查询,我读到这不是一个好计划。我尝试了各种方法来改进它,但是我被卡住了/迷路了

如果它是好的,因为它是那么好,但如果有改进它的建议,我将不胜感激

该查询从各种表格中提取数据,生成一份报告,说明供应商网站的点击次数、供应商的电话号码以及发送给供应商的电子邮件

WHERE子句使用1=1作为有时添加的条件,以按地区、县和供应商的业务类型筛选报告

代码是从mysql\u slow log复制的,用于插入所有$variables。表的结构是从mysql转储输出的

查询:

表格结构:

解释计划。
您的查询有6个相关子查询,总共返回444行。对于每个返回的行,这些相关子查询中的每一个都被有效地执行。因此,您的单个查询将产生不到3000个查询

就个人而言,我更喜欢避免使用大型联接或对子查询进行联接。但是,它取决于返回的行数

此外,您还直接连接到正在执行左连接的表,这将生成大量重复项,然后该组将其排除。由于您没有直接从这些表中获取任何内容,而且GROUP BY位于看起来是唯一键的位置,因此它似乎与此无关

如果保留相关子查询:-

SELECT Count(Message.id) FROM messages as Message 
WHERE (U.id = Message.from_to OR U.id = Message.user_id)  
AND Message.created BETWEEN '2014-04-01 00:00:00' and '2014-04-30 23:59:59'
此表上没有用于此子查询的有用索引。在检查两个不同的列的U.id时,可以做的事情不多,但是创建一个索引会有所帮助。最好复制此子查询,一次从\u检查到,一次检查用户\u id,然后将结果添加到一起。因为你可以在相关的id字段和日期上有一个索引

此外,您正在对看起来是唯一键的值进行计数,因此该值不应为null

SELECT Count(DISTINCT(MessageUnique.user_id)) FROM messages as MessageUnique 
WHERE (U.id = MessageUnique.from_to OR U.id = MessageUnique.user_id) 
AND (MessageUnique.parent_message_id is null OR MessageUnique.parent_message_id = MessageUnique.id)  
AND MessageUnique.created BETWEEN '2014-04-01 00:00:00' and '2014-04-30 23:59:59'
与上一个子查询相同的问题

SELECT Count(*) FROM business_counties as bc2 
WHERE Business.id = bc2.business_id
这有一个商业id上的密钥,应该可以

SELECT Count(click.id) FROM business_clickthroughs as click 
WHERE Business.id = click.business_id  
AND click.created BETWEEN '2014-04-01 00:00:00' and '2014-04-30 23:59:59'
虽然根据业务id编制索引,但没有涵盖业务id和创建日期的索引,这可能会有所帮助

SELECT Count(*) FROM business_regions as br2 
WHERE Business.id = br2.business_id
这需要在“业务区域”表上的“业务单元id”上建立索引

SELECT count(BusinessReveal.id) as reveal_no FROM business_reveals as BusinessReveal
WHERE 1=1  
AND BusinessReveal.created BETWEEN '2014-04-01 00:00:00' and '2014-04-30 23:59:59' 
AND BusinessReveal.business_id = Business.id
这里的密钥不包括创建日期,只包括业务id

如果您想尝试对子查询进行连接(尽管MySQL在连接子查询方面很差,但效率可能更高),那么类似这样的操作(未经测试):-


请格式化您的查询,将带有一些示例数据的设置放在上面,并在此处发布链接。您有任何表的索引吗?如果您没有不必要地加入,那么加入并不坏。@mkross1983似乎是这样。OP已经发布了完整的表定义。kickstart非常感谢。单是您的索引建议就加快了搜索速度,在11s内搜索1200万行,这是一个巨大的改进。接下来我将研究您加入子查询的建议。再次感谢……祝你好运。连接子查询可能更有效,因为MySQL只执行每个子查询一次(而不是每个返回的行执行一次),但在连接时,它确实会丢失索引。使用MySQL,更有效的方法会随着数据量的不同而变化(即,如果您有一条主记录,并将其与返回大量记录的子查询的结果连接起来,那么在连接上缺少索引会导致速度变慢)。
SELECT Count(click.id) FROM business_clickthroughs as click 
WHERE Business.id = click.business_id  
AND click.created BETWEEN '2014-04-01 00:00:00' and '2014-04-30 23:59:59'
SELECT Count(*) FROM business_regions as br2 
WHERE Business.id = br2.business_id
SELECT count(BusinessReveal.id) as reveal_no FROM business_reveals as BusinessReveal
WHERE 1=1  
AND BusinessReveal.created BETWEEN '2014-04-01 00:00:00' and '2014-04-30 23:59:59' 
AND BusinessReveal.business_id = Business.id
SELECT Business.*, 
       mess_1.mess_count + mess_2.mess_count as message_no, 
       mess_3.mess_count + mess_4.mess_count as message_unique_no, 
       business1.county_no, 
       click1.clicks, 
       business_regions.region_no, 
       business_reveals1.reveals_no 
FROM businesses as Business 
LEFT JOIN users as U ON Business.id = U.business_id  
LEFT OUTER JOIN
(
    SELECT Message.from_to, Count(Message.id) AS mess_count
    FROM messages as Message 
    WHERE Message.created BETWEEN '2014-04-01 00:00:00' and '2014-04-30 23:59:59'
    GROUP BY  Message.from_to
) AS mess_1
ON U.id = mess_1.from_to
LEFT OUTER JOIN
(
    SELECT Message.user_id, Count(Message.id) AS mess_count
    FROM messages as Message 
    WHERE Message.created BETWEEN '2014-04-01 00:00:00' and '2014-04-30 23:59:59'
    GROUP BY  Message.user_id
) AS mess_2
ON U.id = mess_2.user_id
LEFT OUTER JOIN
( 
    SELECT MessageUnique.from_to, Count(DISTINCT(MessageUnique.user_id))  AS mess_count
    FROM messages as MessageUnique 
    WHERE (MessageUnique.parent_message_id is null OR MessageUnique.parent_message_id = MessageUnique.id)  
    AND MessageUnique.created BETWEEN '2014-04-01 00:00:00' and '2014-04-30 23:59:59'
    GROUP BY  MessageUnique.from_to
) AS mess_3
ON U.id = mess_3.from_to
LEFT OUTER JOIN
( 
    SELECT MessageUnique.user_id, Count(DISTINCT(MessageUnique.user_id))  AS mess_count
    FROM messages as MessageUnique 
    WHERE (MessageUnique.parent_message_id is null OR MessageUnique.parent_message_id = MessageUnique.id)  
    AND MessageUnique.created BETWEEN '2014-04-01 00:00:00' and '2014-04-30 23:59:59'
    GROUP BY  MessageUnique.user_id
) AS mess_4
ON U.id = mess_4.from_to
LEFT OUTER JOIN
( 
    SELECT business_id, Count(*)  AS county_no
    FROM business_counties as bc2 
    GROUP BY  Business.id 
) as business1
ON Business.id = business1.business_id 
LEFT OUTER JOIN
( 
    SELECT click.business_id, Count(click.id) AS clicks
    FROM business_clickthroughs as click 
    WHERE click.created BETWEEN '2014-04-01 00:00:00' and '2014-04-30 23:59:59'
    GROUP BY click.business_id 
) as click1 
ON Business.id = click1.business_id  
LEFT OUTER JOIN
( 
    SELECT br2.business_id, Count(*) AS region_no 
    FROM business_regions as br2 
    WHERE Business.id = br2.business_id 
    GROUP BY br2.business_id 
) as business_regions 
ON Business.id = business_regions.business_id 
LEFT OUTER JOIN
( 
    SELECT BusinessReveal.business_id, count(BusinessReveal.id) as reveal_no 
    FROM business_reveals as BusinessReveal
    WHERE BusinessReveal.created BETWEEN '2014-04-01 00:00:00' and '2014-04-30 23:59:59' 
    GROUP BY BusinessReveal.business_id
) as business_reveals1 
ON business_reveals1.business_id = Business.id