I'm getting an error and I don't know why:
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
The cursor should contain 145 rows. When I execute the procedure I get the error message above after 54 rows.
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;
/
From a bit of searching it looks like you get this error if you give the same same position for both sets of coordinates.
Assuming your distance
function is defined similarly to that linked example:
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;
/
... then if you pass the same pair of values twice the calculation evaluates to something invalid due to rounding errors, e.g. with
select distance(53.8662, 10.68117, 53.8662, 10.68117) from dual
Adding debugs for the components (in the function, between BEGIN
and RETURN
) shows:
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
and when those are multiplied and added together the result is:
1.00000000000000000000000000000000000001
So the whole thing evaluates to RETURN(6387.7 * ACOS(1.00000000000000000000000000000000000001))
, and ACOS(1.00000000000000000000000000000000000001)
throws the same error, at least in 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
The SQL function gets a different error:
select acos(1.00000000000000000000000000000000000001) from dual;
SQL Error: ORA-01428: argument '1.00000000000000000000000000000000000001' is out of range
... but it's the same problem, it isn't meaningful to pass a value greater than 1 to ACOS.
As a workaround you could change the function to ROUND()
the value before calling ACOS()
, with a high enough argument to not significantly affect other calculations, though as with any rounding it won't be perfect (but clearly isn't anyway!):
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))
);
With that change:
select distance(53.8662, 10.68117, 53.8662, 10.68117) from dual;
DISTANCE(53.8662,10.68117,53.8662,10.68
---------------------------------------
0
If you can't change the function then you will have to compare the values to decide whether it is safe to call it.