我在与TO_DATE参数一起使用时,发现了oracle format_mask函数的奇怪行为。
基本上,我看到的是,在一种情况下,它忽略给定的格式掩码,并使用自己的掩码解析输入,在其他情况下,它会抛出异常。
示例I 预期行为 - 抛出错误:
SELECT TO_DATE('18-02-2016', 'DD/MON/YYYY') FROM dual
ORA-01843:不是有效月份
示例II 意外行为 - 解析日期:
SELECT TO_DATE('18-feb-2016', 'DD/MM/YYYY') FROM dual
2016年2月18日00:00:00
我在文档中看不出任何这样的评论,所以我想知道这种不妥之处是设计还是错误或者我不理解正确的事情?
修改 看看答案我可以同意它很可能是设计的。但是在这里所做的事情对我来说是危险的“自动化”。
如果格式将被错误地解释(由oracle猜测)怎么办?有没有关于这里究竟发生了什么的文件,所以我可以肯定它是安全的吗?
我的问题是 - 我可以把它关掉吗?我唯一的选择是自己验证格式吗?
答案 0 :(得分:18)
请参阅此处的表格:https://docs.oracle.com/cd/B28359_01/server.111/b28286/sql_elements004.htm#g195479
它是Datetime格式模型的String-To-Date Conversion Rules部分。对于MM
,如果没有匹配项,则会尝试MON
和MONTH
。同样,如果您指定MON
但未找到,则尝试MONTH
。如果您指定MONTH
并且找不到,则会尝试MON
,但除了MM
之外,它永远不会尝试MM
。
回答问题:Can I turn it off?
答案是肯定的。
您可以指定FX
作为格式化的一部分。
SELECT TO_DATE('18/february/2016', 'FXDD/MM/YYYY') FROM dual;
现在返回:
[错误]执行(4:16):ORA-01858:非数字字符 找到了预期数字的位置
以下内容:
SELECT TO_DATE('18/02/2016', 'FXDD/MM/YYYY') FROM dual;
返回预期的:
2016年2月18日
请注意,在指定FX
时,必须使用正确的分隔符,否则会出错。
答案 1 :(得分:4)
这是设计的。即使不符合定义的掩码,Oracle也会尝试在字符串中查找确定性日期表示。只有在找不到确定日期或日期的某些必要组件丢失或未解决时才会抛出错误。
答案 2 :(得分:2)
我认为02
可能意味着日期/月/年的任何事情,但feb
只意味着一件事,month
。
我也在11g
中对此进行了交叉检查,并获得了与12c
中相同的输出。所以已经通过oracle设计。
答案 3 :(得分:2)
如果您想要禁用Oracle尝试提供帮助并解释不完全匹配的内容,可以使用the FX format modifier:
<强> FX 强>
格式准确。此修饰符指定TO_DATE函数的字符参数和日期时间格式模型的精确匹配:
- 字符参数中的标点符号和引用文本必须与格式模型的相应部分完全匹配(例外情况除外)。
- 字符参数不能有额外的空格。如果没有FX,Oracle会忽略额外的空白。
- 字符参数中的数字数据必须与格式模型中的相应元素具有相同的位数。没有FX,字符参数中的数字可以省略前导零。
- 启用FX后,您也可以使用FM修改器禁用此前导零的检查。
所以你的例子会出错:
SELECT TO_DATE('18-feb-2016', 'FXDD/MM/YYYY') FROM dual;
SQL Error: ORA-01858: a non-numeric character was found where a numeric was expected
这也需要分隔符完全匹配:
SELECT TO_DATE('18-02-2016', 'FXDD/MM/YYYY') FROM dual;
SQL Error: ORA-01861: literal does not match format string
如果你做得对,那就没关系:
SELECT TO_DATE('18/02/2016', 'FXDD/MM/YYYY') FROM dual;
SELECT TO_DATE('18-feb-2016', 'FXDD-MON-YYYY') FROM dual;
它没有抱怨第二个例子中的不同情况。但如果省略前导零,它会抱怨;
SELECT TO_DATE('18/2/2016', 'FXDD/MM/YYYY') FROM dual;
SQL Error: ORA-01862: the numeric value does not match the length of the format item
...除非你还包含FM修饰符:
SELECT TO_DATE('18/2/2016', 'FMFXDD/MM/YYYY') FROM dual;