【问题标题】:Procedure fails with numeric or value error过程因数字或值错误而失败
【发布时间】:2018-10-01 17:24:26
【问题描述】:

我正在研究一个从表中获取行并稍微更改名称以使总共 50 行的过程...正如您将在代码中看到的那样。

但是,我遇到了问题

一个数字或值错误,你会在我尝试使用的代码中看到

 dbms_output.put_line('some message'); 

但我的输出不想工作,所以发现问题变得很麻烦。

错误提示

"PL/SQL: numeric or value error"
*Cause:    An arithmetic, numeric, string, conversion, or constraint error
       occurred. For example, this error occurs if an attempt is made to
       assign the value NULL to a variable declared NOT NULL, or if an
       attempt is made to assign an integer larger than 99 to a variable
       declared NUMBER(2).
*Action:   Change the data, how it is manipulated, or how it is declared so
       that values do not violate constraints.

如果有人能指出我正确的方向(或什至如何让 dbms 工作),我将不胜感激

(我已打开 dbms 并连接到服务器并设置了 serverouput;)

代码:

create or replace procedure bbt_phone_users (n in number) authid current_user
as

    cursor r10 is select firstname, lastname, password_ from bbt_users_temp;
    r10type r10%rowtype;

    fn bbt_users_temp.firstname%type;
    ln bbt_users_temp.lastname%type;
    pass bbt_users_temp.password_%type;

    tel varchar2(15);
    keymap_ln varchar(4);

    phone_end number(4);
    name_end number;

begin

    phone_end := 1000;

    dbms_output.put_line('hey');
    for i in 1 .. n
    loop
        open r10;
        fetch r10 into fn, ln, pass;
        close r10;
        for oneRow in r10
        loop
            name_end := name_end + 1;

            dbms_output.put_line('works pre-1');
            --1
            -- each row gets the phone_end, which increments on each iteration
            phone_end := phone_end + 1;
            tel := '(317) 456-' || to_char(phone_end);

            dbms_output.put_line('works after 1');
            --2
            -- takes the last name, and adds 000 and some number if its less than 10 OR
            -- adds (concatinates) 00 and the numbers if its > 10
            if name_end < 10 then
                ln := ln || '000' || to_char(phone_end, '9');
            else
                ln := ln || '00' || to_char(phone_end, '99');
            end if;

            dbms_output.put_line('works after 2');
            --3
            -- calls the KEYMAP function and passes it the lastname
            keymap_ln := KEYMAP(ln);

            dbms_output.put_line('works after 3');

            --4
            -- inserts all our values
            insert into phone_users values(tel, fn, ln, keymap_ln, pass);

            --5
            --rest are ignored since we don't do anything with them

        end loop;
    end loop;
end;
/

call bbt_phone_users(10);

select * from phone_users;

【问题讨论】:

  • 你在运行代码之前发出set serveroutput on吗?
  • 是的,我让它在手术前运行
  • 错误出现在哪一行代码?
  • keycap 函数有什么作用?你通过ln(我猜是“姓氏”,虽然这也是the name of an Oracle function),输出必须适合keycap_ln,即4个字符。在我的快速测试中,它在keymap_ln := KEYMAP(ln); 失败了,但我不得不制作一个虚拟函数和表格。
  • Keymap 是一个自定义函数,将姓氏的前 4 个字母变成四个数字

标签: sql oracle dbms-output


【解决方案1】:

您已将ln 声明为与bbt_users_temp.lastname 相同的数据类型和大小。然后您尝试在其上附加五个字符。因此,如果您第一次获取的任何值(由于游标查询没有 order-by 子句而不确定)在该列的最大允许长度的五个字符内,您将在第一次在内循环时得到错误。

假设您的专栏是varchar2(20)。如果第一个获取的值是 16 个字符或更多 - 例如'Vandroogenbroeck' - 然后ln 将从该值开始,长度为 16。然后您可以:

ln := ln || '00' || to_char(phone_end, '99');

将五个字符附加到现有值,使长度为 21。太长而无法放入变量中。

即使使用较短的值,例如“张伯伦”,第一次循环时您附加五个字符,总共 16 个字符,并且可以,但第二次循环时您将另外 5 个字符附加到 - 不是原来的 - 这再次使第二个值21,而且太长了。即使名称较短,列较长,也不会导致循环超过限制。

这并不是重点,但您可能只希望它附加四个字符。它实际上总是附加'00###'。您尚未将name_end 初始化为始终为空,然后您进入else,它附加'00',然后尝试使用掩码99 格式化phone_end。由于此时phone_end 是1001,它不适合两位数,所以你会得到##;但你也可以得到一个标志位置的奖金。

大概您正在尝试解释不同的起始phone_end。您可以将 if/else/end 块替换为单个大格式模型,该模型左填充零,并抑制符号值的空间(您可能也希望为 tel 值执行此操作):

ln := ln || to_char(phone_end, 'FM0000');

但这只是问题的一小部分;现在您每次在循环中添加四个字符而不是五个,因此您可能只需要稍长的时间就可以遇到错误。

您可能希望每次都将这四个字符附加到相同的初始字符串,或者附加到当前光标行的值。如果源表和目标表中的列长度相同,那么您可能需要在添加数字之前截断初始值以确保它适合。

不清楚你想要结束什么,循环逻辑看起来很可疑(你真的想在bbt_users_temp中插入行数的10倍吗?你认为@会发生什么? 987654338@ 以及总共超过 9000 行的格式化值?)。您可能能够重新设计逻辑,并且您可能根本不需要游标循环 - 甚至 PL/SQL -。如果没有样本数据和预期结果,以及更清晰的描述,就无法确定。

【讨论】:

  • 我认为名称长度可能是问题所在,但是原始名称在创建时只有 10 个字符长,并且在表中它的 varchar2(80)
  • @Cyrus - 您仍然只需要表格中的 15 行来解决问题 - 值的初始 10 个字符加上 15*5 => 85,这太长了。只有当 i=1;到 i=10 时,表格中的两行将打破它。 (我想)。调试应该准确地告诉您何时点击它。我不认为您打算继续累积附加到ln。但也要检查你的循环逻辑。
  • 太棒了,你让我意识到问题.. 或者它可能是什么.... 我看了 100 次表格,直到现在才意识到 FORM BBT 的大小只有 10 ...我如何调整最大字符的大小?还是我需要创建另一个长度不同的变量并将值传递给它?
  • 我认为你对 ln 也是正确的.. 我是否需要调用 ln := '';每次迭代?
  • 我不知道你想要达到什么目的。您可能希望一个变量保存原始 ln 值,然后将该固定值与格式化数字连接起来,以每次形成特定于行的 ln
猜你喜欢
  • 2021-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多