2016-06-12 51 views
0

我發現了一個錯誤,我不知道爲什麼:PL/SQL:數字或值錯誤

ORA-06502: PL/SQL: numeric or value error 
ORA-06512: at "SYS.STANDARD", line 394 
ORA-06512: at "DOMINOS.DISTANCE", line 10 
ORA-06512: at "DOMINOS.ZOEKWINKELVOORADRES", line 19 
ORA-06512: at line 5 

光標應該包含145行。當我執行該過程時,我在54行之後得到上面的錯誤消息。

create or replace procedure zoekWinkelVoorAdres 
    (v_postcode in postcode.postcode%type, 
     v_huisnr in WINKEL.HUISNR%type, 
     v_id out WINKEL.ID%type, 
     v_afstand out number) 
is 
    type lat_array is varray(100000) of POSTCODE.LAT%type; 
    type lon_array is varray(100000) of POSTCODE.LON%type; 
    type id_array is varray(100000) of winkel.id%type; 
    a_lat lat_array; 
    a_lon lon_array; 
    a_id id_array; 
    latwin postcode.lat%type; 
    lonwin postcode.lon%type; 
    latklant postcode.lat%type; 
    lonklant postcode.lon%type; 
    vafstand number(38); 
    cursor winkelafstand is 
     select w.ID, p.lat, p.lon 
     from winkel w 
     join postcode p 
     on w.POSTCODE_ID_FK = p.POSTCODE_ID; 
begin 
    select lat, lon into latklant,lonklant 
    from postcode 
    where postcode = v_postcode; 
    open winkelafstand; 
    fetch winkelafstand bulk collect into a_id, a_lat, a_lon; 
    close winkelafstand; 
    for i in a_lat.first..a_lat.last loop 
     vafstand := distance(a_lat(i),a_lon(i),latklant,lonklant); 
     dbms_output.put_line(vafstand || ' ' || a_id(i)); 
     insert into winkel_afstand 
      (Winkel_ID, afstand) values(a_id(i),vafstand); 
    end loop; 
end; 
/
+0

一般問題由您的代碼處理的數據集中的數據導致的數據類型不適合所使用的數據類型。字符串可能太長,或者您將非數字數據視爲數字,即oracle無法將數據轉換爲數字。 – user2672165

回答

1

解析錯誤堆棧:

ORA-06502: PL/SQL: numeric or value error 

由試圖施放一個非數字字符串到數字數據類型,或一些其它數據轉換錯誤引起的。

ORA-06512: at "SYS.STANDARD", line 394 

調用堆棧的底部,引發異常的程序。因爲它是Oracle STANDARD包,這意味着它是內置函數之一,例如TO_NUMBER()

ORA-06512: at "DOMINOS.DISTANCE", line 10 

調用拋出錯誤的前一個函數的過程。

ORA-06512: at "DOMINOS.ZOEKWINKELVOORADRES", line 19 

調用上一個函數的過程。

ORA-06512: at line 5 

調用堆棧的頂部,它啓動它的所有代碼。也許是一個匿名塊?

因此,您發佈的代碼ZOEKWINKELVOORADRES()不是很有幫助,因爲錯誤是由DISTANCE()的第10行生成的。我們可以告訴的是遊標的第54行有值錯誤。所以你必須調試你的數據集。

基本上你需要記錄輸入到DISTANCE()。對於開發,您可以在致電DISTANCE()之前撥打dbms_output.put_line(),其中的值爲a_lat(i),a_lon(i),latklantlonklant。爲了在生產中進行健壯的診斷,您應該在持久性存儲(日誌表或文件)中記錄錯誤和上下文信息,例如輸入參數。

3

bit of searching它看起來像你得到這個錯誤,如果你給這兩組座標相同的位置。

假設你distance功能類似地定義該鏈接例如:

