我很乐意对此提出任何建议 - 无论是重写查询,还是以不同方式设置表格。
我基本上有三个表 - 产品表,位置表和条件表。位置表存储有关位置的所有信息,与条件相同。这个大规模查询的诀窍是只用最新的条件和位置来挑选产品。
我从这个问题中得出了一般性的想法: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 )
答案 0 :(得分:1)
您可以做一些事情:
答案 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