使 oracle last_day 函数在 oracle 和 h2 之间兼容
Make oracle last_day function compatible between oracle and h2
我在 java 项目中有一个 sql 查询。它必须在 oracle 和 h2 中执行,具体取决于客户端和它的 DBMS 选择。
它必须 return 值,其中 sent_date 是月份减一(上个月注册表)
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 但答案集中在一个测试不佳的系统上,而不是一个正确的解决方案。
我们怎样才能做到这一点?
它可能更快乐:
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-08-01 00:00:00 或之后的所有内容,直至(但不包括)2019-09-01 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-)
LAST_DAY(ADD_MONTHS(TRUNC(SYSDATE,'mm'),-1))
可以简化为
TRUNC(SYSDATE,'mm') - 1
没有 LAST_DAY
功能,但 H2 也不支持非标准 TRUNC
和 'mm' 参数。
所以你也需要避免它。这样的查询将与两个数据库兼容(但是,您需要最新版本的 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-style 算术:
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-only 查询,只有在您确保所有日期都在 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
可能是必要的。)
我在 java 项目中有一个 sql 查询。它必须在 oracle 和 h2 中执行,具体取决于客户端和它的 DBMS 选择。
它必须 return 值,其中 sent_date 是月份减一(上个月注册表)
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 但答案集中在一个测试不佳的系统上,而不是一个正确的解决方案。
我们怎样才能做到这一点?
它可能更快乐:
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-08-01 00:00:00 或之后的所有内容,直至(但不包括)2019-09-01 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-)
LAST_DAY(ADD_MONTHS(TRUNC(SYSDATE,'mm'),-1))
可以简化为
TRUNC(SYSDATE,'mm') - 1
没有 LAST_DAY
功能,但 H2 也不支持非标准 TRUNC
和 'mm' 参数。
所以你也需要避免它。这样的查询将与两个数据库兼容(但是,您需要最新版本的 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-style 算术:
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-only 查询,只有在您确保所有日期都在 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
可能是必要的。)