如何将 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中如何设置控件水平四分之一为绿色,四分之三为灰色,如下图

在python中按半小时间隔对文件列表进行分组

sql按半小时统计

sql语句! 8小时为一天! 半小时为准,分钟不满半小时按半小时算!

如何在 Informix 中转换插入日期时间的格式?

请问四分之三圆怎么画。