【问题标题】:PL/SQL: numeric or value errorsPL/SQL:数字或值错误
【发布时间】:2016-10-12 20:12:13
【问题描述】:

我遇到了一个错误,我不知道为什么:

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;
/

【问题讨论】:

  • 一般来说,问题是由您的代码处理的数据集中的数据不适合所使用的数据类型引起的。字符串可能太长,或者您将非数字数据视为数字,即 oracle 无法将数据转换为数字。

标签: oracle plsql ora-06502


【解决方案1】:

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

为组件添加调试(在函数中,在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是没有意义的。

作为一种解决方法,您可以在调用 ACOS() 之前将函数更改为 ROUND() 的值,并使用足够高的参数不会显着影响其他计算,尽管与任何舍入一样,它不会是完美的(但显然不是无论如何都不会!):

  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

如果您无法更改函数,则必须比较值以确定调用它是否安全。

【讨论】:

  • 为什么不使用 LEASTACOS 限制为最大 1 而不是四舍五入并可能失去准确性?
  • @MTO - 无论如何,您都会从现有的舍入误差中损失 一些 的准确性,尽管没有那么多。不知道对整体距离计算有多大的影响。但是,是的,任何保持它有效的东西。或者该函数可以检查它们是否相同,然后返回零而不计算任何内容,
  • 谢谢!请问你把调试打印放在哪里?
  • @Mr_and_Mrs_D - 他们进入函数,在BEGINRETURN 之间。 (这是他们唯一可以去的地方,但将其添加到答案中)。您的客户当然也需要显示输出,例如set serveroutput on 在 SQL*Plus 或 SQL Developer 中调用函数之前。
【解决方案2】:

解析错误堆栈:

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】:

    Oracle 有自己的 Spatial 库,其中包含处理纬度/经度点之间距离的函数。

    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;
    /
    

    【讨论】:

    • 空间是一个额外的选择,不是吗?当然,如果可用,使用它是有意义的......
    • 是的,根据this licensing information Spatial and Graph 是企业版成本选项。
    猜你喜欢
    • 2023-03-09
    • 1970-01-01
    • 1970-01-01
    • 2014-04-25
    • 2014-06-13
    • 1970-01-01
    • 1970-01-01
    • 2016-12-18
    相关资源
    最近更新 更多