【问题标题】:Make oracle last_day function compatible between oracle and h2使 oracle last_day 函数在 oracle 和 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,但答案集中在测试不佳的系统上,而不是正确的解决方案上。

我们怎样才能做到这一点?

【问题讨论】:

    标签: java oracle h2


    【解决方案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 相乘。)

      【讨论】:

        猜你喜欢
        • 2017-05-13
        • 2013-12-11
        • 2014-02-20
        • 1970-01-01
        • 2018-03-03
        • 2020-06-21
        • 2021-10-02
        • 2017-05-23
        • 1970-01-01
        相关资源
        最近更新 更多