2011-04-15 215 views
6

我拥有的查询是针对库存表的。子查询连接的作用是获取每个库存资产的工作订单总数。如果我使用设备类型,供应商,位置和房间的主要连接运行基本查询,它运行得很好。不到一秒钟即可返回结果。在子查询连接中使用它,需要15到20秒才能返回结果。如何加快我的查询速度。子查询太慢

下面是完整的查询:

SELECT `inventory`.inventory_id AS 'inventory_id', 
     `inventory`.media_tag AS 'media_tag', 
     `inventory`.asset_tag AS 'asset_tag', 
     `inventory`.idea_tag AS 'idea_tag', 
     `equipTypes`.equipment_type AS 'equipment_type', 
     `inventory`.equip_make AS 'equip_make', 
     `inventory`.equip_model AS 'equip_model', 
     `inventory`.equip_serial AS 'equip_serial', 
     `inventory`.sales_order AS 'sales_order', 
     `vendors`.vendor_name AS 'vendor_name', 
     `inventory`.purchase_order AS 'purchase_order', 
     `status`.status AS 'status', 
     `locations`.location_name AS 'location_name', 
     `rooms`.room_number AS 'room_number', 
     `inventory`.notes AS 'notes', 
     `inventory`.send_to AS 'send_to', 
     `inventory`.one_to_one AS 'one_to_one', 
     `enteredBy`.user_name AS 'user_name', 
     from_unixtime(`inventory`.enter_date, '%m/%d/%Y') AS 'enter_date', 
     from_unixtime(`inventory`.modified_date, '%m/%d/%Y') AS 'modified_date', 
     COALESCE(at.assets,0) AS assets 
FROM mod_inventory_data AS `inventory` 
LEFT JOIN mod_inventory_equip_types AS `equipTypes` 
     ON `equipTypes`.equip_type_id = `inventory`.equip_type_id 
LEFT JOIN mod_vendors_main AS `vendors` 
     ON `vendors`.vendor_id = `inventory`.vendor_id 
LEFT JOIN mod_inventory_status AS `status` 
     ON `status`.status_id = `inventory`.status_id 
LEFT JOIN mod_locations_data AS `locations` 
     ON `locations`.location_id = `inventory`.location_id 
LEFT JOIN mod_locations_rooms AS `rooms` 
     ON `rooms`.room_id = `inventory`.room_id 
LEFT JOIN mod_users_data AS `enteredBy` 
     ON `enteredBy`.user_id = `inventory`.entered_by 
LEFT JOIN 
     (SELECT asset_tag, count(*) AS assets 
     FROM mod_workorder_data 
     WHERE asset_tag IS NOT NULL 
     GROUP BY asset_tag) AS at 
     ON at.asset_tag = inventory.asset_tag 
ORDER BY inventory_id ASC LIMIT 0,20 

MySQL的EXPLAIN数据,这是在这里

+----+-------------+--------------------+--------+---------------+-----------+---------+-------------------------------------+-------+---------------------------------+ 
| id | select_type | table    | type | possible_keys | key  | key_len | ref         | rows | Extra       | 
+----+-------------+--------------------+--------+---------------+-----------+---------+-------------------------------------+-------+---------------------------------+ 
| 1 | PRIMARY  | inventory   | ALL | NULL   | NULL  | NULL | NULL        | 12612 | Using temporary; Using filesort | 
| 1 | PRIMARY  | equipTypes   | eq_ref | PRIMARY  | PRIMARY | 4  | spsd_woidbs.inventory.equip_type_id |  1 |         | 
| 1 | PRIMARY  | vendors   | eq_ref | PRIMARY  | PRIMARY | 4  | spsd_woidbs.inventory.vendor_id  |  1 |         | 
| 1 | PRIMARY  | status    | eq_ref | PRIMARY  | PRIMARY | 4  | spsd_woidbs.inventory.status_id  |  1 |         | 
| 1 | PRIMARY  | locations   | eq_ref | PRIMARY  | PRIMARY | 4  | spsd_woidbs.inventory.location_id |  1 |         | 
| 1 | PRIMARY  | rooms    | eq_ref | PRIMARY  | PRIMARY | 4  | spsd_woidbs.inventory.room_id  |  1 |         | 
| 1 | PRIMARY  | enteredBy   | eq_ref | PRIMARY  | PRIMARY | 4  | spsd_woidbs.inventory.entered_by |  1 |         | 
| 1 | PRIMARY  | <derived2>   | ALL | NULL   | NULL  | NULL | NULL        | 4480 |         | 
| 2 | DERIVED  | mod_workorder_data | range | asset_tag  | asset_tag | 13  | NULL        | 15897 | Using where; Using index  | 
+----+-------------+--------------------+--------+---------------+-----------+---------+-------------------------------------+-------+---------------------------------+ 

使用MySQL查询分析我得到这个:

+--------------------------------+------------+ 
| Status       | Time  | 
+--------------------------------+------------+ 
| starting      | 0.000020 | 
| checking query cache for query | 0.000263 | 
| Opening tables     | 0.000034 | 
| System lock     | 0.000013 | 
| Table lock      | 0.000079 | 
| optimizing      | 0.000011 | 
| statistics      | 0.000138 | 
| preparing      | 0.000019 | 
| executing      | 0.000010 | 
| Sorting result     | 0.000004 | 
| Sending data     | 0.015103 | 
| init       | 0.000094 | 
| optimizing      | 0.000009 | 
| statistics      | 0.000049 | 
| preparing      | 0.000022 | 
| Creating tmp table    | 0.000104 | 
| executing      | 0.000009 | 
| Copying to tmp table   | 15.410168 | 
| Sorting result     | 0.009488 | 
| Sending data     | 0.000215 | 
| end       | 0.000006 | 
| removing tmp table    | 0.001997 | 
| end       | 0.000018 | 
| query end      | 0.000005 | 
| freeing items     | 0.000112 | 
| storing result in query cache | 0.000011 | 
| removing tmp table    | 0.000022 | 
| closing tables     | 0.000036 | 
| logging slow query    | 0.000005 | 
| logging slow query    | 0.000005 | 
| cleaning up     | 0.000013 | 
+--------------------------------+------------+ 

