我使用TO_CHAR
函数格式化从0001
到9999
的数字,并使列大小(VARCHAR2(4)
)适合插入值(即使值是> 9999)。
我使用这样的功能:
TO_CHAR(n, 'FM0000')
有效的例子:
SELECT TO_CHAR(1, 'FM0000') FROM DUAL;
结果:0001
SELECT TO_CHAR(1234, 'FM0000') FROM DUAL;
结果:1234
但是当我使用大于9999的值测试时,我会得到一个额外的字符:
SELECT TO_CHAR(12345, 'FM0000') FROM DUAL;
结果:#####
SELECT TO_CHAR(123456, 'FM0000') FROM DUAL;
结果:#####
有关信息,我预期的结果是####
(在4个字符上)。
总结一下:
如何解释这个?
我没有在Oracle文档https://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm#i170559
中找到解释我尝试了几个Oracle版本(9,10,11),结果是一样的。
我找到的 解决方法是使用RPAD()函数RPAD(TO_CHAR(n,'FM0000'), 4)
截断结果,但我需要理解为什么TO_CHAR函数不够。
答案 0 :(得分:11)
您的格式模型仍然必须允许值的符号。没有办法向TO_CHAR()
表明它永远不会是负面的(如果你的价值实际上就是这种情况)。即使使用4位数字,格式化允许允许五个字符,您可以从列标题中看到:
SQL> SELECT TO_CHAR(1234, 'FM0000') FROM DUAL;
TO_CH
-----
1234
注意列标题是TO_CH
,它是五个字符,而不是四个字符。如果您有一个负数(如Florin所建议的那样),您需要额外的空间:
SQL> SELECT TO_CHAR(-1234, 'FM0000') FROM DUAL;
TO_CH
-----
-1234
如果没有FM
修饰符,您会在返回的字符串中获得正值的前导空格,因此LENGTH(TO_CHAR(1234, '0000'))
为5但LENGTH(TO_CHAR(1234, 'FM0000'))
为4,因为前导空格(通常会使列右对齐的值被抑制。如果为负值,则返回字符串的长度为5。格式模型确定返回的数据类型为varchar2(5)
到允许用于符号,即使您知道永远不会有负值 - 格式模型也没有任何方法可以反映出来。
如果你强制显示标志,你也可以看到正值:
SQL> SELECT TO_CHAR(1234, 'FMS0000') FROM DUAL;
TO_CH
-----
+1234
在TO_CHAR
电话中,您无法做任何事情。作为RPAD
解决方法的替代方法,您可以使用SUBSTR
仅获取格式化字符串的最后四个字符:
SQL> SELECT SUBSTR(TO_CHAR(12345, 'FM0000'), -4) FROM DUAL
SUBSTR(TO_CHAR(1
----------------
####
但如果你确实有负值,你就会失去这个标志:
SQL> SELECT SUBSTR(TO_CHAR(-1234, 'FM0000'), -4) FROM DUAL
SUBSTR(TO_CHAR(-
----------------
1234
使用你的RPAD,你保留了标志,但失去了第四位数字:
SQL> SELECT RPAD(TO_CHAR(-1234, 'FM0000'), 4) FROM DUAL
RPAD(TO_CHAR(-12
----------------
-123
这也不好。你可能不必处理负数;但如果您处理的数字大于预期(即,当您只期望< = 9999时,您得到的数字> = 10000)那么我不确定您是否可以确定您不会看到(无效?)在某些时候的负数。无论如何,这似乎是一个数据问题,而不是格式问题。
根据你对Ollie的评论,另一种可能对代码的未来维护者更明确和明显的方法是在CASE中拼写出来:
SELECT CASE WHEN n BETWEEN 0 AND 9999 THEN TO_CHAR(n, 'FM0000') ELSE '####' END FROM DUAL
如果您愿意,还可以将字符串列留空或使用其他魔法值而不是####
。
另一种修剪价值的方法,即可能也更清晰,就是CAST:
SQL> SELECT CAST(TO_CHAR(12345, 'FM0000') AS VARCHAR2(4)) FROM DUAL;
CAST
----
####