2011-04-15 13 views
6

La consulta que tengo es para una tabla de inventario. Lo que hace la subconsulta de unión es obtener la cantidad total de órdenes de trabajo que hay para cada activo de inventario. Si ejecuto la consulta base con las combinaciones principales para el tipo de equipo, proveedor, ubicación y sala, funciona perfectamente. Menos de un segundo para devolver un resultado. usándolo con la combinación de subconsulta, tarda de 15 a 20 segundos en devolver un resultado.¿Cómo puedo acelerar mi consulta? la subconsulta es demasiado lenta

Ésta es la consulta completa:

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 

El MySQL datos de explicación para esto es que aquí

+----+-------------+--------------------+--------+---------------+-----------+---------+-------------------------------------+-------+---------------------------------+ 
| 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  | 
+----+-------------+--------------------+--------+---------------+-----------+---------+-------------------------------------+-------+---------------------------------+ 

uso de perfiles de consulta MySQL me sale esto:

+--------------------------------+------------+ 
| 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 | 
+--------------------------------+------------+ 

que me muestra que el cuello de la botella está copiando a la mesa temporal, pero no estoy seguro de cómo acelerarlo. ¿Hay configuraciones en el servidor que puedo configurar para que esto sea más rápido? ¿Hay cambios en la consulta existente que pueda hacer que produzcan los mismos resultados que serían más rápidos?

Me parece que la subconsulta LEFT JOIN daría la misma matriz de datos resultante cada vez, así que si tiene que ejecutar esa consulta para cada fila en la lista de inventario, puedo ver por qué sería lenta. ¿O MySQL almacena en caché la subconsulta cuando se ejecuta? Creí leer en algún lugar que MySQL no almacena en caché las subconsultas, ¿es así?

Cualquier ayuda es apreciada.

+0

¿Cuántos datos hay en estas tablas que está consultando? ¿Y se ha asegurado de que todas las columnas que está utilizando para sus predicados de unión estén indexadas? –

+0

La tabla de datos de inventario tiene 12,612 registros y la tabla de datos de órdenes de trabajo en la subconsulta tiene 19,159 registros. Tengo índices para todos los campos ID y el campo asset_tag usado en los JOIN. Quise indicar eso en la publicación original, pero lo olvidé. ¿Debo tenerlos todos en un solo índice? Actualmente tengo índices separados. Por cierto, gracias por reformatear la publicación. –

+0

Hmm. Eso no suena como una cantidad masiva de datos y no creo que la combinación de índices genere mucho impulso. Para solucionar el problemaproblema, realmente comenzaría por desmantelar la consulta. Elimine una unión a la vez y compare el rendimiento. Hacerlo puede revelar un problema en alguna parte. Tal vez comience eliminando la subconsulta y vea cómo funciona. Si solo es el culpable, una opción puede ser desnormalizar los datos y crear una tabla que contenga esos conteos. –

Respuesta

1

Esto es lo que hice que parece funcionar bien. Creé una tabla llamada mod_workorder_counts. La tabla tiene dos campos, etiqueta de activo que es única y wo_count que es y campo INT (3). Estoy poblar esa mesa con esta consulta:

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

que ejecutó en 0.1580 segundos que pueden ser considerados un poco lento, pero no está mal.

Ahora en que funciono esta modificación de mi búsqueda original:

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

Se ejecuta en 0,0051 segundos. Eso suma un total entre las dos consultas a 0.1631 segundos, que es cerca de 1/10 de segundo versus 15+ segundos con la sub consulta original.

Si acabo de incluir el campo "wo_count" sin usar el COALESCE, obtuve valores NULL para cualquier etiqueta de activo que no figuraba en la tabla "mod_workorder_counts". Entonces el COALESCE me daría un 0 por cualquier valor NULL, que es lo que quiero.

Ahora lo configuraré para que cuando se ingrese una orden de trabajo para una etiqueta de activo, tenga la consulta INSERT/UPDATE para la actualización de la tabla de conteos en ese momento para que no se ejecute innecesariamente.

Cuestiones relacionadas