PL/SQL: 数字或值错误
PL/SQL: numeric or value errors
我收到一个错误,我不知道为什么:
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;
/
解析错误堆栈:
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)
、 latklant
和 lonklant
的值.为了在生产中进行稳健的诊断,您应该记录错误和上下文信息,例如持久存储中的输入参数(日志 table 或文件)。
从 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
为组件添加调试(在函数中,在 BEGIN
和 RETURN
之间)显示:
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
...但是同样的问题,给ACOS传一个大于1的值是没有意义的
作为一种解决方法,您可以将函数更改为 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
如果您无法更改该函数,那么您将不得不比较这些值来决定调用它是否安全。
Oracle 有自己的 Spatial 库,其中包含处理 latitude/longitude 点之间距离的函数。
Oracle 设置:
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
语句(不需要游标、变量数组或循环):
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;
/
我收到一个错误,我不知道为什么:
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;
/
解析错误堆栈:
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)
、 latklant
和 lonklant
的值.为了在生产中进行稳健的诊断,您应该记录错误和上下文信息,例如持久存储中的输入参数(日志 table 或文件)。
从 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
为组件添加调试(在函数中,在 BEGIN
和 RETURN
之间)显示:
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
...但是同样的问题,给ACOS传一个大于1的值是没有意义的
作为一种解决方法,您可以将函数更改为 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
如果您无法更改该函数,那么您将不得不比较这些值来决定调用它是否安全。
Oracle 有自己的 Spatial 库,其中包含处理 latitude/longitude 点之间距离的函数。
Oracle 设置:
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
语句(不需要游标、变量数组或循环):
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;
/