2016-08-25 2 views
2

Ma table 'mes_journaux' contient environ 20 000 000 enregistrements et je souhaite connaître le nombre de journaux que j'ai dans chaque date en quelques jours.La meilleure façon de convertir efficacement le fuseau horaire dans une requête MYSQL

Je veux avoir un résultat comme

+------------+---------+ | date | count | +------------+---------+ | 2016-07-01 | 1623 | | 2016-07-02 | 1280 | | 2016-07-03 | 2032 | +------------+---------+

Cette requête ci-dessous ne me prendre millisecondes pour terminer, ce qui est bon

SELECT DATE_FORMAT(created_at, '%Y-%m-%d') as date, 
     COUNT(*) as count 
    FROM my_logs 
    WHERE created_at BETWEEN '2016-07-01' AND '2016-07-04' 
    GROUP BY DATE_FORMAT(created_at, '%Y-%m-%d') 

Le Expliquer de requête:

+------------+---------+-------+-----------------------------+ |select_type | table | type | possible_keys | +------------+---------+-------+-----------------------------+ | SIMPLE | my_logs| index | index_my_logs_on_created_at | +------------+---------+-------+-----------------------------+

+-----------------------------+---------+----------+ | key | key_len | rows | +-----------------------------+---------+----------+ | index_my_logs_on_created_at | 10 | 23458462 | +-----------------------------+---------+----------+

+-----------------------------------------------------------+ | Extra | +-----------------------------------------------------------+ | Using where; Using index; Using temporary; Using filesort | +-----------------------------------------------------------+

Cependant, je dois convertir le fuseau horaire de chaque enregistrement pour tenir le temps dans mon pays, et je dois groupe par l'information « Date », donc je dois convertir la colonne elle-même.

deux

SELECT COUNT(*) 
    FROM my_logs 
    WHERE DATE_ADD(created_at, INTERVAL 8 HOUR) BETWEEN '2016-07-01' AND '2016-07-04' 
    GROUP BY DATE_FORMAT(DATE_ADD(created_at, INTERVAL 8 HOUR), '%Y-%m-%d') 

et

SELECT COUNT(*) 
    FROM my_logs 
    WHERE CONVERT_TZ(created_at, "+00:00", "+08:00") BETWEEN '2016-07-01' AND '2016-07-04' 
    GROUP BY DATE_FORMAT(CONVERT_TZ(created_at, "+00:00", "+08:00"), 

'% Y-% m-% d')

prendre moi de 12s pour terminer la requête, il est insupportable lent !!

(Le Expliquer est la même que la requête en haut)


Je pense que ce problème est courant, mais je ne peux pas trouver une bonne façon de traiter avec elle, ce que quelqu'un a une plus efficace façon de le faire? Merci!

+1

['convert_tz'] (http://dev.mysql.com/doc/refman/5.7/fr/date-and-time-functions.html#function_convert-tz) convertit d'un fuseau horaire à un autre. –

+0

Pouvez-vous fournir les plans de requête (sortie 'EXPLAIN') pour les deux cas? – Vatev

+0

J'ajoute la sortie! Merci. En fait, j'ai déjà la sortie que je veux, mais mon problème concerne l'efficacité. Après avoir examiné le problème du fuseau horaire, je ne peux pas trouver un moyen efficace de faire quelque chose qui, à mon avis, devrait être facile à l'origine. –

Répondre

1

Quel type de données, TIMESTAMP par rapport à DATETIME, avez-vous utilisé? (Mais, je vais ignorer cela.)

Ne pas "cacher" une colonne indexée (created_at) à l'intérieur de toute fonction (CONVERT_TZ()). Il fait en sorte que la clause WHERE ne peut pas utiliser l'index et doit analyser la table à la place. Cette correction est simple:

WHERE created_at >= '2016-07-01' - INTERVAL 8 HOUR 
    AND created_at < '2016-07-04' - INTERVAL 8 HOUR 

(ou d'utiliser CONVERT_TZ). Notez que j'ai également corrigé le bug dans lequel vous avez inclus minuit à partir du 4ème. Note: Même + INTERVAL... est effectivement une fonction.

Les expressions dans SELECT et GROUP BY sont beaucoup moins critiques pour les performances.

+0

Super! Donc, la meilleure façon de le faire est de convertir la plage de dates dans la clause WHERE et de convertir la colonne dans la clause GROUP! Cela résout mon problème! Merci –