2010-07-02 5 views
0

ici, il estComment améliorer cette requête mysql vitesse

SELECT tbl_rls . * , (

SELECT count(*) 
FROM `comments` 
WHERE `post_id` = `tbl_rls`.`id` 
) AS `t_comments` 
FROM `tbl_rls` 
WHERE 1 =1 
AND `status` <> 'denied' 
AND (
`id` 
IN (

SELECT `rls_id` 
FROM `tbl_visitors_logs` 
WHERE `date` LIKE '2010-07-02%' 
AND `page_type` = 'post' 
GROUP BY `rls_id` 
ORDER BY count(*) DESC 
) 
) 
AND (
`cat` = '6' 
OR `cat` 
IN (

SELECT `id` 
FROM `tbl_cats` 
WHERE `parent_id` = '6' 
) 
) 
ORDER BY `tbl_rls`.`date` DESC 
LIMIT 0 , 20 

Ceci est en train de tuer presque DB lors de l'exécution, peut suggérer une solution pour le rendre rapide?

Je suis ici pour fournir toute information supplémentaire nécessaire.

Merci.

+1

ce qu'il devrait faire. coller ici vos tables mysql, c'est difficile de résoudre le problème juste à partir de la requête –

Répondre

1

Voici mon re-écriture de votre requête:

SELECT t. *, 
      x.t_comments 
    FROM tbl_rls t 
LEFT JOIN (SELECT c.post_id, 
        COUNT(*) AS t_comments 
      FROM COMMENTS c 
     GROUP BY t.post_id) x ON x.post_id = t.id 
    JOIN tbl_visitors_logs tvl ON tvl.rls_id = t.id 
           AND tvl.date LIKE '2010-07-02%' 
           AND tvl.page_type = 'post' 
    WHERE t.status != 'denied' 
     AND (t.cat = '6' OR t.cat IN (SELECT `id` 
             FROM `tbl_cats` 
            WHERE `parent_id` = '6')) 
ORDER BY t.`date` DESC 
    LIMIT 0, 20 
1

N'utilisez pas de sous-requêtes. Chaque sous-requête s'exécute une fois pour chaque ligne. Donc, si la requête externe renvoie 10 lignes, la requête interne sera exécutée 10 fois.

L'effet est encore aggravé par le fait que vous avez une sous-requête dans une sous-requête. L'effet se multiplie, donc si l'extérieur renvoie 10 lignes et l'interne renvoie 10 lignes, alors la plus interne courra 100 fois. Edit: faites attention à ce dernier paragraphe - il semblait que vous aviez une sous-requête dans une sous-requête, mais en regardant à nouveau, ce n'est pas le cas. De toute façon, n'utilisez pas de sous-requêtes.

+0

Oh putain :(. je vois maintenant – Arshdeep

+1

Vous parlez en fait de sous-requêtes corrélées, et si les sous-requêtes étaient si mauvaises - elles auraient été supprimées. –

+0

Pas vraiment Les sous-requêtes corrélées sont si mauvaises et elles n'ont pas été enlevées – riwalk

2

Avez-vous exécuté une commande EXPLAIN pour voir quelle partie de la requête est lente?

En outre, cette ligne peut poser un problème: WHERE date LIKE '2010-07-02%' Cela peut provoquer la conversion de la colonne de date en chaîne (indiquez-moi que ce n'est pas une chaîne!) Qui empêchera l'utilisation d'un index. Essayez plutôt WHERE DATE(date) = '2010-07-02'.

+0

ce n'est pas une chaîne, c'est Datetime feild .Mais je veux avoir des enregistrements de date unique et les valeurs sont comme '2010-07-02 12:03: 49 '. – Arshdeep

+0

thats exactement quoi pour: tronque le temps hors de date/heure: mysql> SELECT DATE ('2003-12-31 01:02:03'); -> '2003-12-31' –

1

Vous pouvez essayer cette (sans données, il est difficile de tester)

SELECT r.*, COUNT(c.id) 
FROM tbl_rls r, comments c, tbl_visitors_logs v, tbl_cats t 
WHERE c.post_id = r.id 
    AND v.rls_id = r.id 
    AND t.parent_id = r.cat 
    AND r.status <> 'denied' 
    AND v.`date` LIKE '2010-07-02%' 
    AND page_type = 'post' 
    AND cat = 6 OR t.parent_id = 6 
GROUP BY c.post_id 
ORDER BY r.`date` DESC 
LIMIT 0, 20 

est cette structure de données correcte?

CREATE TABLE IF NOT EXISTS `tbl_cats` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `parent_id` int(11) NOT NULL, 
    `name` varchar(10) NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; 

CREATE TABLE IF NOT EXISTS `tbl_rls` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `status` varchar(10) NOT NULL, 
    `cat` int(11) NOT NULL, 
    `date` date NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; 

CREATE TABLE IF NOT EXISTS `tbl_visitors_logs` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `rls_id` int(11) NOT NULL, 
    `date` date NOT NULL, 
    `page_type` varchar(10) NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; 

CREATE TABLE IF NOT EXISTS `comments` (
    `id` int(11) NOT NULL AUTO_INCREMENT, 
    `post_id` int(11) NOT NULL, 
    `commetn` varchar(50) NOT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ; 
+0

'COUNT (DISTINCT c.id)' ou cela ne fonctionnera pas –

1
SELECT tbl_rls.*, 
     COUNT(distinct comments.id) AS t_comments 
    FROM tbl_rls 
     JOIN tbl_visitors_logs tvl ON tvl.rls_id = tbl_rls.id 
     AND tvl.page_type = 'post' 
     AND DATE(tvl.date) = '2010-07-02' 
     LEFT JOIN comments ON comments.post_id = tbl_rls.id 
     LEFT JOIN tbl_cats ON tbl_cats.id = cat AND tbl_cats.parent_id = '6' 
WHERE status <> 'denied' 
    AND (cat = 6 OR tbl_cats.id is not null) 
GROUP BY tbl_rls.id       
ORDER BY tbl_rls.date DESC 
LIMIT 0, 20 
1

La meilleure chose que vous pouvez faire est de se débarrasser du mot-clé LIKE, et dire simplement:

WHERE v.Date > '2010-07-02' AND v.Date < '2010-07-03' 

De cette façon, vous aurez tout pour la journée (ou quelle que soit la plage de dates dont vous avez besoin). La meilleure façon d'y penser est que mySQL devra passer en revue et évaluer chaque ligne, même s'il s'agit déjà d'un champ datetime. Si le champ v.Date fait l'objet d'une recherche régulière, vous pouvez lui ajouter un index pour accélérer les choses, puis accélérer les choses car il aura une idée de l'emplacement des données.

Vous pouvez également utiliser COUNT (ID) au lieu de tout compter. Compter un champ au lieu de 10 ou 20 ou 50 peut sauver quelques millisecondes.

Questions connexes