2012-02-16 6 views
0

J'ai hérité du code, l'auteur original n'est pas joignable et je serais extrêmement reconnaissant pour toute aide, car ma connaissance de MySQL n'est pas géniale. J'ai la requête suivante qui prend environ 4 secondes à exécuter, il y a seulement environ 20 000 lignes de données dans toutes les tables combinées, donc je pense que la requête pourrait être rendue plus efficace, peut-être en la divisant en plus d'une requête, la voici:Performance de la requête MySQL

SELECT SQL_CALC_FOUND_ROWS ci.id AS id, ci.customer AS customer, ci.installer AS installer, ci.install_date AS install_date, ci.registration AS registration, ci.wf_obj AS wf_obj, ci.link_serial AS link_serial, ci.sim_serial AS sim_serial, sc.call_status AS call_status 
    FROM ap_servicedesk.corporate_installs AS ci 
    LEFT JOIN service_calls AS sc ON ci.wf_obj = sc.wf_obj 
    WHERE ci.acc_id = 3 
    GROUP BY ci.id 
    ORDER BY link_serial 
       asc 
    LIMIT 40, 20 

Quelqu'un peut-il trouver un moyen de rendre cela plus efficace, merci.

(Certaines valeurs sont définies en tant que variables, mais au-dessus exécutant la requête en PHPMyAdmin prend ~ 4secs)

La colonne id est l'index primaire.

Plus d'infos comme demandé:

table corporate_installs:

Field  Type Null Key Default Extra 

id    int(11) NO PRI NULL auto_increment 
customer  varchar(800) NO  NULL  
acc_id  varchar(11) NO  NULL  
installer  varchar(50) NO  NULL  
install_date varchar(50) NO  NULL  
address_name varchar(30) NO  NULL  
address_street varchar(40) NO  NULL  
address_city varchar(30) NO  NULL  
address_region varchar(30) NO  NULL  
address_post_code varchar(10) NO  NULL  
latitude   varchar(15) NO  NULL  
longitude   varchar(15) NO  NULL  
registration varchar(50) NO  NULL  
driver_name   varchar(50) NO  NULL  
vehicle_type varchar(50) NO  NULL  
make   varchar(50) NO  NULL  
model   varchar(50) NO  NULL  
vin     varchar(50) NO  NULL  
wf_obj   varchar(50) NO  NULL  
link_serial   varchar(50) NO  NULL  
sim_serial   varchar(50) NO  NULL  
tti_inv_no   varchar(50) NO  NULL  
pro_serial   varchar(50) NO  NULL  
eco_serial   varchar(50) NO  NULL  
eco_bluetooth varchar(50) NO  NULL  
warranty_expiry varchar(50) NO  NULL  
project_no   varchar(50) NO  NULL  
status   varchar(15) NO  NULL  

table service_calls:

Field   Type   Null Key Default Extra 
id     int(11)  NO  PRI NULL auto_increment 
acc_id   int(15)   NO  NULL  
ciid   int(11)   NO  NULL  
installer_job_no varchar(50) NO  NULL  
installer_inv_no varchar(50) NO  NULL  
engineer   varchar(50) NO  NULL  
request_date varchar(50) NO  NULL  
completion_date varchar(50) NO  NULL  
call_status   varchar(50) NO  NULL  
registration varchar(50) NO  NULL  
wf_obj   varchar(50) NO  NULL  
driver_name   varchar(50) NO  NULL  
driver_phone varchar(50) NO  NULL  
team_leader_name varchar(50) NO  NULL  
team_leader_phone varchar(50) NO  NULL  
servicing_address varchar(150) NO  NULL  
region   varchar(50) NO  NULL  
post_code   varchar(50) NO  NULL  
latitude   varchar(50) NO  NULL  
longitude   varchar(50) NO  NULL  
incident_no   varchar(50) NO  NULL  
service_type varchar(20) NO  NULL  
fault_description varchar(50) NO  NULL  
requested_action varchar(50) NO  NULL  
requested_replacemt varchar(100) NO  NULL  
fault_detected varchar(50) NO  NULL  
action_taken varchar(50) NO  NULL  
parts_used   varchar(50) NO  NULL  
new_link_serial varchar(50) NO  NULL  
new_sim_serial varchar(50) NO  NULL  

(Toutes mes excuses pour la mise en forme, je l'ai fait de mon mieux)

Faites-moi savoir si vous avez besoin de plus d'informations merci.

Plus d'infos (je l'ai fait à nouveau la requête avec EXPLAIN):

id select_type table type possible_keys key key_len ref rows Extra 
1 SIMPLE ci ALL acc_id NULL NULL NULL 7227 Using where; Using temporary; Using filesort 
1 SIMPLE sc ALL NULL NULL NULL NULL 410 
+0

Pouvez-vous également fournir la structure de la table que votre problème peut être plus là plutôt que la requête – Ryan

+0

