2011-06-02 6 views
2

Je sais que je dois éviter d'utiliser la clause "IN", mais je ne sais pas comment faire dans ce cas. Le scénario est le suivant: de PHP que je reçois une liste de valeurs, puis construire une requête comme ceci:MySQL: Comment simplifier une requête "IN"

SELECT id FROM 
    (SELECT * FROM `tips` r 
     LEFT JOIN (SELECT tip FROM `tips_showed` WHERE user='8') AS a ON r.id=a.tip 
     WHERE a.tip IS NULL) AS t 
WHERE t.id IN 
    ('3','4','5','2','6','7','8','9','10','18', 
    '11','12','13','14','15','16','17','20') LIMIT 1 

Cela rend mon site très lent (parce qu'il est exécuté à chaque fois qu'un utilisateur visite il). Comment peut-il le rendre plus rapide?

+0

Pourquoi ne pas déplacer le «IN» à la sous-sélection? – Halcyon

Répondre

4

Je crois que vous pouvez simplifier la requête:

SELECT t.id 
    FROM `tips` t 
     LEFT JOIN `tips_showed` a 
      ON t.id = a.tip 
       AND a.user = '8' 
    WHERE a.tip IS NULL 
     AND t.id IN ('3','4','5','2','6','7','8','9','10','18', 
        '11','12','13','14','15','16','17','20') 
    LIMIT 1 

En utilisant NOT EXISTS au lieu du LEFT JOIN peut aussi vous acheter des performace supplémentaires. Essayez chacun et comparez.

SELECT t.id 
    FROM `tips` t 
    WHERE t.id IN ('3','4','5','2','6','7','8','9','10','18', 
        '11','12','13','14','15','16','17','20') 
     AND NOT EXISTS(SELECT NULL 
          FROM `tips_showed` a 
          WHERE a.tip = t.id 
           AND a.user = '8') 
    LIMIT 1 
+0

J'ai testé et le premier est le plus rapide. Merci – Ivan

1

Débarrassez-vous des sous-sélections. La clause IN() est parfaitement ok.

Suite à la demande de AJ pour écrire la requête corrigée:

SELECT `r`.`id` 
FROM `tips` AS `r` 
LEFT JOIN `tips_showed` AS `a` 
ON (`a`.`user`='8' AND `r`.`id`=`a`.`tip`) 
WHERE `r`.`id` IN ('3','4','5','2','6','7','8','9','10','18', 
    '11','12','13','14','15','16','17','20') 
AND `a`.`tip` IS NULL 
LIMIT 1 

Le code ci-dessus n'est pas testé et peut contenir des erreurs graves (a été écrit ad hoc) - si vous trouvez, s'il vous plaît me donner des commentaires à les commentaires.

plus

Ne pas oublier d'ajouter des index appropriés.

+0

Serait utile si vous avez réécrit la requête pour montrer à l'OP comment il pourrait être amélioré. –

+0

@AJ J'ai écrit ma version proposée sur votre demande, mais elle peut contenir beaucoup d'erreurs, comme obtenir une colonne de la mauvaise table. – Tadeck

1

je remarquai que le IN est une série séquentielle des valeurs

2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 and an extra number 20. 

Vous pouvez simplement remplacer le ici avec

WHERE (r.id = 20) OR (r.id BETWEEN 2 AND 18) 

beaucoup plus rapide, car il vous suffit de le faire 3 comparaisons pire des cas.

Si vous avez des valeurs aléatoires

Utilisez une table de mémoire temporaire.
Avec une clé primaire de hachage.
Et faire une jointure interne sur la clé de hachage primaire.

Temps cela et dites-moi combien plus vite il est allé :-)

+0

Merci, mais les valeurs peuvent être aléatoires :) – Ivan