今天遇到了一個很有意思的NUMBER類型Scale引發的問題,我用一個簡單的測試用例來展示一下這個案例。假如有個TEST的表,有個字段類型為NUMBER,我插入下面兩條數據
CREATE TABLE TEST
(
Category VARCHAR(12),
QTY NUMBER
)
INSERT INTO TEST
SELECT 'M', 12 FROM DUAL UNION ALL
SELECT 'C', 0.99999999999999999 FROM DUAL;
COMMIT;
此時直接查詢表TEST,發現QTY字段值為1,
使用下面SQL語句統計時,SUM_QTY的值也是1
但是如果在游標裡面獲取該值的時候,你會發現字段QTY的值為原來的值.99999999999999999
DECLARE CURSOR c_test
IS
SELECT Category, SUM(QTY) AS SUM_QTY FROM TEST
GROUP BY Category;
c_row c_test%rowtype;
begin
for c_row in c_test loop
dbms_output.put_line('the result is ' || c_row.SUM_QTY);
end loop;
end;
the result is .99999999999999999
the result is 12
為什麼會有這個奇怪的現象呢? 其實我們遇到這個案例時是按這個順序反著的,最後發現是插入的值是0.999999999. 當然這個過程是非常糾結的。遠非我們例子裡面那樣輕松簡單。要解釋這個問題,要從NUMBER類型說起,NUMBER (p, s) 聲明一個定點數 p(precision)為精度,s(scale)表示小數點右邊的數字個數,精度最大值為38,scale的取值范圍為-84到127。Number(p) 表示聲明一個整數相當於Number(p, 0), 如果不指定p和s,NUMBER類型,它的默認精度值為38, 默認的scale值為0. 所以出現在SELECT語句中,一個值為.99999999999999999 的顯示為1,但是在游標中,它獲取的是這個字段的真實值,沒有經過轉化。所以出現了這個稀奇古怪的問題。雖然事後理順過後覺得非常簡單,但是當時不了解情況下,覺得非常不可思議,非常納悶!
另外附上定點數的精度(p)和刻度(s)遵循以下規則:
當一個數的整數部分的長度 > p-s 時,Oracle就會報錯
當一個數的小數部分的長度 > s 時,Oracle就會捨入。
當s(scale)為負數時,Oracle就對小數點左邊的s個數字進行捨入。
當s > p 時, p表示小數點後第s位向左最多可以有多少位數字,如果大於p則Oracle報錯,小數點後s位向右的數字被捨入