SQL周期选择
我在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求助!
就我个人而言,我会建立时间戳,然后调整它,以便时间落入一个日历日。然后,它只是一个计算日期组的事..
像这样
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)
谢谢这个解决方案工作的很棒! –
@YuriyRadchenko很高兴听到它。但请注意,这是一个简单的解决方案。性能可能不是最佳状态。该表达式将导致完整扫描;无论如何,如果你选择了大部分行,这可能就足够了。 – Charles
想必你有一个日历表,如果你能够在周/月/年查询(如果不是,你应该创建一个)。使用WHERE
和GROUP 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
更快。
所以是的,如果你首先从起始数据构造时间戳,你仍然可以查询和分组日期子字段。
谢谢,但我需要像这样的全年结果: - 时间段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”基础上。 –
啊。我以为你的日期格式是MDY(这是美国通常的标准),而不是DMY。这就是为什么使用ISO(YMD)更好,并且毫不含糊。无论哪种方式,该技术不会改变,并且答案已经更新。 –
它的db2(与AS400的ODBC连接) –
我对您的表格设计的直接反应是...不要单独存储日期和时间。这只会在以后引起很大的头痛(现在实际上)。 –
是的它是真的,但即使使用TIMESTAMP功能,我也无法找到选择时间周期的方法 –