这说明我瓶颈正在复制到临时表中,但我不确定如何加快速度。服务器端的设置是否可以配置使其更快?是否有更改现有的查询,我可以做,会产生相同的结果,会更快?

在我看来,LEFT JOIN子查询每次都会给出相同的结果数据矩阵,所以如果它必须为清单列表中的每一行运行该查询,我可以看到它为什么会很慢。还是MySQL在运行时缓存子查询?我以为我读somwhere MySQL不缓存子查询,这是真的吗?

任何帮助表示赞赏。

+0

您正在查询的表中有多少数据?你是否确信你用于连接谓词的所有列都被编入索引? – 2011-04-15 23:51:11

+0

库存数据表有12,612条记录,子查询中的工单数据表有19,159条记录。我确实有所有ID字段和JOIN中使用的asset_tag字段的索引。我的意思是在原帖中指出,但忘记了。我应该把它们全部放在一个指定的索引中吗?我目前有单独的索引。顺便说一句,感谢您重新格式化这篇文章。 – 2011-04-16 01:43:55

+0

嗯。这听起来不像大量的数据,我不认为合并指数会产生很大的促进作用。排查性能问题。问题我实际上是通过拆除查询开始的。一次删除一个连接并对性能进行基准测试。这样做可能会在某处出现问题。也许从删除子查询开始,看看它是如何执行的。如果只有它是罪魁祸首,那么一个选项可能是对数据进行非规范化并创建一个包含这些计数的表。 – 2011-04-16 03:01:44

回答

1

这是我所做的似乎工作很好。我创建了一个名为mod_workorder_counts的表。该表有两个字段,唯一的资产标签,以及是INT和(3)字段的wo_count。我用这个查询填充该表格:

INSERT INTO mod_workorder_counts (asset_tag, wo_count) 
select s.asset_tag, ct 
FROM 
    (SELECT t.asset_tag, count(*) as ct 
    FROM mod_workorder_data t 
    WHERE t.asset_tag IS NOT NULL 
    GROUP BY t.asset_tag 
) as s 
ON DUPLICATE KEY UPDATE mod_workorder_counts.wo_count = ct

它在0.1580秒内执行,这可能会被认为是稍慢但不坏。

现在,当我跑我的原始查询的这个修改:

SELECT `inventory`.inventory_id AS 'inventory_id', 
     `inventory`.media_tag AS 'media_tag', 
     `inventory`.asset_tag AS 'asset_tag', 
     `inventory`.idea_tag AS 'idea_tag', 
     `equipTypes`.equipment_type AS 'equipment_type', 
     `inventory`.equip_make AS 'equip_make', 
     `inventory`.equip_model AS 'equip_model', 
     `inventory`.equip_serial AS 'equip_serial', 
     `inventory`.sales_order AS 'sales_order', 
     `vendors`.vendor_name AS 'vendor_name', 
     `inventory`.purchase_order AS 'purchase_order', 
     `status`.status AS 'status', 
     `locations`.location_name AS 'location_name', 
     `rooms`.room_number AS 'room_number', 
     `inventory`.notes AS 'notes', 
     `inventory`.send_to AS 'send_to', 
     `inventory`.one_to_one AS 'one_to_one', 
     `enteredBy`.user_name AS 'user_name', 
     from_unixtime(`inventory`.enter_date, '%m/%d/%Y') AS 'enter_date', 
     from_unixtime(`inventory`.modified_date, '%m/%d/%Y') AS 'modified_date', 
     COALESCE(at.wo_count, 0) AS workorders 
FROM mod_inventory_data AS `inventory` 
LEFT JOIN mod_inventory_equip_types AS `equipTypes` 
     ON `equipTypes`.equip_type_id = `inventory`.equip_type_id 
LEFT JOIN mod_vendors_main AS `vendors` 
     ON `vendors`.vendor_id = `inventory`.vendor_id 
LEFT JOIN mod_inventory_status AS `status` 
     ON `status`.status_id = `inventory`.status_id 
LEFT JOIN mod_locations_data AS `locations` 
     ON `locations`.location_id = `inventory`.location_id 
LEFT JOIN mod_locations_rooms AS `rooms` 
     ON `rooms`.room_id = `inventory`.room_id 
LEFT JOIN mod_users_data AS `enteredBy` 
     ON `enteredBy`.user_id = `inventory`.entered_by 
LEFT JOIN mod_workorder_counts AS at 
     ON at.asset_tag = inventory.asset_tag 
ORDER BY inventory_id ASC LIMIT 0,20

它执行0.0051秒。这使得两个查询之间的总和为0.1631秒,接近1/10秒,而原始子查询则为15+秒。

如果我只是在不使用COALESCE的情况下包含字段“wo_count”,那么对于未在“mod_workorder_counts”表中列出的任何资产标签,我将获得NULL值。所以COALESCE会给我一个0的NULL值,这正是我想要的。

现在我将设置它,以便为资产标签输入工单时,我将在当时对计数表更新进行INSERT/UPDATE查询,因此不会不必要地运行。