2017-10-11 89 views
0

我在DB2中有一个表是这样的:SQL周期选择

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

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

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

  • 期2017年1月1日10:00:00时至2017年2月1日时间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进行选择。 thx求助!

+0

它的db2(与AS400的ODBC连接) –

+2

我对您的表格设计的直接反应是...不要单独存储日期和时间。这只会在以后引起很大的头痛(现在实际上)。 –

+0

是的它是真的,但即使使用TIMESTAMP功能,我也无法找到选择时间周期的方法 –

回答

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)           
+0

谢谢这个解决方案工作的很棒! –

+0

@YuriyRadchenko很高兴听到它。但请注意,这是一个简单的解决方案。性能可能不是最佳状态。该表达式将导致完整扫描;无论如何,如果你选择了大部分行,这可能就足够了。 – Charles

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更快。

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

+0

谢谢,但我需要像这样的全年结果: - 时间段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个任务 - 等等...... 无论如何,结果必须在每天的“第X天的10:00:00至第X + 1天的09:59:59”基础上。 –

+0

啊。我以为你的日期格式是MDY(这是美国通常的标准),而不是DMY。这就是为什么使用ISO(YMD)更好,并且毫不含糊。无论哪种方式,该技术不会改变,并且答案已经更新。 –