PL/SQL 错误问题

Posted

技术标签:

【中文标题】PL/SQL 错误问题【英文标题】:PL/SQL error question 【发布时间】:2011-07-22 21:41:24 【问题描述】:

我正在尝试编写一个将一行插入员工表的存储过程。如果部门不存在,则需要将该部门插入到部门表中。我有以下代码:

drop table employees;
drop table departments;

create table departments(
dept            varchar2(30),
dept_number     number,
dept_city       varchar2(30),
CONSTRAINT pk_dept PRIMARY KEY(dept)
);

create table employees( 
dept            varchar2(30),
employee_name   varchar2(40),
employee_id     number,
CONSTRAINT pk_id PRIMARY KEY(employee_id),
CONSTRAINT fk_dept FOREIGN KEY (dept) REFERENCES departments(dept)
);


CREATE  OR REPLACE PROCEDURE employeeadd(
a_dept    IN  VARCHAR2,
a_employee_name    IN VARCHAR2,
a_employee_id    IN NUMBER)
as
    li_count    NUMBER;
BEGIN
sp_check_dept(a_dept, li_count);    
if li_count = 0 then
INSERT INTO departments (dept) values (a_dept);
    return;
end if;
INSERT INTO employee values (a_dept, a_employee_name, a_employee_id);
end;
/

create or replace procedure sp_check_dept(a_dept IN NUMBER,
                        a_count  OUT NUMBER)
as
begin
    select count(*)
into a_count
from departments
where dept_number = a_dept;
end;
/

当我以 execute employeeadd('marketing', 'john', 10); 运行我的执行语句时我收到以下错误。我似乎无法弄清楚如何克服错误和/或正确编写:

ORA-06502:PL/SQL:数字或值错误:字符到数字的转换错误 ORA-06512: 在 "employeeadd" 第 8 行 ORA-06512: 在第 1 行

【问题讨论】:

sp_check_dept的定义是什么? 创建或替换过程 sp_check_dept(a_dept IN NUMBER, a_count OUT NUMBER) as begin select count(*) into a_count from department where dept_number = a_dept;结尾; / 如果您用换行符分隔离散的错误消息将会很有帮助。您是否收到 3 个离散错误?发布两个表“departments”和“employee”的定义以及 sp_check_dept() 过程的定义也会很有用。 第一个错误“字符到数字的转换”一定是由于 a_dept IN VARCHAR2 参数,该参数被传递给 sp_check_dept(),它的第一个参数是 NUMBER。 【参考方案1】:

为什么 li_count 在 BEGIN...END 块之外声明?在将其作为参数发送给 sp_check_dept() 之前是否需要对其进行分配?

编辑:刚刚看到您的后续评论: sp_check_dept 期望一个数字作为其第一个参数;您已将 a_dept 声明为 VARCHAR。

【讨论】:

【参考方案2】:

sp_check_dept 将部门编号作为输入参数(数字)并返回计数作为输出参数。 employeeadd 将部门名称 (VARCHAR2) 作为第一个参数传递给 sp_check_dept。有几种方法可以解决这个问题。通常,您需要一种更一致的参数命名方法,以便更轻松地识别这些问题。

选项 1:对两个职能都使用部门名称

create or replace procedure sp_check_dept(p_dept_name IN departments.dept%type,
                                          p_count    OUT NUMBER)
as
begin
    select count(*)
      into p_count
      from departments
     where dept = p_dept_name;
end;
/

CREATE  OR REPLACE PROCEDURE employeeadd(
  p_dept_name     IN departments.dept%type,
  p_employee_name IN employees.employee_name%type,
  p_employee_id   IN employees.employee_id%type)
as
    li_count    NUMBER;
BEGIN
  sp_check_dept(p_dept_name, li_count);    
  if li_count = 0 then
    INSERT INTO departments (dept) 
      VALUES (p_dept_name);
  end if;
  INSERT INTO employee(dept, employee_name, employee_id)
    VALUES (p_dept, p_employee_name, p_employee_id);
