MySQL查询速度非常慢

时间:2011-09-16 01:51:53

标签: mysql sql join query-optimization

我很乐意对此提出任何建议 - 无论是重写查询,还是以不同方式设置表格。

我基本上有三个表 - 产品表,位置表和条件表。位置表存储有关位置的所有信息,与条件相同。这个大规模查询的诀窍是只用最新的条件和位置来挑选产品。

我从这个问题中得出了一般性的想法:MySQL MIN/MAX returning proper value, but not the related record info

答案只是将当前位置和条件存储在主产品表中,并保留这些历史记录表,但不使用它们进行搜索?我喜欢将它们分开的想法,但当然这个查询需要50秒才能运行,这根本不实用。

SELECT 
'$table' AS tablename, 
$table.id, 
product_name, 
$table.status,
CL.event AS last_event,
CONCAT_WS(' ', CL.location, CL.floor, CL.bin, CL.bay) AS current_loc,
CC.status AS current_cond
FROM $table

LEFT OUTER JOIN
    (SELECT DISTINCT
        C.work_type,
        C.work_id,
        C.status,
        C.inspected_timestamp
        FROM
        (SELECT 
            CONCAT(work_type, work_id) AS condition_id, 
            status,
            MAX(inspected_timestamp) as current
            FROM conditions
            GROUP BY condition_id
        ) XC
    JOIN conditions C
      on CONCAT(C.work_type, C.work_id) = XC.condition_id
      and C.inspected_timestamp = XC.current
    ) CC ON 
    $table.id = CC.work_id AND 
    CC.work_type = '$table'                         

LEFT OUTER JOIN
    (SELECT DISTINCT
        L.work_type,
        L.work_id,
        L.event,
        L.location,
        L.floor,
        L.bin,
        L.bay,
        L.timestamp
        FROM
        (SELECT
            CONCAT(work_type, work_id) AS location_id, 
            location,
            MAX(timestamp) as current
            FROM locations
            GROUP BY location_id
        ) XL
    JOIN locations L
      on CONCAT(L.work_type, L.work_id) = XL.location_id
      and L.timestamp = XL.current
    ) CL ON 
    $table.id = CL.work_id AND 
    CL.work_type = '$table'

HAVING last_event = 'Received'

我在这里添加EXTENDED EXPLAIN的结果。

[0] => Array ( 
    [id] => 1 
    [select_type] => PRIMARY 
    [table] => paintings 
    [type] => ALL 
    [possible_keys] => 
    [key] => 
    [key_len] => 
    [ref] => 
    [rows] => 1159 
    [filtered] => 100.00 
    [Extra] => )

[1] => Array ( 
    [id] => 1 
    [select_type] => PRIMARY 
    [table] => 
    [type] => ALL 
    [possible_keys] => 
    [key] => 
    [key_len] => 
    [ref] => 
    [rows] => 3211 
    [filtered] => 100.00 
    [Extra] => ) 

[2] => Array ( 
    [id] => 1 
    [select_type] => PRIMARY 
    [table] => 
    [type] => ALL 
    [possible_keys] => 
    [key] => 
    [key_len] => 
    [ref] => 
    [rows] => 1870 
    [filtered] => 100.00 
    [Extra] => ) 

[3] => Array ( 
    [id] => 4 
    [select_type] => DERIVED 
    [table] => 
    [type] => ALL 
    [possible_keys] => 
    [key] => 
    [key_len] => 
    [ref] => 
    [rows] => 1868 
    [filtered] => 100.00 
    [Extra] => Using temporary )

[4] => Array ( 
    [id] => 4 
    [select_type] => DERIVED 
    [table] => L 
    [type] => ref 
    [possible_keys] => timestamp 
    [key] => timestamp 
    [key_len] => 8 
    [ref] => XL.current 
    [rows] => 5 
    [filtered] => 100.00 
    [Extra] => Using where ) 