CREATE OR REPLACE FUNCTION DISTANCE 
( 
Lat1 IN NUMBER, 
Lon1 IN NUMBER, 
Lat2 IN NUMBER, 
Lon2 IN NUMBER 
) RETURN NUMBER IS 
DegToRad NUMBER := 57.29577951; 
BEGIN 
RETURN(6387.7 * ACOS((sin(NVL(Lat1,0)/DegToRad) * SIN(NVL(Lat2,0)/DegToRad)) + 
(COS(NVL(Lat1,0)/DegToRad) * COS(NVL(Lat2,0)/DegToRad) * 
COS(NVL(Lon2,0)/DegToRad - NVL(Lon1,0)/ DegToRad)))); 
END; 
/

...那麼如果你通過同一對值的兩倍計算結果爲無效的東西,由於舍入誤差,例如與

select distance(53.8662, 10.68117, 53.8662, 10.68117) from dual 

添加(1)調試用於組分(在功能,BEGINRETURN之間)表示:

dbms_output.put_line(lat1 ||','|| lon1); 
dbms_output.put_line(sin(NVL(Lat1,0)/DegToRad)); 
dbms_output.put_line(SIN(NVL(Lat2,0)/DegToRad)); 
dbms_output.put_line(COS(NVL(Lat1,0)/DegToRad)); 
dbms_output.put_line(COS(NVL(Lat2,0)/DegToRad)); 
dbms_output.put_line(COS(NVL(Lon2,0)/DegToRad)); 
dbms_output.put_line(NVL(Lon1,0)/ DegToRad); 

.8076421638813717679360124563997362950201 
.8076421638813717679360124563997362950201 
.5896729051949185735939828069514084977347 
.5896729051949185735939828069514084977347 
.9826737619730074300608748352929523713616 
.1864215844752715888130518254292967904505 

和當這些相乘並相加的結果是:

1.00000000000000000000000000000000000001 

因此,整個事情的計算結果爲RETURN(6387.7 * ACOS(1.00000000000000000000000000000000000001)),並且ACOS(1.00000000000000000000000000000000000001)會引發相同的錯誤,至少在PL/SQL中爲:

declare 
    result number; 
begin 
    result := acos(1.00000000000000000000000000000000000001); 
end; 
/

ORA-06502: PL/SQL: numeric or value error 
ORA-06512: at "SYS.STANDARD", line 394 
ORA-06512: at line 4 

SQL函數得到一個不同的錯誤:

select acos(1.00000000000000000000000000000000000001) from dual; 

SQL Error: ORA-01428: argument '1.00000000000000000000000000000000000001' is out of range 

...但它同樣的問題,這是沒有意義的傳遞一個值大於1至ACOS。

作爲一種變通方法,你可以改變的功能ROUND()值調用ACOS()之前,有足夠高的參數不顯著影響其他的計算,雖然與任何四捨五入它不會是完美的(但顯然不是反正!):

RETURN(6387.7 * ACOS(ROUND((
    (SIN(NVL(Lat1,0)/DegToRad) * SIN(NVL(Lat2,0)/DegToRad)) 
     + (COS(NVL(Lat1,0)/DegToRad) 
     * COS(NVL(Lat2,0)/DegToRad) 
     * COS(NVL(Lon2,0)/DegToRad - NVL(Lon1,0)/ DegToRad) 
     ) 
    ), 9)) 
); 

與該改變:

select distance(53.8662, 10.68117, 53.8662, 10.68117) from dual; 

