2017-10-11 3 views
0

J'ai une table comme ceci dans DB2:sélection de période SQL entre time1 de day1 et time2 day2

  • nom de la tâche
  • tast_end_date au format de date
  • task_end_time en format temps

La table contient 100s d'enregistrements par jour et je dois construire une requête pour compter les tâches terminées de 10:00:00 de dayX à 09:59:59 de dayX + 1 pour le mois entier.

Pour moi, le résultat devrait être comme celui-ci, par exemple:

  • période 01/01/2017 temps en temps 10:00:00 02/01/2017 09:59:59, terminé le 25 tâches
  • période 02.01 .2017 heure 10:00:00 à 03.01.2017 heure 09:59:59, terminé 25 tâches
  • période 03.01.2017 heure 10:00:00 à 04.01.2017 heure 09:59:59, terminée 25 tâches
  • etc.

Donc ce n'est pas une simple sélection de date, je dois utiliser la plage de jours et l'heure en même temps ... crrrrazy

Je suis en utilisant dbeaver pour sélectionner. Merci pour l'aide!

+0

it's db2 (connexion ODBC à AS400) –

+2

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). –

+0

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 –

Répondre

0

Personnellement, je construirais l'horodatage, puis l'ajusterais de sorte que le temps tombe dans un seul jour calendrier. Ensuite, il est juste une question de compter les groupes de dates ..

quelque chose comme si

with adj as (            
    select 
    timestamp(mydate, mytime)       
     - 9 hours - 59 minutes - 59 seconds - 1 microsecond 
    as ts 
from mytable             
)               
select month(ts), count(*) as nbr_tasks     
from adj             
group by month(ts)           
+0

merci cette solution fonctionne très bien !!! –

+0

@YuriyRadchenko heureux de l'entendre. Notez cependant que c'est une solution simple. La performance est probablement sous-optimale. L'expression provoquera un balayage complet; cela peut être assez bon si vous sélectionnez la plupart des lignes de toute façon. – Charles

0

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.

+0

merci mais j'ai besoin du résultat comme ceci pour toute l'année: - période 01.01.2017 heure 10:00:00 au 02.01.2017 heure 09:59:59, terminé 25 tâches - période 02.01.2017 heure 10:00 : 00 au 03.01.2017 heure 09:59:59, terminé 25 tâches - période 03.01.2017 heure 10:00:00 à 04.01.2017 heure 09:59:59, terminé 25 tâches - etc ... de toute façon, le résultat doit être sur une base "10:00:00 du jourX à 09:59:59 du jourX + 1" pour chaque jour –

+0

Ah. Je pensais que votre format de date était MDY (qui est la norme habituelle ici aux États-Unis), pas DMY. C'est pourquoi il est préférable d'utiliser ISO (AMJ), et soyez sans ambiguïté. De toute façon, la technique ne change pas, et la réponse a été mise à jour. –