SQL查询填充/ tmp并需要几分钟才能运行

时间:2015-02-04 04:19:29

标签: mysql

我有一个查询,它不运行大量数据(恕我直言)但需要花费几分钟(5-10)来执行,并在执行时最终填充/ tmp空间(最多需要20GB)。一旦完成,空间就会被释放。

查询如下:

SELECT c.name, count(b.id), c.parent_accounting_reference, o.contract, a.contact_person, a.address_email, a.address_phone, a.address_fax, concat(ifnull(concat(a.description, ', '),''), ifnull(concat(a.apt_unit, ', '),''), ifnull(concat(a.preamble, ', '),''), ifnull(addr_entered,''))                FROM
                booking b
            join visit v on (b.visit_id = v.id)
            join super_booking s on (v.super_booking_id = s.id)
            join customer c on (s.customer_id = c.id)
            join address a on (a.customer_id = c.id)
            join customer_number cn on (cn.customer_numbers_id = c.id)
            join number n on (cn.number_id = n.id)
            join customer_email ce on (ce.customer_emails_id = c.id)
            join email e on (ce.email_id = e.id)
            left join organization o on (o.accounting_reference = c.parent_accounting_reference)
            left join address_type at on (a.type_id = at.id and at.name_key = 'billing')
            where s.company_id = 1
            and v.expected_start_date between '2015-01-01 00:00:00' and '2015-02-01 00:00:00'
            group by s.customer_id
            order by count(b.id) desc

同样的解释计划是:

+----+-------------+-------+--------+--------------------------------------------------------------+---------------------+---------+--------------------------------------+-------+----------------------------------------------+
| id | select_type | table | type   | possible_keys                                                | key                 | key_len | ref                                  | rows  | Extra                                        |
+----+-------------+-------+--------+--------------------------------------------------------------+---------------------+---------+--------------------------------------+-------+----------------------------------------------+
|  1 | SIMPLE      | s     | ref    | PRIMARY,FKC4F8739580E01B03,FKC4F8739597AD73B1                | FKC4F8739580E01B03  | 9       | const                                | 74088 | Using where; Using temporary; Using filesort |
|  1 | SIMPLE      | ce    | ref    | FK864C4FFBAF6458E3,customer_emails_id,customer_emails_id_2   | customer_emails_id  | 9       | id_dev.s.customer_id                 |     1 | Using where                                  |
|  1 | SIMPLE      | cn    | ref    | FK530F62CA30E87991,customer_numbers_id,customer_numbers_id_2 | customer_numbers_id | 9       | id_dev.ce.customer_emails_id         |     1 | Using where                                  |
|  1 | SIMPLE      | c     | eq_ref | PRIMARY                                                      | PRIMARY             | 8       | id_dev.s.customer_id                 |     1 |                                              |
|  1 | SIMPLE      | e     | eq_ref | PRIMARY                                                      | PRIMARY             | 8       | id_dev.ce.email_id                   |     1 | Using index                                  |
|  1 | SIMPLE      | n     | eq_ref | PRIMARY                                                      | PRIMARY             | 8       | id_dev.cn.number_id                  |     1 | Using index                                  |
|  1 | SIMPLE      | v     | ref    | PRIMARY,FK6B04D4BEF4FD9A                                     | FK6B04D4BEF4FD9A    | 8       | id_dev.s.id                          |     1 | Using where                                  |
|  1 | SIMPLE      | b     | ref    | FK3DB0859E1684683                                            | FK3DB0859E1684683   | 8       | id_dev.v.id                          |     1 | Using index                                  |
|  1 | SIMPLE      | o     | ref    | org_acct_reference                                           | org_acct_reference  | 767     | id_dev.c.parent_accounting_reference |     1 |                                              |
|  1 | SIMPLE      | a     | ref    | FKADDRCUST,customer_address_idx                              | FKADDRCUST          | 9       | id_dev.c.id                          |   256 | Using where                                  |
|  1 | SIMPLE      | at    | eq_ref | PRIMARY                                                      | PRIMARY             | 8       | id_dev.a.type_id                     |     1 |                                              |
+----+-------------+-------+--------+--------------------------------------------------------------+---------------------+---------+--------------------------------------+-------+----------------------------------------------+

它似乎正在使用正确的索引等,所以我无法理解为什么大量使用/ tmp和长执行时间。

2 个答案:

答案 0 :(得分:0)

您的查询使用临时表,您可以通过Using temporary;结果中的EXPLAIN注释查看该表。您的MySQL设置可能配置为使用/ tmp存储临时表。

如果您想进一步优化查询,您应该调查为什么需要临时表。最好的方法是逐步简化查询,直到找出导致它的原因。在这种情况下,可能只是需要处理的行数,所以如果你确实需要所有这些数据,你可能也需要临时表。但是不要放弃优化我的帐户;)

顺便说一句,在另一个注释中,您可能希望查看COALESCE以处理NULL值。

答案 1 :(得分:0)

您仍然坚持使用临时表,因为您正在进行聚合查询,然后通过聚合中的一个结果对其进行排序。您的优化目标应该是减少该临时表中的行数和/或列数。

visit.expected_start_date上添加索引。这可以帮助MySQL更快地满足您的查询,尤其是当您的visit表中有许多行位于查询的日期范围之外时。

您似乎正在尝试查找特定日期范围内预订量最多的客户。

因此,让我们从子查询开始,总结数据库中最少量的资料。

        SELECT count(*) booking_count, s.customer_id           
          FROM  visit v
          JOIN  super_booking s ON v.super_booking_id = s.id
          JOIN  booking b ON v.id = b.visit_id
         WHERE  v.expected_start_date <= '2015-01-01 00:00:00'
           AND  v.expected_start_date >  '2015-02-01 00:00:00'
           AND  s.company_id = 1
         GROUP BY s.customer_id 

这会返回有关日期范围和公司ID的预订计数和客户ID列表。这将是非常有效的,特别是如果你在expected_start_date表中的visit上放一个索引

然后,让我们将该子查询加入到提取所需信息的那个子查询中。

 SELECT c.name, booking_count, c.parent_accounting_reference, 
        o.contract, 
        a.contact_person, a.address_email, a.address_phone, a.address_fax, 
        concat(ifnull(concat(a.description, ', '),''), 
               ifnull(concat(a.apt_unit, ', '),''), 
               ifnull(concat(a.preamble, ', '),''), 
               ifnull(addr_entered,'')) 
   FROM (
        SELECT count(*) booking_count, s.customer_id           
          FROM  visit v
          JOIN  super_booking s ON v.super_booking_id = s.id
          JOIN  booking b ON v.id = b.visit_id
         WHERE  v.expected_start_date <= '2015-01-01 00:00:00'
           AND  v.expected_start_date >  '2015-02-01 00:00:00'
           AND  s.company_id = 1
         GROUP BY s.customer_id 
         ) top
   join customer c on top.customer_id = c.id
   join address a on (a.customer_id = c.id)
   join customer_number cn on (cn.customer_numbers_id = c.id)
   join number n on (cn.number_id = n.id)
        join customer_email ce on (ce.customer_emails_id = c.id)
        join email e on (ce.email_id = e.id)
        left join organization o on (o.accounting_reference = c.parent_accounting_reference)
        left join address_type at on (a.type_id = at.id and at.name_key = 'billing')
  order by booking_count DESC

通过减少您需要总结的数据大小,这应该可以加快您的工作速度。

注意:注意date BETWEEN this AND that中的陷阱。你真的想要

     date >= this
 AND date < that

因为BETWEEN表示

    date >= this
AND date <= that