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_dept
和add_employee
(动词后跟主语,下划线分隔单词,没有前缀)。但是,如果您想要sp_check_dept
和sp_add_employee
或checkDept
和addEmployee
甚至sp_dept_check
和sp_employee_add
,那就没问题了。但是,如果您的过程命名约定没有模式,您自己和其他开发人员都会发疯。
【讨论】:
感谢贾斯汀的所有好建议!我很感激!【参考方案3】:我可以看到 2 种可能性: 1.employee 表的列顺序与您的 insert 语句不同,它试图将 dept 或 name 转换为 id 2. li_count 中设置的值不是数字,所以它试图将返回值转换为数字并给你错误
【讨论】:
以上是关于PL/SQL 错误问题的主要内容,如果未能解决你的问题,请参考以下文章
ORACLE:错误错误(6,3):PL/SQL:SQL 语句被忽略和错误(8,3):PL/SQL:ORA-00933:SQL 命令未在过程中正确结束