2009-07-07 2 views
0

Je suis à la recherche d'un conseil sur les meilleures pratiques pour accélérer les requêtes et en même temps minimiser les frais généraux nécessaires pour appeler les fonctions date/mktime. Pour banaliser le problème que je fais face à la mise en page de tableau suivant:Comment minimiser la charge dans les requêtes nécessitant un regroupement avec différents invertis?

CREATE TABLE my_table(
    id INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT, 
    important_data INTEGER, 
    date INTEGER); 

L'utilisateur peut choisir d'afficher 1) toutes les entrées entre deux dates:

SELECT * FROM my_table 
    WHERE date >= ? AND date <= ? 
    ORDER BY date DESC; 

Sortie:

10-21-2009 12:12:12, 10002 
10-21-2009 14:12:12, 15002 
10-22-2009 14:05:01, 20030 
10-23-2009 15:23:35, 300 
.... 

Je ne pense pas qu'il y ait beaucoup à améliorer dans ce cas.

2) Résumer/groupe la sortie par jour, semaine, mois, année:

SELECT COUNT(*) AS count, SUM(important_data) AS important_data 
    FROM my_table 
    WHERE date >= ? AND date <= ? 
    ORDER BY date DESC; 

Exemple de sortie par mois:

10-2009, 100002 
11-2009, 200030 
12-2009, 3000 
01-2010, 0 /* <- very important to show empty dates, with no entries in the table! */ 
.... 

Pour accomplir l'option 2) Je suis actuellement un très coûteux pour-boucle avec mktime/date comme le suivant:

for(...){ /* example for group by day */ 
    $span_from = (int)mktime(0, 0, 0, date("m", $time_min), date("d", $time_min)+$i, date("Y", $time_min)); 
    $span_to = (int)mktime(0, 0, 0, date("m", $time_min), date("d", $time_min)+$i+1, date("Y", $time_min)); 
    $query = ".."; 
    $output = date("m-d-y", ..); 
} 

Quelles sont mes idées jusqu'à présent? Ajoutez des colonnes supplémentaires/redondantes (INTEGER) pour le jour (20091212), le mois (200912), la semaine (200942) et l'année (2009). De cette façon, je peux me débarrasser de toutes les requêtes inutiles dans la boucle for. Cependant je suis toujours confronté au problème pour calculer très rapidement toutes les dates qui n'ont pas d'équivalent dans la base de données. Une façon de simplement déplacer le problème pourrait être de laisser MySQL faire le travail et simplement utiliser une grosse requête (calculer toutes les dates/utiliser les fonctions de date MySQL) avec une jointure à gauche (les données). Serait-il sage de laisser MySQL prendre la charge supplémentaire? Quoi qu'il en soit, je suis réticent à utiliser tous ces mktime/date dans la boucle for. Depuis que j'ai un contrôle complet sur la disposition de la table et le code, même les suggestions avec des changements majeurs sont les bienvenues!

Mise à jour

Merci à Greg je suis venu avec la requête SQL suivante. Cependant, il encore me dérange d'utiliser 50 lignes d'instructions SQL - construire avec php - que peut-être pourrait être fait plus rapidement et avec plus d'élégance autrement:

SELECT * FROM ( 
    SELECT DATE_ADD('2009-01-30', INTERVAL 0 DAY) AS day UNION ALL 
    SELECT DATE_ADD('2009-01-30', INTERVAL 1 DAY) AS day UNION ALL 
    SELECT DATE_ADD('2009-01-30', INTERVAL 2 DAY) AS day UNION ALL 
    SELECT DATE_ADD('2009-01-30', INTERVAL 3 DAY) AS day UNION ALL 
    ...... 
    SELECT DATE_ADD('2009-01-30', INTERVAL 50 DAY) AS day) AS dates 
LEFT JOIN (
    SELECT DATE_FORMAT(date, '%Y-%m-%d') AS date, SUM(data) AS data 
    FROM test 
    GROUP BY date 
) AS results 
ON DATE_FORMAT(dates.day, '%Y-%m-%d') = results.date; 

Répondre

1

Vous devriez certainement pas faire une requête dans une boucle. Vous pouvez groupe comme celui-ci:

SELECT COUNT(*) AS count, SUM(important_data) AS important_data, DATE_FORMAT('%Y-%m', date) AS month 
    FROM my_table 
    WHERE date BETWEEN ? AND ? -- This should be the min and max of the whole range 
    GROUP BY DATE_FORMAT('%Y-%m', date) 
    ORDER BY date DESC; 

Tirez ensuite ceux-ci dans un tableau calée par date et boucle sur vos données de distance que vous faites (cette boucle doit être assez léger sur CPU).

+0

Merci pour la réponse rapide! Il me faudra du temps pour tester votre idée, car je dois passer de INTEGER à DATETIME pour utiliser DATE_FORMAT avec succès. Cependant, cela semble prometteur. Si je ne me trompe pas, une chose manque, comment puis-je obtenir toutes ces dates sans entrées dans le tableau? Par exemple: j'ai des données pour 2009-10 et 2009-11, mais 2009-12 est vide, mais devrait être visible néanmoins ..devrais-je revenir à la fonctionnalité PHP pour cela ou y at-il une solution mysql élégante pour cela? – merkuro

+0

@merkuro: Vous ne pouvez sortir que des données * présentes *. Les enregistrements manquants n'apparaîtront pas par magie. Si vous avez besoin d'un enregistrement pour chaque jour de l'année, créez une table de calendrier et créez une jointure externe contre cela. Une telle table doit être créée/remplie seulement une fois et elle a juste besoin de 365 rangées par an. De cette façon, tous les "trous" disparaîtraient dans la sortie. – Tomalak

+0

@Tomalak Vous voulez dire que les installations MySQL ne sont pas équipées d'un magicien intégré? Honnêtement, dans quel but ton commentaire sarcastique? J'ai déjà mentionné l'option d'utiliser une jointure à gauche, bien que je vois un problème de performance potentiel dans cette solution en raison de l'utilisation accrue du disque. Quoi qu'il en soit, j'espérais que quelqu'un trouverait une belle gamme/tableau comme caractéristique que j'ai oubliée. – merkuro

Questions connexes