Je travaille sur les rapports de notre application de suivi du temps. Chaque entrée de temps est liée à un projet et à un service. Voici une requête simplifiée pour regrouper les entrées de temps par projet et service.
SELECT
projects.name as project_name,
services.name as service_name,
SUM(minutes) AS minutes
FROM `time_entries`
JOIN `projects` ON `projects`.id = `time_entries`.project_id
JOIN `services` ON `services`.id = `time_entries`.service_id
GROUP BY
time_entries.project_id,
time_entries.service_id
ORDER BY
max(minutes) DESC
Il en résultera un tableau comme celui-ci :
+---------------+--------------+---------+
| project_name | service_name | minutes |
+---------------+--------------+---------+
| Business Card | Consulting | 4800 |
| Microsite | Coding | 3200 |
| Microsite | Consulting | 2400 |
| Microsite | Design | 2400 |
| Business Card | Design | 800 |
+---------------+--------------+---------+
Ce que j'essaie d'obtenir, c'est la possibilité de trier par la somme des minutes du projet. Ce n'est pas le projet "Carte de visite" qui devrait être en tête, mais le projet "Microsite", parce qu'il a plus de minutes.
+---------------+--------------+-----------------+---------+
| project_name | service_name | project_minutes | minutes |
+---------------+--------------+-----------------+---------+
| Microsite | Coding | 8000 | 3200 |
| Microsite | Consulting | 8000 | 2400 |
| Microsite | Design | 8000 | 2400 |
| Business Card | Consulting | 5600 | 4800 |
| Business Card | Design | 5600 | 800 |
+---------------+--------------+-----------------+---------+
La seule façon que j'ai trouvée pour obtenir la colonne "project_minutes" est de créer d'abord une table et de la joindre à elle-même. La requête que j'ai trouvée :
DROP TABLE IF EXISTS group2;
CREATE TABLE group2 SELECT
projects.id as project_id,
projects.name as project_name,
services.name as service_name,
SUM(minutes) AS minutes
FROM `time_entries`
JOIN `projects` ON `projects`.id = `time_entries`.project_id
JOIN `services` ON `services`.id = `time_entries`.service_id
GROUP BY
time_entries.project_id,
time_entries.service_id
ORDER BY
max(minutes) DESC
LIMIT 0, 30;
SELECT
project_name, service_name, project_minutes, minutes
FROM
group2
LEFT JOIN
(
SELECT project_id as project_id, sum(minutes) AS project_minutes
FROM group2
GROUP BY project_id
) as group1 on group1.project_id = group2.project_id
ORDER BY
project_minutes DESC,
minutes DESC;
Je ne suis même pas capable de créer une table temporaire à cause d'un bug mySQL ( ?): http://www.google.com/search?&q=site:bugs.mysql.com+reopen+temporary+table
Mes questions :
- Quelle serait la meilleure façon d'obtenir une colonne comme "project_minutes" qui additionne les minutes d'un groupe et ajoute le résultat en tant que colonne supplémentaire ? Existe-t-il une astuce SQL que je ne connais pas ?
- Si vous ne voyez pas de solution à ma première question, pensez-vous qu'il soit judicieux de créer une table supplémentaire pour chaque requête ? Est-ce plus rapide que de faire cette logique manuellement après dans le code ? Nous utilisons Rails, au cas où cela ferait une différence.
Merci beaucoup pour votre aide !
MISE À JOUR
Merci pour vos réponses jusqu'à présent. Je les résume pour avoir une meilleure vue d'ensemble : http://gist.github.com/553560
Ai-je raison de dire qu'il n'y a pas d'autre moyen que d'interroger la table time_entries une fois pour chaque instruction group by ? Si oui, constatez-vous des problèmes de performance en raison des faits suivants :
- La table time_entries est de loin celle qui contient le plus grand nombre de lignes (~4 millions).
- L'utilisateur peut regrouper jusqu'à 6 colonnes. Jetez un coup d'œil à cette capture d'écran : http://dl.dropbox.com/u/732913/time_entries_grouped_by_customer_project_service_user.png