在第1天的time1和第2天之间选择SQL周期

时间:2017-10-11 11:32:08

标签: sql timestamp db2 between

我在DB2中有这样的表:

  • 任务名称
  • 日期格式
  • tast_end_date
  • task_end_time的时间格式

该表每天包含100条记录,我必须构建一个查询来计算从dayX的10:00:00到dayX + 1的09:59:59整个月结束的任务。

对我来说,结果应该是这样的,例如:

  • 期间01.01.2017时间10:00:00至02.01.2017时间09:59:59,结束25个任务
  • 期间02.01.2017时间10:00:00至03.01.2017时间09:59:59,结束25项任务
  • 期间03.01.2017时间10:00:00至04.01.2017时间09:59:59,结束25个任务

所以这不是一个简单的日期选择,我必须同时使用日期和时间范围... crrrrazy

我正在使用dbeaver进行选择。请求帮忙!

2 个答案:

答案 0 :(得分:0)

就个人而言,我建立时间戳然后进行调整,以便时间落入一个日历日。然后,这只是计算日期组的问题..

类似的东西

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)                                        

答案 1 :(得分:0)

假设您有一个日历表,如果您能够查询周/月/年(如果没有,则应创建一个)。使用WHEREGROUP BY子句中的函数(包括日期数学)会使索引的使用无效,这通常会导致查询速度变慢。相反,最好指定范围起点/终点,以便系统可以直接命中索引。

无论如何,我们可以为我们的日期数据添加时间!

好的,我们白天会尝试分组,对吧?

SELECT calendarDate as start
FROM CalendarTable
WHERE calendarDate >= :rangeStart
      AND calendarDate < :rangeEnd

......好吧,好吧,这让我们成为了开始,但是当查询它有助于结束下一个组的开始,或者真的,下一个组的开始时:

SELECT calendarDate as start, calendarDate + 1 DAY as end
FROM CalendarTable
WHERE calendarDate >= :rangeStart
      AND calendarDate < :rangeEnd

这就是日期......除了我们还需要增加时间!

幸运的是,这是一个恒定的值:

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

我们可以将它包装在子查询或CTE中,但是用于连接的实际条件是什么?

嗯,问题是检查或忽略涉及日期的时间:

task_end_date > startDate OR (task_end_date =  startDate AND task_end_time >= startTime)

......以及上限:

task_end_date < endDate OR (task_end_date = endDate AND task_end_time < endTime)

所以把它们放在一起看起来像这样:

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(忽略对其他RDBMS工作的微小更改,原则是合理的。)

作为旁注,如果您实际将日期/时间存储为单个时间戳,则会更加容易。假设您的日历文件仍然只处理日期(它应该),只需使用它来构建完整的时间戳,而不是分隔的字段:

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

...然后使查询只使用一对支票。

LEFT JOIN Tasks
       ON Tasks.task_end_stamp >= QueryRange.rangeStart 
          AND Tasks.task_end_stamp < QueryRange.rangeEnd

....几乎肯定会比分离字段所需的混合AND / OR更快。

所以,是的,如果你首先从起始数据构造时间戳,你仍然可以查询和按日期子字段分组。