[5] => Array ( 
    [id] => 5 
    [select_type] => DERIVED 
    [table] => locations 
    [type] => ALL 
    [possible_keys] => 
    [key] => 
    [key_len] => 
    [ref] => 
    [rows] => 3913 
    [filtered] => 100.00 
    [Extra] => Using temporary; Using filesort ) 

[6] => Array ( 
    [id] => 2 
    [select_type] => DERIVED 
    [table] => 
    [type] => ALL 
    [possible_keys] => 
    [key] => 
    [key_len] => 
    [ref] => 
    [rows] => 3191 
    [filtered] => 100.00 
    [Extra] => Using temporary ) 

[7] => Array ( 
    [id] => 2 
    [select_type] => DERIVED 
    [table] => C 
    [type] => ref 
    [possible_keys] => inspected_timestamp 
    [key] => inspected_timestamp 
    [key_len] => 8 
    [ref] => XC.current 
    [rows] => 45 
    [filtered] => 100.00 
    [Extra] => Using where ) 

[8] => Array (
     [id] => 3 
    [select_type] => DERIVED 
    [table] => conditions 
    [type] => index 
    [possible_keys] => 
    [key] => work_type_2 
    [key_len] => 316 
    [ref] => 
    [rows] => 3986 
    [filtered] => 100.00 
    [Extra] => Using index; Using temporary; Using filesort )

3 个答案:

答案 0 :(得分:1)

您可以做一些事情:

  1. 查询上的EXPLAIN PLAN。看看那里是否有一个TABLE SCAN。那是杀手。
  2. 查看重新排列查询是否会对EXPLAIN PLAN结果产生影响。提前过滤更多记录将减少所需的时间。
  3. 检查以确保每个WHERE子句中的列都有索引。
  4. 涉及的记录越多,查询的时间就越长。你保留了多少历史?你在说几行?您应该有一个策略可以删除早于保留截止日期的记录,并将它们放在历史记录或报告模式中。
  5. 您是否可以利用触发器或视图来预先计算这些值?

答案 1 :(得分:1)

由于评论期限的限制,我把这个放在答案中。

我查看你的查询已经有一段时间了,我认为它主要是它的本质,以及编写它的方式导致查询需要花费很多时间,但我看不出任何明显的东西错了。

在你做分组的地方,为了得到一个摘要行,然后自己加入那些查询,而我不完全理解你的表或数据的设计,这将是昂贵的,正如解释所示。所以这是桌面扫描。你也是对的,制作临时表并对它们进行分类的成本更高。

因此,在汇总表中预先汇总和访问这些值会有很大帮助,如果这是一个所需的时间是不可接受的。当您查看说明时,请注意行数,因为这应该可以让您了解查询的作用是否合理。

根据定义,最后的having子句不会被优化。如果有办法将其移动到where子句或作为其中一个联接中的条件,那么您有机会显着改进查询计划,但考虑到摘要的成本,它仍然需要一些时间。

此时我唯一可以建议的是将其分解成小块,看看你是否可以优化各个组件然后重新组装。

答案 2 :(得分:0)

正如@gview解释的那样,有很多东西可以帮助这个查询 残酷地 慢。除了他的答案中提到的所有内容之外,还有两个表中使用CONCAT()函数,其中结果稍后用于加入这两个派生表。

如果您只想显示表product的行,只显示location中的最新相关行和condition中的最新相关行,您可以使用以下内容(此只有最新condition的逻辑,您需要另一个类似LEFT JOIN的最新location):

SELECT 
  t.id, 
  t.product_name, 
  t.status,
  cc.status AS current_cond
FROM 
      $table AS t
  LEFT OUTER JOIN
      ( SELECT c.*
        FROM 
              conditions AS c
          JOIN
              ( SELECT 
                  work_id, 
                  MAX(inspected_timestamp) as current_ts
                FROM conditions mc
                WHERE work_type = '$table'
                GROUP BY condition_id
              ) AS mc
            ON  mc.work_id = c.work_id
            AND mc.current_ts = c.inspected_timestamp 
        WHERE c.work_type = '$table'
      ) AS cc  
    ON cc.work_id = t.id