2009-08-28 6 views
0

Hey. J'ai ces deux tables dans une relation 1: n.GROUP BY Optimisation

CREATE TABLE IF NOT EXISTS `de_locations` (
`id` int(11) NOT NULL auto_increment, 
`user_id` int(11) default NULL, 
`author_id` int(11) NOT NULL, 
`city_id` int(11) NOT NULL, 
`district_id` int(11) NOT NULL, 
`title` varchar(150) collate utf8_unicode_ci NOT NULL, 
`description` tinytext collate utf8_unicode_ci, 
`lat` double NOT NULL, 
`lng` double NOT NULL, 
`stars` double default '0', 
`comments` mediumint(9) default '0', 
`flag` tinyint(4) default '0', 
PRIMARY KEY (`id`), 
KEY `user_id` (`user_id`), 
KEY `flag` (`flag`), 
KEY `rating_district` (`district_id`,`stars`,`comments`), 
KEY `rating_city` (`city_id`,`stars`,`comments`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=15 ; 

et

CREATE TABLE IF NOT EXISTS `de_location2category` (
`id` int(11) NOT NULL auto_increment, 
`location_id` int(11) NOT NULL, 
`cat_id` mediumint(9) NOT NULL, 
PRIMARY KEY (`id`), 
UNIQUE KEY `rel` (`location_id`,`cat_id`) 
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=14 ; 

Un emplacement peut être placé dans plus d'une catégorie.

Par exemple:

Lieu: "Pizza Hut" Catégories: "Cuisine italienne", "Fast Food"

Ces catégories sont les catégories d'enfants de la nourriture de catégorie parente.

Maintenant, je veux sélectionner tous les endroits dans la catégorie des aliments.

SELECT a.id, a.title, a.description, a.street, a.hnr, ROUND(a.stars) as stars, a.comments, a.lat, a.lng 
FROM de_locations as a 
INNER JOIN de_location2category as b 
ON b.location_id = a.id 
WHERE b.cat_id BETWEEN 0 AND 100 
AND a.city_id = 1000 
GROUP BY a.id 
ORDER BY a.stars DESC, a.comments DESC 

J'ai besoin du GROUP BY parce que je veux ne pas les endroits en double si elles lorsqu'elles se rapportent à plus d'une catégorie. Mais cette requête crée une table temporaire et utilise filesort. Si je laisse le groupe par tout va bien, mais j'en ai besoin ...

Dois-je ajouter un autre index? Ou est-ce que quelque chose ne va pas avec mon régime? Comment pourriez-vous résoudre ce problème? Merci beaucoup.

+0

« Mais cette requête crée une table temporaire et utilise filesort. » Et alors? C'est ainsi que fonctionnent les bases de données relationnelles. Qu'est-ce qui ne va pas avec ça? Sinon, comment proposez-vous qu'il supprime les doublons? –

+0

http://dev.mysql.com/doc/refman/5.0/en/order-by-optimization.html "Dans certains cas, MySQL ne peut pas utiliser les index pour résoudre ORDER BY, bien qu'il utilise toujours des index pour trouver les lignes Ces cas sont les suivants: * Vous utilisez ORDER BY sur différentes clés: SELECT * FROM t1 ORDER BY key1, key2; * " – Amber

+0

" Qu'est-ce qui ne va pas? " La performance. Dans un simple SELECT un GROUP BY ne forcera pas toujours les tables temporaires. N'y a-t-il pas moyen d'y parvenir dans ce cas? L'index "rating_city" n'est pas utilisé avec GROUP BY. – Status4

Répondre

0

Pourquoi ne pas simplement utiliser DISTINCT a.id?

+0

même résultat que GROUP BY: tables temporaires et filesort – Status4

+0

Oui, c'est à peu près un fait puisque vous triez par plusieurs clés. C'est juste que DISTINCT décrit ce que vous essayez réellement de faire mieux que GROUP BY. – Amber

+0

Est-ce que je vous ai bien compris que le problème est que j'utilise l'index "rel" dans la clause WHERE et l'index "rating_city" dans la clause ORDER BY? Et à cause de cela, je n'ai pas eu l'occasion de changer seulement l'index? Je dois donc vivre avec des tables et des fichiers temporaires? Désolé, j'ai des problèmes de langue ... – Status4

1

Je pense que votre problème est que la requête est lente. Pas besoin de s'inquiéter de temporaire et filesort, mais pourquoi la requête est lente. Ajoutez la sortie de EXPLAIN {yourquery} pour que nous puissions vérifier ce qui se passe exactement.

Ou vous pouvez aussi essayer une sous-requête:

SELECT a.id, a.title, a.description, a.street, a.hnr, ROUND(a.stars) as stars, a.comments, a.lat, a.lng 
FROM de_locations as a 
WHERE 
a.id IN (SELECT DISTINCT b.location_id FROM de_location2category as b WHERE b.cat_id BETWEEN 0 AND 100) 
AND a.city_id = 1000 
GROUP BY a.id 
ORDER BY a.stars DESC, a.comments DESC 
+0

Hey Jay, merci. Je vais essayer ça. – Status4