end;
/

选项 2:将employeeAdd 中的部门名称转换为部门编号,然后再将其传递给sp_check_dept

create or replace procedure sp_check_dept(p_dept_number IN departments.dept_number%type,
                                          p_count      OUT NUMBER)
as
begin
    select count(*)
      into p_count
      from departments
     where dept_number = p_dept_number;
end;
/

CREATE OR REPLACE FUNCTION get_dept_number( p_dept_name IN departments.dept%tyep )
  RETURN departments.dept_number%type
IS
  l_dept_number departments.dept_number%type;
BEGIN
  SELECT dept_number
    INTO l_dept_number
    FROM departments
   WHERE dept = p_dept_name;

  RETURN l_dept_number
END;
/

CREATE  OR REPLACE PROCEDURE employeeadd(
  p_dept_name     IN departments.dept%type,
  p_employee_name IN employees.employee_name%type,
  p_employee_id   IN employees.employee_id%type)
as
    li_count    NUMBER;
BEGIN
  sp_check_dept( get_dept_number(p_dept_name), li_count);    
  if li_count = 0 then
    INSERT INTO departments (dept) 
      VALUES (p_dept_name);
  end if;
  INSERT INTO employee(dept, employee_name, employee_id)
    VALUES (p_dept, p_employee_name, p_employee_id);
end;
/

其他一些观察结果

    我从employeeAdd 中的 IF 语句中删除了 RETURN 语句。在将行插入DEPARTMENTS 表之后,再将行插入EMPLOYEE 表之前,您几乎肯定不想退出该过程。 您的表定义使用了复数 EMPLOYEES。您的程序使用了单数 EMPLOYEE。我没有更正,因为我不确定您发布的 DDL 是否不正确,或者您发布的程序是否不正确。 一般来说,将sp_check_dept 实现为一个返回计数的函数而不是一个带有OUT 参数的过程会更有意义。如果一段代码只是为了向调用者返回数据而存在,则应将其声明为函数。 从数据模型的角度来看,列名DEPT 并不是特别好。使用 DEPARTMENT_NAME 之类的东西来传达列实际代表的内容会更合适。 从数据模型的角度来看,将 VARCHAR2 列 DEPT(即使它重命名为 DEPARTMENT_NAME)作为 DEPARTMENTS 的主键和 EMPLOYEES 中的外键没有多大意义。主键应该是不可变的。但是,部门的名称会随着时间的推移而改变。将DEPARTMENT_NUMBER 用作主键并将DEPARTMENT_NAME 简单地标记为唯一会更有意义。当营销部门在未来更名为广告时,这将变得更加容易,因为您不必追踪所有子表来更新它们。 您应该为过程选择一个命名约定并坚持下去。我更喜欢check_deptadd_employee(动词后跟主语,下划线分隔单词,没有前缀)。但是,如果您想要sp_check_deptsp_add_employeecheckDeptaddEmployee 甚至sp_dept_checksp_employee_add,那就没问题了。但是,如果您的过程命名约定没有模式,您自己和其他开发人员都会发疯。

【讨论】:

感谢贾斯汀的所有好建议!我很感激!【参考方案3】:

我可以看到 2 种可能性: 1.employee 表的列顺序与您的 insert 语句不同,它试图将 dept 或 name 转换为 id 2. li_count 中设置的值不是数字,所以它试图将返回值转换为数字并给你错误

【讨论】:

以上是关于PL/SQL 错误问题的主要内容,如果未能解决你的问题,请参考以下文章

从 PL/SQL 返回错误

PL/SQL 函数返回错误结果

ORACLE:错误错误(6,3):PL/SQL:SQL 语句被忽略和错误(8,3):PL/SQL:ORA-00933:SQL 命令未在过程中正确结束

pl/sql 中的错误处理

获取 PL/SQL:数字或值错误:字符到数字的转换错误

PL/SQL 过程编译错误