Peut-être mieux sur [révision de code] (http://codereview.stackexchange.com/?as=1) ou [administrateurs de base de données ] (http://dba.stackexchange.com/?as=1) –

Répondre

2

Ajoutez des index sur les deux colonnes wf_obj, la colonne link_serial (vous pouvez également avoir besoin d'un index sur le acc_id).

Ensuite, essayez cette version:

SELECT ... 
FROM 
     (SELECT * 
     FROM ap_servicedesk.corporate_installs 
     WHERE acc_id = 3 
     ORDER BY link_serial ASC 
     LIMIT 60 
    ) AS ci 
    LEFT JOIN service_calls AS sc 
    ON sc.PK =       --- the PRIMARY KEY of the table 
    (SELECT PK 
     FROM service_calls AS scm 
     WHERE ci.wf_obj = scm.wf_obj 
     ORDER BY scm. --- whatever suits you 
     LIMIT 1 
    ) 
ORDER BY ci.link_serial ASC 
LIMIT 20 OFFSET 40 

Le ORDER BY scm.SomeColumn est nécessaire non pas pour la performance, mais pour obtenir des résultats cohérents. Votre requête telle qu'elle est, joint une ligne de la première table à toutes les lignes connexes de la deuxième table. Mais la dernière GROUP BY agrège toutes ces lignes (de la deuxième table), ainsi votre SELECT ... sc.call_status choisit un call_status plus ou moins aléatoire parmi l'une de ces lignes.

+0

Merci, j'obtiens une erreur: # 1064 - Vous avez une erreur dans votre syntaxe SQL; consultez le manuel qui correspond à votre version du serveur MySQL pour la bonne syntaxe à utiliser près de 'SELECT PK FROM service_calls AS scm WHERE ci.wf_obj = scm.wf_obj' à la ligne 11 – davidjwest

+0

N'écrivez pas 'PK'. Mettez la colonne Clé primaire là à la place. –

+0

Merci beaucoup, semble beaucoup plus rapide maintenant, laissez-moi le faire fonctionner correctement, rendra compte. – davidjwest

2

Le premier endroit où je DECOUVRIR ceci devrait être un index.

Il y a un groupe sur ci.id qui est le PK qui est bien, mais vous commandez par link_ser (table source non spécifiée) et vous sélectionnez en fonction de ci.acc_id.

Si vous ajoutez une clé supplémentaire sur la table corp_installs pour le champ acc_id, cela seul devrait aider à augmenter les performances car il sera utilisable pour la clause WHERE. En regardant plus loin, vous avez ci.wf_obj = sc.wf_obj dans la jointure. Rejoindre un VARCHAR sera lent, et vous n'êtes pas réellement utiliser ce dans le cadre des critères de sélection et donc un sous-requête peut être votre ami, considérer les points suivants

SELECT 
    serviceCallData.*, 
    sc.call_status AS call_status 

FROM (
    SELECT 
    SQL_CALC_FOUND_ROWS AS found_rows, 
    ci.id AS id, 
    ci.customer AS customer, 
    ci.installer AS installer, 
    ci.install_date AS install_date, 
    ci.registration AS registration, 
    ci.wf_obj AS wf_obj, 
    ci.link_serial AS link_serial, 
    ci.sim_serial AS sim_serial 

    FROM ap_servicedesk.corporate_installs AS ci 
    WHERE ci.acc_id = 3 
    GROUP BY ci.id 
    ORDER BY ci.link_serial ASC 
    LIMIT 40, 20 
) AS serviceCallData 
LEFT JOIN serice_calls AS sc ON serviceCallData.wf_obj = sc.wf_obj 

En plus de cela, changer que (ACC_ID) clé à être (acc_id, link_serial) comme alors il sera utilisable dans le tri. Ajoutez également une clé sur (wf_obj) dans serice_calls.

Cela sélectionnera les 20 lignes de la table corpoprate_installs et seulement les rejoindre sur la table service_calls en utilisant la VARCHAR inefficace rejoindre

J'espère que cela est d'une aide

+0

Merci, c'est 2 dixièmes de seconde plus vite maintenant mais j'avais espéré un peu mieux. Tout autre conseil est apprécié. – davidjwest

+0

Ayant regardé votre schéma, la plus grande sonnerie d'alarme est votre LEFT JOIN sur ci.wf_obj = sc.wf_obj, où les deux sont VARCHAR (50). Ce sera lent en particulier sur les jeux de données plus volumineux. Une autre petite modification que vous pourriez apporter est d'ajouter une clé pour wf_obj dans la table service_calls, et de changer la clé précédente pour qu'elle soit acc_id, wf_obj, mais cela n'obtiendrait probablement pas de grande amélioration. Will mise à jour répondre sous peu –

1

Je pense que l'option SQL_CALC_FOUND_ROWS utilisé avec join et un groupe par pourraient être en train de dégrader les performances (regardez here pour certains tests, info sur SQL_CALC_FOUND_ROWS here). Il semble en fait que les indices ne sont pas utilisés dans ce cas.Essayez de remplacer votre requête par deux requêtes distinctes, celle avec LIMIT suivie d'un COUNT().

+0

Merci, drôlement, je lisais cet article avant de poster sur ici, avec la quantité de données que je travaille avec Je doute que la division de la requête ferait beaucoup, mais je vais garder cela à l'esprit une fois que nous commençons obtenir plus de données. Appréciez le conseil. – davidjwest