如何将 Informix 日期时间按半小时、四分之一和小时四舍五入?
Posted
技术标签:
【中文标题】如何将 Informix 日期时间按半小时、四分之一和小时四舍五入?【英文标题】:How to round Informix datetime by half hour, quarter and hour? 【发布时间】:2016-05-07 05:25:03 【问题描述】:这在 SQL Server 中很容易做到,而 Informix 让我很生气。 显示日期时间所在的小时很容易:
select startdatetime,
startdatetime::DATETIME HOUR TO HOUR AS IntervalHour
from Contactcalldetail ccd
给予:
startdatetime IntervalHour 04-05-2016 19:53:35 19
我想要:
startdatetime IntervalHour IntervalHalfHour IntervalQuarterHour 04-05-2016 19:53:35 19 19:30 19:45 04-05-2016 19:56:57 19 19:30 19:45 04-05-2016 20:23:14 20 20:00 20:15
到目前为止,我已经尝试过....在我的屏幕上咒骂,告诉谷歌去可怕的地方,因为它给我的结果不包括“informix”这个词。
还有什么可以尝试的想法?
【问题讨论】:
你没有四舍五入。你正在地板/截断。例如,将 20:23 四舍五入到一刻钟将得到 20:30,而不是 20:15。请澄清。 好点,我认为地板是更好的术语。我想下到最近的区间 【参考方案1】:也许对你有帮助。
select startdatetime
, startdatetime::DATETIME HOUR TO HOUR AS IntervalHour
, Case when DatePart(minute,startdatetime)>=30 then cast(DatePart(hour,startdatetime) as varchar)+':30'
else cast(DatePart(hour,startdatetime) as varchar)+':00'
End
as IntervalHalfHour
, Case when DatePart(minute,startdatetime)>=45 then cast(DatePart(hour,startdatetime) as varchar)+':45'
when DatePart(minute,startdatetime)>=15 then cast(DatePart(hour,startdatetime) as varchar)+':15'
else cast(DatePart(hour,startdatetime) as varchar)+':00'
End
as IntervalQuarterHour
from Contactcalldetail ccd
【讨论】:
我兴奋了一会儿,然后看到不能与 Informix 一起使用的“DatePart”:(【参考方案2】:虽然其他答案中显示的折磨人的 CASE 陈述显然会起作用,但这并不是唯一的解决方案。即使您坚持使用它,将这个逻辑封装在 FUNCTION (SPL) 中也会使代码更易于重用,如果它会出现在大量代码中。
但我建议集合论是数据库最擅长的,并且有更好的方法:
创建一个包含您需要的所有范围的表格 - 这将是完全静态的,如果四分之一小时是您需要的最细粒度,那么您将拥有 24 * 4 = 96 行。它看起来像这样:
from_intvl | to_intvl | intvl_qtr | intvl_hlf | intvl_hr
-----------+----------+-----------+-----------+---------
00:00:00 | 00:14:59 | 00:00 | 00:00 | 0
00:15:00 | 00:29:59 | 00:15 | 00:00 | 0
...
08:30:00 | 08:44:59 | 08:30 | 08:30 | 8
...
22:45:00 | 22:59:59 | 22:45 | 22:30 | 22
...
该表中的前两列是 DATETIME HOUR TO SECOND
,其余列可以是适当粒度的 DATETIME
,或者 CHAR、INT 或任何适合您的应用程序的列。
使用起来很简单:
SELECT ccd.startdatetime, it.intvl_qtr, it.intvl_hlf, it.intvl_hr
FROM contactcalldetail AS ccd
JOIN intvl AS it
ON ccd.startdatetime::DATETIME HOUR TO SECOND BETWEEN it.from_intvl AND it.to_intvl
如果您后来决定需要 5 分钟范围、20 分钟范围,甚至 4 小时块,则向查找表添加更多行或更多列是微不足道的。即使细化到单独的分钟记录,仍然只能生成一个包含 1,440 行的表。
【讨论】:
【参考方案3】:我会创建一个存储过程来完成这项工作。虽然可以在具有足够强制转换的表达式中完成,但这会令人不快。关键技巧是将一小时后的分钟数转换为CHAR(2)
字符串,然后可以自动转换为数字。 AFAIK,没有其他方法可以将区间转换为数字。
DROP PROCEDURE IF EXISTS Multiple_Of_Quarter_Hour;
CREATE PROCEDURE Multiple_Of_Quarter_Hour(dtval DATETIME YEAR TO MINUTE
DEFAULT CURRENT YEAR TO MINUTE)
RETURNING DATETIME YEAR TO MINUTE AS rounded_value;
DEFINE mm CHAR(2);
LET mm = EXTEND(dtval, MINUTE TO MINUTE);
LET dtval = dtval - mm UNITS MINUTE;
RETURN dtval + (15 * (mm / 15)::INTEGER) UNITS MINUTE;
END PROCEDURE;
DROP PROCEDURE IF EXISTS Multiple_Of_N_Minutes;
CREATE PROCEDURE Multiple_Of_N_Minutes(dtval DATETIME YEAR TO MINUTE
DEFAULT CURRENT YEAR TO MINUTE,
n_min INTEGER DEFAULT 15)
RETURNING DATETIME YEAR TO MINUTE AS rounded_value;
DEFINE mm CHAR(2);
LET mm = EXTEND(dtval, MINUTE TO MINUTE);
LET dtval = dtval - mm UNITS MINUTE;
RETURN dtval + (n_min * (mm / n_min)::INTEGER) UNITS MINUTE;
END PROCEDURE;
测试代码:
CREATE TEMP TABLE t_times (dtval DATETIME YEAR TO SECOND PRIMARY KEY);
INSERT INTO t_times VALUES('2016-05-27 00:00:00');
INSERT INTO t_times VALUES('2016-05-27 00:04:59');
INSERT INTO t_times VALUES('2016-05-27 00:06:59');
INSERT INTO t_times VALUES('2016-05-27 00:14:59');
INSERT INTO t_times VALUES('2016-05-27 00:15:00');
INSERT INTO t_times VALUES('2016-05-27 00:16:57');
INSERT INTO t_times VALUES('2016-05-27 00:21:00');
INSERT INTO t_times VALUES('2016-05-27 00:24:36');
INSERT INTO t_times VALUES('2016-05-27 00:25:11');
INSERT INTO t_times VALUES('2016-05-27 00:29:59');
INSERT INTO t_times VALUES('2016-05-27 00:30:00');
INSERT INTO t_times VALUES('2016-05-27 00:35:44');
INSERT INTO t_times VALUES('2016-05-27 00:44:59');
INSERT INTO t_times VALUES('2016-05-27 00:45:00');
INSERT INTO t_times VALUES('2016-05-27 00:49:53');
INSERT INTO t_times VALUES('2016-05-27 00:50:30');
INSERT INTO t_times VALUES('2016-05-27 00:59:59');
INSERT INTO t_times VALUES('2016-05-27 01:16:07');
INSERT INTO t_times VALUES('2016-05-27 01:34:10');
INSERT INTO t_times VALUES('2016-05-27 02:24:46');
INSERT INTO t_times VALUES('2016-05-27 04:32:08');
INSERT INTO t_times VALUES('2016-05-27 11:52:09');
INSERT INTO t_times VALUES('2016-05-27 14:00:28');
INSERT INTO t_times VALUES('2016-05-27 16:10:31');
INSERT INTO t_times VALUES('2016-05-27 17:46:58');
INSERT INTO t_times VALUES('2016-05-27 19:25:35');
INSERT INTO t_times VALUES('2016-05-27 22:52:48');
SELECT dtval,
Multiple_Of_Quarter_Hour(dtval) AS m15a,
EXTEND(Multiple_Of_N_Minutes(dtval, 2), HOUR TO MINUTE) AS m02,
EXTEND(Multiple_Of_N_Minutes(dtval, 3), HOUR TO MINUTE) AS m03,
EXTEND(Multiple_Of_N_Minutes(dtval, 4), HOUR TO MINUTE) AS m04,
EXTEND(Multiple_Of_N_Minutes(dtval, 5), HOUR TO MINUTE) AS m05,
EXTEND(Multiple_Of_N_Minutes(dtval, 6), HOUR TO MINUTE) AS m06
FROM t_times
ORDER BY dtval;
SELECT dtval,
EXTEND(Multiple_Of_N_Minutes(dtval, 10), HOUR TO MINUTE) AS m10,
EXTEND(Multiple_Of_N_Minutes(dtval, 12), HOUR TO MINUTE) AS m12,
EXTEND(Multiple_Of_N_Minutes(dtval, 15), HOUR TO MINUTE) AS m15b,
EXTEND(Multiple_Of_N_Minutes(dtval, 20), HOUR TO MINUTE) AS m20,
EXTEND(Multiple_Of_N_Minutes(dtval, 30), HOUR TO MINUTE) AS m30,
EXTEND(Multiple_Of_N_Minutes(dtval), HOUR TO MINUTE) AS m15c
FROM t_times
ORDER BY dtval;
示例输出:
dtval m15a m02 m03 m04 m05 m06
2016-05-27 00:00:00 2016-05-27 00:00 00:00 00:00 00:00 00:00 00:00
2016-05-27 00:04:59 2016-05-27 00:00 00:04 00:03 00:04 00:00 00:00
2016-05-27 00:06:59 2016-05-27 00:00 00:06 00:06 00:04 00:05 00:06
2016-05-27 00:14:59 2016-05-27 00:00 00:14 00:12 00:12 00:10 00:12
2016-05-27 00:15:00 2016-05-27 00:15 00:14 00:15 00:12 00:15 00:12
2016-05-27 00:16:57 2016-05-27 00:15 00:16 00:15 00:16 00:15 00:12
2016-05-27 00:21:00 2016-05-27 00:15 00:20 00:21 00:20 00:20 00:18
2016-05-27 00:24:36 2016-05-27 00:15 00:24 00:24 00:24 00:20 00:24
2016-05-27 00:25:11 2016-05-27 00:15 00:24 00:24 00:24 00:25 00:24
2016-05-27 00:29:59 2016-05-27 00:15 00:28 00:27 00:28 00:25 00:24
2016-05-27 00:30:00 2016-05-27 00:30 00:30 00:30 00:28 00:30 00:30
2016-05-27 00:35:44 2016-05-27 00:30 00:34 00:33 00:32 00:35 00:30
2016-05-27 00:44:59 2016-05-27 00:30 00:44 00:42 00:44 00:40 00:42
2016-05-27 00:45:00 2016-05-27 00:45 00:44 00:45 00:44 00:45 00:42
2016-05-27 00:49:53 2016-05-27 00:45 00:48 00:48 00:48 00:45 00:48
2016-05-27 00:50:30 2016-05-27 00:45 00:50 00:48 00:48 00:50 00:48
2016-05-27 00:59:59 2016-05-27 00:45 00:58 00:57 00:56 00:55 00:54
2016-05-27 01:16:07 2016-05-27 01:15 01:16 01:15 01:16 01:15 01:12
2016-05-27 01:34:10 2016-05-27 01:30 01:34 01:33 01:32 01:30 01:30
2016-05-27 02:24:46 2016-05-27 02:15 02:24 02:24 02:24 02:20 02:24
2016-05-27 04:32:08 2016-05-27 04:30 04:32 04:30 04:32 04:30 04:30
2016-05-27 11:52:09 2016-05-27 11:45 11:52 11:51 11:52 11:50 11:48
2016-05-27 14:00:28 2016-05-27 14:00 14:00 14:00 14:00 14:00 14:00
2016-05-27 16:10:31 2016-05-27 16:00 16:10 16:09 16:08 16:10 16:06
2016-05-27 17:46:58 2016-05-27 17:45 17:46 17:45 17:44 17:45 17:42
2016-05-27 19:25:35 2016-05-27 19:15 19:24 19:24 19:24 19:25 19:24
2016-05-27 22:52:48 2016-05-27 22:45 22:52 22:51 22:52 22:50 22:48
dtval m10 m12 m15b m20 m30 m15c
2016-05-27 00:00:00 00:00 00:00 00:00 00:00 00:00 00:00
2016-05-27 00:04:59 00:00 00:00 00:00 00:00 00:00 00:00
2016-05-27 00:06:59 00:00 00:00 00:00 00:00 00:00 00:00
2016-05-27 00:14:59 00:10 00:12 00:00 00:00 00:00 00:00
2016-05-27 00:15:00 00:10 00:12 00:15 00:00 00:00 00:15
2016-05-27 00:16:57 00:10 00:12 00:15 00:00 00:00 00:15
2016-05-27 00:21:00 00:20 00:12 00:15 00:20 00:00 00:15
2016-05-27 00:24:36 00:20 00:24 00:15 00:20 00:00 00:15
2016-05-27 00:25:11 00:20 00:24 00:15 00:20 00:00 00:15
2016-05-27 00:29:59 00:20 00:24 00:15 00:20 00:00 00:15
2016-05-27 00:30:00 00:30 00:24 00:30 00:20 00:30 00:30
2016-05-27 00:35:44 00:30 00:24 00:30 00:20 00:30 00:30
2016-05-27 00:44:59 00:40 00:36 00:30 00:40 00:30 00:30
2016-05-27 00:45:00 00:40 00:36 00:45 00:40 00:30 00:45
2016-05-27 00:49:53 00:40 00:48 00:45 00:40 00:30 00:45
2016-05-27 00:50:30 00:50 00:48 00:45 00:40 00:30 00:45
2016-05-27 00:59:59 00:50 00:48 00:45 00:40 00:30 00:45
2016-05-27 01:16:07 01:10 01:12 01:15 01:00 01:00 01:15
2016-05-27 01:34:10 01:30 01:24 01:30 01:20 01:30 01:30
2016-05-27 02:24:46 02:20 02:24 02:15 02:20 02:00 02:15
2016-05-27 04:32:08 04:30 04:24 04:30 04:20 04:30 04:30
2016-05-27 11:52:09 11:50 11:48 11:45 11:40 11:30 11:45
2016-05-27 14:00:28 14:00 14:00 14:00 14:00 14:00 14:00
2016-05-27 16:10:31 16:10 16:00 16:00 16:00 16:00 16:00
2016-05-27 17:46:58 17:40 17:36 17:45 17:40 17:30 17:45
2016-05-27 19:25:35 19:20 19:24 19:15 19:20 19:00 19:15
2016-05-27 22:52:48 22:50 22:48 22:45 22:40 22:30 22:45
显然,Multiple_Of_Quarter_Hour()
可以写成Multiple_Of_N_Minutes()
的简单封面。
单个表达式:
SELECT dtval,
(EXTEND(dtval, YEAR TO MINUTE) -
(EXTEND(dtval, MINUTE TO MINUTE)::CHAR(2)) UNITS MINUTE) +
(15 * ((EXTEND(dtval, MINUTE TO MINUTE)::CHAR(2)) / 15)::INTEGER) UNITS MINUTE
FROM t_times
ORDER BY dtval;
如果您必须多次编写该代码,那么不使用存储过程将是可笑的。
虽然有点棘手,但也可以舍入到最接近的 N 分钟倍数,而不是总是截断(例如,如果间隔是 10 分钟,则时间从 13:55:00 到 14:04 :59 将全部转换为 14:00)。
DROP PROCEDURE IF EXISTS Nearest_Multiple_Of_N_Minutes;
CREATE PROCEDURE Nearest_Multiple_Of_N_Minutes(dtval DATETIME YEAR TO SECOND
DEFAULT CURRENT YEAR TO SECOND,
n_min INTEGER DEFAULT 15)
RETURNING DATETIME YEAR TO MINUTE AS rounded_value;
DEFINE mm CHAR(2);
DEFINE dt_yy_mm DATETIME YEAR TO MINUTE;
LET dt_yy_mm = dtval + ((30 * n_min) UNITS SECOND);
LET mm = EXTEND(dt_yy_mm, MINUTE TO MINUTE);
LET dt_yy_mm = dt_yy_mm - mm UNITS MINUTE;
RETURN dt_yy_mm + (n_min * (mm / n_min)::INTEGER) UNITS MINUTE;
END PROCEDURE;
SELECT dtval,
Nearest_Multiple_Of_N_Minutes(dtval, 10) AS m10,
Nearest_Multiple_Of_N_Minutes(dtval, 15) AS m15,
Nearest_Multiple_Of_N_Minutes(dtval, 20) AS m20,
Nearest_Multiple_Of_N_Minutes(dtval, 30) AS m30
FROM t_times
ORDER BY dtval;
注意函数时间参数类型的变化。
【讨论】:
【参考方案4】:我想通了,感谢 shamim reza 给了我一些关于逻辑的想法:
TO_CHAR(startdatetime::DATETIME HOUR TO HOUR, '%H')||':00' AS IntervalHour,
Case when startdatetime::datetime minute to minute::char(2)::int >=30 then TO_CHAR(startdatetime::DATETIME HOUR TO HOUR, '%H')||':30'
else TO_CHAR(startdatetime::DATETIME HOUR TO HOUR, '%H')||':00'
End as IntervalHalfHour,
Case when startdatetime::datetime minute to minute::char(2)::int >=45 then TO_CHAR(startdatetime::DATETIME HOUR TO HOUR, '%H')||':45'
when startdatetime::datetime minute to minute::char(2)::int >=30 then TO_CHAR(startdatetime::DATETIME HOUR TO HOUR, '%H')||':30'
when startdatetime::datetime minute to minute::char(2)::int >=15 then TO_CHAR(startdatetime::DATETIME HOUR TO HOUR, '%H')||':15'
else TO_CHAR(startdatetime::DATETIME HOUR TO HOUR, '%H')||':00'
【讨论】:
【参考方案5】:只是另一种方式。
SELECT
startdatetime,
IntervalHour,
IntervalHour + (minutes/30)::INT * 30 UNITS MINUTE,
IntervalHour + (minutes/15)::INT * 15 UNITS MINUTE
FROM (
SELECT
startdatetime,
EXTEND(TRUNC(startdatetime, 'HH'), HOUR TO MINUTE) AS IntervalHour,
TO_CHAR(startdatetime, '%M') AS minutes
FROM contactcalldetail
)
基本上,您可以通过使用TRUNC 函数将日期截断到小时的开头来获得IntervalHour
。并使用EXTEND函数调整DATETIME
的精度。
不要使用 ROUND 函数,因为它会将日期四舍五入到最接近的小时或分钟的开头。
使用TO_CHAR 函数提取分钟,并使用UNITS operator 的简单数学运算来获得较低的间隔,不需要CASE。
【讨论】:
以上是关于如何将 Informix 日期时间按半小时、四分之一和小时四舍五入?的主要内容,如果未能解决你的问题,请参考以下文章
Android studio中如何设置控件水平四分之一为绿色,四分之三为灰色,如下图