DISTANCE(53.8662,10.68117,53.8662,10.68 
--------------------------------------- 
             0 

如果你不能改變的功能,那麼你將有比較值來決定是否可以安全地調用它。

+1

頂級Google Fu !! – APC

+0

爲什麼不使用'LEAST'將'ACOS'限制爲最大值1而不是四捨五入並可能失去準確性? – MT0

+0

@MTO - 無論從現有的舍入誤差中,你都失去了一些*精度,儘管不是那麼多。不知道對整體距離計算有多大的差異。但是,是的,任何事情都要保持有效。或者該函數可以檢查它們是否相同,只是返回零而不計算任何東西, –

1

Oracle擁有自己的Spatial函數庫,其中包含處理緯度/經度點之間距離的函數。

甲骨文設置

CREATE TABLE Postcode (
    postcode_id NUMBER(8,0), 
    postcode VARCHAR2(9), 
    location SDO_GEOMETRY 
); 

INSERT INTO USER_SDO_GEOM_METADATA (
    TABLE_NAME, COLUMN_NAME, DIMINFO, SRID 
) VALUES (
    'POSTCODE', 
    'LOCATION', 
    SDO_DIM_ARRAY(
    SDO_DIM_ELEMENT('LAT', -90.0, 90.0, 0.5), 
    SDO_DIM_ELEMENT('LONG', -180.0, 180.0, 0.5) 
), 
    8307 
); 

CREATE INDEX Postcode_SIDX ON Postcode(location) 
    INDEXTYPE IS MDSYS.SPATIAL_INDEX; 

CREATE TABLE winkel (id INT, postcode_id INT); 

CREATE TABLE winkel_afstand (id INT, distance NUMBER(10,5)); 

測試數據

INSERT INTO winkel 
SELECT 1, 1 FROM DUAL UNION ALL 
SELECT 2, 2 FROM DUAL UNION ALL 
SELECT 3, 3 FROM DUAL UNION ALL 
SELECT 4, 4 FROM DUAL; 

INSERT INTO Postcode 
-- Buckingham Palace, London, England 
SELECT 1, 'SW1A 1AA', SDO_GEOMETRY(2001, 8307, SDO_POINT_TYPE(51.5014, -0.1419,NULL), NULL, NULL) FROM DUAL UNION ALL 
-- Big Ben, London, England 
SELECT 2, 'SW1A 0AA', SDO_GEOMETRY(2001, 8307, SDO_POINT_TYPE(51.5007, -0.1246,NULL), NULL, NULL) FROM DUAL UNION ALL 
-- Edinburgh CAstle, Edinburgh, Scotland 
SELECT 3, 'EH1 2NG', SDO_GEOMETRY(2001, 8307, SDO_POINT_TYPE(55.9486, -3.1999,NULL), NULL, NULL) FROM DUAL UNION ALL 
-- Snowdon, Llanberis, Wales 
SELECT 4, 'LL55 4TY', SDO_GEOMETRY(2001, 8307, SDO_POINT_TYPE(53.0685, -4.0763,NULL), NULL, NULL) FROM DUAL; 

查詢

你可以重寫你的程序作爲一個單一的INSERT聲明(沒有光標, varrays或循環必要):

INSERT INTO winkel_afstand 
SELECT w.id, 
     sdo_geom.sdo_distance(p.location, q.loc, 0.005, 'unit=mile') 
FROM winkel w 
     INNER JOIN 
     postcode p 
     ON w.postcode_id = p.postcode_id 
     CROSS JOIN 
     (SELECT location AS loc 
     FROM postcode 
     WHERE postcode = 'SW1A 0AA') q; 

輸出

SELECT * FROM winkel_afstand; 

     ID DISTANCE 
---------- ---------- 
     1 1.18963 
     2   0 
     3 373.09907 
     4 292.33809 

然而,即使不使用Oracle的空間數據,你仍然可以簡化你的程序有很多:

CREATE PROCEDURE zoekWinkelVoorAdres (
    v_postcode in postcode.postcode%type, 
    v_huisnr in WINKEL.HUISNR%type, 
    v_id  out WINKEL.ID%type, 
    v_afstand out number 
) 
IS 
BEGIN 
    INSERT INTO winkel_afstand 
    SELECT w.id, 
     distance(
      lat, 
      lon, 
      lt, 
      ln 
     ) 
    FROM winkel w 
     INNER JOIN 
     (SELECT p.*, 
        FIRST_VALUE(CASE WHEN postcode = v_postcode THEN lat END) 
        IGNORE NULLS OVER() AS lt, 
        FIRST_VALUE(CASE WHEN postcode = v_postcode THEN lon END) 
        IGNORE NULLS OVER() AS ln 
      FROM postcode p) p 
     ON w.postcode_id = p.postcode_id; 
END; 
/
+0

空間是一個額外成本選項,不是嗎?當然,如果可用的話,有意義... –

+0

是的,根據[本許可信息](https://docs.oracle.com/cd/E11882_01/license.112/e47877.pdf)空間和圖形是企業版成本選項。 – MT0