2010-08-30 6 views
2

Je n'arrive pas à résoudre une requête. J'ai essayé des sous-requêtes, des jointures différentes et group_concat() mais elles ne fonctionnent pas ou sont douloureusement lentes. Cela peut être un peu compliqué à expliquer, mais voici le problème:mysql select - sous-requête, group_concat() ne fonctionne pas/trop lent

J'ai une table "article" (avec environ 2000 produits). J'ai une table "tag" (qui contient environ 2000 étiquettes de produits différents). Et j'ai une table "tagassign" (qui relie les tags aux items, avec environ 200000 enregistrements). J'utilise l'étiquette pour définir les caractéristiques des produits, par exemple la couleur, la compatibilité, si le produit fait l'objet d'une offre spéciale, etc. Maintenant, si je veux pouvoir montrer les produits pour lesquels une étiquette donnée a été attribuée à eux, j'utilise une requête simple comme:

select * from item, tagassign 
    where item.itemid = tagassign.itemid 
    and tagassign.tagid = "specialoffer" 

Le problème est que je peux vouloir voir les éléments qui ont plusieurs étiquettes. Par exemple, je pourrais vouloir voir seulement les cas de téléphone portable noir qui sont compatibles avec l'iPhone d'Apple et sont nouveaux. Donc, je veux essentiellement voir tous les enregistrements de la table de l'article, qui ont des balises "noir" et "cas" et "iphone" et "nouveau". La seule façon de faire fonctionner ceci est de créer 4 alias (sélectionnez * depuis item, tagassign, tagassign comme t1, tagassign comme t2, tagassign comme t3 etc.). Dans certains cas, je pourrais chercher 10 ou 20 tags différents, et avec autant d'enregistrements, les requêtes sont terriblement lentes. Je sais qu'il me manque quelque chose d'évident. Des idées? Merci!

+0

Montrez-nous vos index 'EXPLAIN SELECT'? – Piskvor

Répondre

3
SELECT * 
FROM item i 
WHERE (
     SELECT COUNT(*) 
     FROM tagassign ta 
     WHERE ta.tagid IN ('black', 'case', 'iphone', 'new') 
       AND ta.itemid = i.itemid 
     ) = 4 

Remplacez le nombre réel des balises que vous cherchez au lieu de 4.

Créez un index unique ou une clé primaire sur tagassign (itemid, tagid) (dans cet ordre) pour que cela fonctionne rapidement.

Si vous recherchez des lots d'étiquettes (ou tags utilisés rarement), cette requête peut également être plus rapide:

SELECT i.* 
FROM (
     SELECT itemid 
     FROM tagassign ta 
     WHERE ta.tagid IN ('black', 'case', 'iphone', 'new') 
     GROUP BY 
       itemid 
     HAVING COUNT(*) = 4 
     ) t 
JOIN item i 
ON  i.itemid = t.itemid 

Pour cette requête, vous auriez besoin d'un index unique sur tagassign (tagid, itemid).

+0

Vous êtes curieux du choix de la syntaxe - pourquoi ne pas utiliser EXISTS et spécifier 'HAVING COUNT (DISTINCT ta.tagid) = 4'? Il n'était pas là quand je faisais des commentaires, je le jure ... –

+0

@OMGPonies: Puisque l'index est unique, le COUNT (*) ne comptera que les enregistrements correspondants, chacun une fois. 'EXISTS' serait une exagération là-bas. Vous pouvez fournir une réponse avec cela, juste pour l'exhaustivité, je vais l'upvote :) – Quassnoi

+0

+1: J'ai manqué la contrainte unique, distrait par la syntaxe dans la première option –