On peut supposer que vous avez une table de calendrier, si vous êtes en mesure d'interroger la semaine/mois/année (sinon , vous devriez en créer un). L'utilisation des fonctions (y compris la date mathématique) dans les clauses WHERE
et GROUP BY
invalide l'utilisation d'un index, ce qui entraîne généralement des requêtes plus lentes. Au lieu de cela, il est préférable de spécifier les points de début et de fin de la plage afin que le système puisse directement toucher les indices.
Dans tous les cas, ajoutons du temps à nos données de date!
D'accord, nous essayons de grouper par jour, non?
SELECT calendarDate as start
FROM CalendarTable
WHERE calendarDate >= :rangeStart
AND calendarDate < :rangeEnd
... eh bien, d'accord, qui nous obtient le début, mais quand il est utile d'interroger d'avoir la fin, ou bien, le début du groupe suivant, ainsi:
SELECT calendarDate as start, calendarDate + 1 DAY as end
FROM CalendarTable
WHERE calendarDate >= :rangeStart
AND calendarDate < :rangeEnd
Voilà pour les dates ... sauf qu'il faut ajouter du temps!
Heureusement, c'est une valeur constante:
SELECT calendarDate as startDate, TIME('10:00:00') as startTime
calendarDate + 1 DAY as endDate, TIME('10:00:00') as endTime
FROM CalendarTable
WHERE calendarDate >= :rangeStart
AND calendarDate < :rangeEnd
Nous pouvons conclure cela dans un sous-requête ou CTE, mais ce qui est des conditions réelles utilisées pour la jointure?
Eh bien, le problème est la vérification ou en ignorant le moment où la date est impliquée:
task_end_date > startDate OR (task_end_date = startDate AND task_end_time >= startTime)
... et pour la borne supérieure:
task_end_date < endDate OR (task_end_date = endDate AND task_end_time < endTime)
donc mettre tous ensemble ressemble à quelque chose comme ceci:
WITH QueryRange AS (SELECT calendarDate as startDate, CAST('10:00:00' as TIME) as startTime,
calendarDate + 1 DAY as endDate, CAST('10:00:00' as TIME) as endTime
FROM CalendarTable
WHERE calendarDate >= :startRange
AND calendarDate < :endRange)
SELECT QueryRange.startDate, QueryRange.startTime,
QueryRange.endDate, QueryRange.endTime,
TasksEnded.ended
FROM (SELECT QueryRange.startDate, COUNT(Tasks.task_name) as ended
FROM QueryRange
LEFT JOIN Tasks
ON (Tasks.task_end_date > QueryRange.startDate
OR (Tasks.task_end_date = QueryRange.startDate
AND Tasks.task_end_time >= QueryRange.startTime))
AND (Tasks.task_end_date < QueryRange.endDate
OR (Tasks.task_end_date = QueryRange.endDate
AND Tasks.task_end_time < QueryRange.endTime))
GROUP BY QueryRange.startDate) as TasksEnded
JOIN QueryRange
ON QueryRange.startDate = TasksEnded.startDate
ORDER BY QueryRange.startDate
Fiddle Example (Ignorer les modifications mineures à travailler un SGBDR différents, les principes sont solides.)
Comme une note de côté, cela est largement plus facile si vous avez réellement stocké la date/heure en une seule horodatage. En supposant que votre fichier calendrier traite encore que dans les dates (ce qui devrait), il suffit d'utiliser pour construire l'horodatage complet, au lieu des champs séparés:
SELECT TIMESTAMP(calendarDate, '10:00:00') as rangeStart
TIMESTAMP(calendarDate + 1 DAY, '10:00:00') as rangeEnd
FROM CalendarTable
WHERE calendarDate >= :rangeStart
AND calendarDate < :rangeEnd
... qui fait alors l'utilisation de requêtes seulement une paire de contrôles .
LEFT JOIN Tasks
ON Tasks.task_end_stamp >= QueryRange.rangeStart
AND Tasks.task_end_stamp < QueryRange.rangeEnd
.... et qui serait certainement plus rapide que le AND
/OR
mixte nécessaire avec les champs séparés. Donc, oui, vous pouvez toujours interroger et regrouper les sous-champs date, si vous construisez d'abord les horodatages à partir des données de départ.
it's db2 (connexion ODBC à AS400) –
Ma réaction immédiate à la conception de votre table est ... ne pas stocker les dates et les heures séparément. Cela causera seulement un gros mal de tête plus tard (en ce moment même). –
oui c'est vrai, mais même en utilisant la fonction TIMESTAMP je ne peux pas trouver la façon de choisir la période de temps entre les jours –