2010-01-18 6 views
1

donc, j'ai essayé tout ce que je peux penser, et ne peut pas obtenir cette requête en moins de 3 secondes sur mon serveur local. Je sais que le problème a à voir avec l'OR référençant à la fois le owner_id et le person_id. si je cours l'un ou l'autre, cela arrive instantanément, mais avec un ou je n'arrive pas à le faire fonctionner - j'ai cherché à réécrire le code, mais la façon dont l'application a été conçue ne sera pas facile. Y at-il un moyen que je peux appeler un équivalent ou qui ne prendra pas si longtemps? voici le sql:Slow MySQL Query Rupture de mon dos!

SELECT event_types.name as event_type_name,event_types.id as id, count(events.id) as 
count,sum(events.estimated_duration) as time_sum FROM events,event_types 
WHERE event_types.id = events.event_type_id AND events.event_type_id != '4' 
AND (events.status!='cancelled') 
AND events.event_type_id != 64 
AND (events.owner_id = 161 OR events.person_id = 161) 
GROUP BY event_types.name 
ORDER BY event_types.name DESC; 

Voici la soupe Expliquer, bien que je suppose qu'il est inutile la cause il y a probablement une meilleure façon de structurer ce ou qui saute aux yeux:

merci beaucoup! chris.

+----+-------------+-------------+-------+---------------------------------------------------------------------------------------------------------+-------------------------------+---------+-------------------------------------+------+----------------------------------------------+ 
| id | select_type | table  | type | possible_keys                       | key       | key_len | ref         | rows | Extra          | 
+----+-------------+-------------+-------+---------------------------------------------------------------------------------------------------------+-------------------------------+-- 
| 1 | SIMPLE  | event_types | range | PRIMARY                         | PRIMARY      | 4  | NULL        | 78 | Using where; Using temporary; Using filesort | 
| 1 | SIMPLE  | events  | ref | index_events_on_status,index_events_on_event_type_id,index_events_on_person_id,index_events_on_owner_id | index_events_on_event_type_id | 5  | thenumber_production.event_types.id | 907 | Using where         | 
+----+-------------+-------------+-------+---------------------------------------------------------------------------------------------------------+-------------------------------+---------+-------------------------------------+------+----------------------------------------------+ 
+3

Quels sont les index sur 'events' et' event_types'? – wallyk

+0

D'accord, la sortie de 'SHOW CREATE TABLE' sur toutes les tables pertinentes serait très utile ici. –

Répondre

0

La requête va poser un problème. La solution normale d'affacturage des conditions OR (qui ne vont jamais être performant) est d'utiliser une UNION ALL-à-dire:

SELECT * 
FROM a 
WHERE field1 = 1 OR field2 = 2 

à:

SELECT * 
FROM a 
WHERE field1 = 1 
UNION ALL 
SELECT * 
FROM a 
WHERE field2 = 2 

Le fonctionne tant que vous ne faites pas obtenir (ou ne pas déranger) les doublons. Si vous avez des doublons et avez besoin de les factoriser, vous pouvez utiliser UNION à la place, mais cela implique une agrégation DISTINCT implicite, ce qui sera beaucoup moins performant.

Vous voulez également trier le résultat de cela, ce qui va encore être un problème. C'est l'un de ces cas où votre meilleure approche est de changer votre modèle de données afin qu'il se prête à l'écriture de requêtes performantes (c'est-à-dire que vous n'avez pas besoin d'utiliser un OR du tout).

Une question: est-ce que owner_id et person_id pour un événement peuvent être identiques?

+0

oui, ils pourraient éventuellement être les mêmes - bien que ce soit rare. On dirait qu'il est temps de creuser plus profondément et de commencer à refactoriser le modèle de données. Par curiosité, le syndicat travaillerait-il avec le GROUPE PAR? –

+0

Les UNIONs ne font que concaténer les résultats de la requête, alors oui vous pouvez GROUP BY par chaque morceau (ou même GROUP BY un et pas l'autre). Les colonnes doivent simplement correspondre en nombre et en type. – cletus

0

Que diriez-vous d'utiliser deux sous-requêtes, dont chacune fait l'un des deux chemins "ou", et l'union de ces sous-requêtes pour additionner les totaux dans un select de haut niveau?

Quelque chose comme: (et la syntaxe est pas exacte ici, comme je vais de mémoire):

select (bname, id, somme (id), somme (time_sum) de ((sélectionner. .. BigQuery avec owner_id) union (choisir ... BigQuery avec person_id))

0

Peut-être essayer avec:

SELECT event_types.name AS event_type_name, event_types.id AS id, 
      COUNT(events.id) AS count, SUM(events.estimated_duration) AS time_sum 
    FROM events 
    JOIN event_types ON event_types.id = event.event_type_id 
    WHERE events.event_type_id <> 4 
     AND events.status <> 'cancelled' 
     AND events.event_type_id <> 64 
     AND (events.owner_id = 161 OR events.person_id = 161) 
GROUP BY event_types.id 
ORDER BY event_types.name DESC; 

Et voici quelques conseils:

  • utilisation <> au lieu de !=
  • utilisation int valeurs 4 pour id au lieu de string'4'
+0

Pourquoi '<>' est-il différent de '! ='? –

+0

Juste pour clarifier, <> et! = Sont sémantiquement identiques dans mysql (et dans la plupart des autres versions de sql), donc cela ne vous donnera aucun avantage de performance. Cependant, changer '' 4 '' en seulement 4 améliorera les performances car sinon mysql doit faire une conversion sur chaque comparaison et encore plus important, il ne peut pas utiliser un index quand il doit faire la conversion de valeur. –

0

Voilà ce que je voudrais essayer.

Je veillerais j'avoir des clés uniques sur les deux 'OWNER_ID' et 'person_id':

alter table events add unique (person_id, id), add unique (owner_id, id); 

Vous devez également exécuter régulièrement ce:

analyze table events; 

Donnez qu'un essai. Une autre option consisterait à essayer de déplacer la partie incorrecte de la requête dans un prédicat de jointure.

SELECT event_types.name as event_type_name,event_types.id as id, count(events.id) as 
count,sum(events.estimated_duration) as time_sum FROM events,event_types 
JOIN event_types on event_types.id = events.event_type_id 
AND (events.owner_id = 161 OR events.person_id = 161) 
WHERE events.event_type_id != '4' 
AND (events.status!='cancelled') 
AND events.event_type_id != 64 
GROUP BY event_types.name 
ORDER BY event_types.name DESC; 

Notez que je n'ai pas essayé tout cela, et tout cela est hors de ma tête. Mais est ce que je voudrais essayer en premier.

0

essayer le casser en 2 requêtes avec UNION pour se joindre à la ResultSet, avec un events.owner_id = 161 et l'autre avec events.person_id = 161.

le « à l'aide filesort, en utilisant temporaire » est également un très mauvais signe, vous devriez probablement ajouter un index sur event_types.names.