如何解决Oracle查询过去12个月时出现的ORA-01839错误?
这个问题我之前也碰到过,Oracle的日期加减在处理月末最后几天的时候特别容易踩坑!就像你遇到的情况,当当前日期是29-DEC-17时,用原SQL计算会试图生成不存在的29-FEB-17,直接触发ORA-01839错误。下面给你几个实用的解决方案:
方法1:先截断到当月月初再计算
既然我们只需要月份标识(mon-yy),不需要具体的日,那可以先把当前日期截断到当月第一天,再做月份减法。这样不管当前是几号,计算出来的都是目标月份的第一天,绝对不会出现无效日期:
select to_char( add_months(trunc(sysdate, 'month'), -(level - 1)), 'mon-yy') as month from dual connect by level <= 12;
这里用trunc(sysdate, 'month')拿到当月1号,再通过add_months往前推对应月份数,最后格式化即可。这个方法最简单,完全满足你的需求。
方法2:基于当月最后一天计算
如果你需要确保得到的是每个月的最后一天(这个方法更通用,哪怕后续需要具体日期也能用),可以先用last_day函数获取当前月的最后一天,再用add_months往前偏移:
select to_char( add_months(last_day(sysdate), -(level - 1)), 'mon-yy') as month from dual connect by level <= 12;
last_day(sysdate)会自动识别当月的最后一天(比如非闰年2月的28号),add_months在处理最后一天时,会自动调整到目标月份的最后一天——比如从31-DEC-17往前推1个月,会得到30-NOV-17,完全不会出现日期无效的问题。
为什么原SQL会报错?
原SQL里trunc(sysdate)得到的是当天的具体日期(比如29-DEC-17),用numtoyminterval减月份时,Oracle会尝试寻找1个月前的同一天,但目标月份没有这个日期(比如非闰年2月没有29号),自然就抛出错误了。上面的方法要么避开了具体的日,要么用Oracle内置函数处理了月末的自动调整,完美解决这个问题。
内容的提问来源于stack exchange,提问作者Akhilesh

