使 oracle last_day 函数在 oracle 和 h2 之间兼容
Posted
技术标签:
【中文标题】使 oracle last_day 函数在 oracle 和 h2 之间兼容【英文标题】:Make oracle last_day function compatible between oracle and h2 【发布时间】:2019-09-04 15:58:22 【问题描述】:我在 java 项目中有一个 sql 查询。它必须在 oracle 和 h2 中执行,具体取决于客户端和 DBMS 选举。
它必须返回 sent_date 为 Month 减一的值(上个月的注册表)
SELECT * FROM TABLE WHERE SENT_DATE BETWEEN ADD_MONTHS(TRUNC(SYSDATE,'mm'),-1)
AND LAST_DAY(ADD_MONTHS(TRUNC(SYSDATE,'mm'),-1)) ORDER BY ELAPSED_TIME DESC
问题是,LAST_DAY 函数是 Oracle 特定的函数,在 H2 中不存在,并且 H2 日期管理有时在 Oracle 中不起作用,但我需要它完全兼容。之前有一个问题Make Oracle last_day function be compatible with H2 database,但答案集中在测试不佳的系统上,而不是正确的解决方案上。
我们怎样才能做到这一点?
【问题讨论】:
【参考方案1】:可能会更开心:
SELECT * FROM TABLE
WHERE SENT_DATE BETWEEN ADD_MONTHS(TRUNC(SYSDATE,'mm'),-1) AND TRUNC(SYSDATE,'mm') - 1
ORDER BY ELAPSED_TIME DESC
表明它们评估为相同的值:
SELECT ADD_MONTHS(TRUNC(SYSDATE,'mm'),-1) as orig_start,
LAST_DAY(ADD_MONTHS(TRUNC(SYSDATE,'mm'),-1)) as orig_end,
TRUNC(SYSDATE,'mm') - 1 as new_end
FROM dual;
ORIG_START ORIG_END NEW_END
------------------- ------------------- -------------------
2019-08-01 00:00:00 2019-08-31 00:00:00 2019-08-31 00:00:00
但请记住,between
包含在内,因此如果您的 sent_date
有非午夜时间,则将排除 2019-08-31 00:00:01 到 2019-08-31 23:59:59 之间的任何时间。
这样做更安全:
SELECT * FROM TABLE
WHERE SENT_DATE >= ADD_MONTHS(TRUNC(SYSDATE,'mm'),-1)
AND SENT_DATE < TRUNC(SYSDATE,'mm')
ORDER BY ELAPSED_TIME DESC
这将包括 2019 年 8 月 1 日 00:00:00 或之后的所有内容,直到(但不包括)2019 年 9 月 1 日 00:00:00 - 这样就不会出现缺失的日期。
或者如果它不能识别 add_months
或者:
SELECT * FROM TABLE
WHERE SENT_DATE >= TRUNC(SYSDATE,'mm') - INTERVAL '1' MONTH
AND SENT_DATE < TRUNC(SYSDATE,'mm')
ORDER BY ELAPSED_TIME DESC
(我无法测试这些中的任何一个是否真的在 H2 中工作... *8-)
【讨论】:
【参考方案2】:LAST_DAY(ADD_MONTHS(TRUNC(SYSDATE,'mm'),-1))
可以简化为
TRUNC(SYSDATE,'mm') - 1
没有LAST_DAY
函数,但是H2也不支持带有'mm'参数的非标准TRUNC
。
所以你也需要避免它。这样的查询将与两个数据库兼容(但是,您需要 H2 的最新版本,例如 1.4.199):
SELECT * FROM tableName WHERE SENT_DATE BETWEEN
CURRENT_DATE - (EXTRACT(DAY FROM CURRENT_DATE) - 1) - INTERVAL '1' MONTH
AND
CURRENT_DATE - EXTRACT(DAY FROM CURRENT_DATE)
ORDER BY ELAPSED_TIME DESC
请注意,这个查询不是标准的,我故意使用Oracle风格的日期算法来简化查询,H2支持它。如果你愿意,你也可以在这里使用非标准的ADD_MONTH
。
如果您只需要使用标准语法来兼容更多数据库,则可以避免使用 Oracle/H2 风格的算法:
SELECT * FROM tableName WHERE SENT_DATE BETWEEN
CURRENT_DATE - (EXTRACT(DAY FROM CURRENT_DATE) - 1) * INTERVAL '1' DAY - INTERVAL '1' MONTH
AND
CURRENT_DATE - EXTRACT(DAY FROM CURRENT_DATE) * INTERVAL '1' DAY
ORDER BY ELAPSED_TIME DESC
还请注意,Oracle 与其他数据库不同,没有正常的DATE
数据类型,它的DATE
类型可能有时间(H2 可以在Oracle 兼容模式下模拟这种偏差)。因此,所有这些带有“BETWEEN”的查询,包括您最初的仅 Oracle 查询,只有在您确保您的日期都在 00:00:00
时才能正常工作。如果您的日期还包含时间部分,您需要使用几个比较运算符:
SELECT * FROM tableName WHERE SENT_DATE >=
CURRENT_DATE - (EXTRACT(DAY FROM CURRENT_DATE) - 1) - INTERVAL '1' MONTH
AND SENT_DATE <
CURRENT_DATE - (EXTRACT(DAY FROM CURRENT_DATE) - 1)
ORDER BY ELAPSED_TIME DESC
(此处也使用了 Oracle/H2 算法,对于其他数据库,可能需要与 INTERVAL '1' DAY
相乘。)
【讨论】:
以上是关于使 oracle last_day 函数在 oracle 和 h2 之间兼容的主要内容,如果未能解决你的问题,请参考以下文章
oracle 日期常用函数 (ADD_MONTHS,LAST_DAY,NEXT_DAY,MONTHS_BETWEEN,NEW_TIME,ROUND,TRUNC)
Oracle - LAST_DAY 和 TRUNC - 执行顺序