我正在尝试编写一个PLSQL函数,该函数实现了一个约束,即员工不能同时成为驱动程序和机制,换句话说,TRKEMPLOYEE中的同一E#不能同时在TRKDRIVER和TRKMECHANIC中。如果数据库的内容违反了该约束,请列出所有同时是机制和驱动程序的员工的E#和NAME。上述表格如下:
TRKEMPLOYEE(E# NUMBER(12) NOT NULL
NAME VARCHAR(50) NOT NULL,
DOB DATE ,
ADDRESS VARCHAR(300) NOT NULL,
HIREDATE DATE NOT NULL,
CONSTRAINT TRKEMPLOYEE_PKEY PRIMARY KEY(E#))
TRKDRIVER(E# NUMBER(12) NOT NULL
L# NUMBER(8) NOT NULL,
STATUS VARCHAR(10) NOT NULL,
CONSTRAINT TRKDRIVER_PKEY PRIMARY KEY(E#),
CONSTRAINT TRKDRIVER_FKEY FOREIGN KEY(E#) REFERENCES TRKEMPLOYEE(E#))
TRKMECHANIC(E# NUMBER(12) NOT NULL
L# NUMBER(8) NOT NULL,
STATUS VARCHAR(10) NOT NULL,
EXPERIENCE VARCHAR(10) NOT NULL,
CONSTRAINT TRKMECHANIC_PKEY PRIMARY KEY(E#),
CONSTRAINT TRKMECHANIC_FKEY FOREIGN KEY(E#) REFERENCES TRKEMPLOYEE(E#))
我试图编写一个函数,但在第1行第7列中一直遇到编译错误。有人可以告诉我为什么我的代码不起作用吗?我的代码如下
CREATE OR REPLACE FUNCTION Verify()
IS DECLARE
E# TRKEMPLOYEE.E#%TYPE;
CURSOR C1 IS SELECT E# FROM TRKEMPLOYEE;
BEGIN
OPEN C1;
LOOP
FETCH C1 INTO EMPNUM;
IF(EMPNUM IN(SELECT E# FROM TRKMECHANIC )AND EMPNUM IN(SELECT E# FROM TRKDRIVER))
SELECT E#, NAME FROM TRKEMPLOYEE WHERE E#=EMPNUM;
ELSE
dbms_output.put_line(“OK”);
ENDIF
EXIT WHEN C1%NOTFOUND;
END LOOP;
CLOSE C1;
END;
/
任何帮助将不胜感激。感谢。
答案 0 :(得分:1)
您正在创建一个函数,但缺少RETURN
声明。如果您不想返回结果,请使用create or replace procedure
。
此外,如果您没有任何参数,请移除括号()
,DECLARE
关键字也不正确。
检查表格的方式效率不高。您可以通过单个查询获取违反业务约束的所有员工的计数:
select emp.e#,
emp.name,
count(drv.e#) + count(mec.e#) as cnt
from trkemployee emp
left join trkdriver drv on drv.e# = emp.e#
left join trkmechanic mec on mec.e# = emp.e#
group by emp.e#, emp.name
having count(drv.e#) + count(mec.e#) > 1;
此外,EXIT WHEN C1%NOTFOUND;
应该在 FETCH
声明后正确。否则,即使光标没有取任何东西,你仍然在循环中。
如果您使用我的简化声明,您可以循环通过所有员工并打印确定,也可以根据计数打印:
CREATE OR REPLACE procedure Verify
IS
empnum number(12);
cnt integer;
empname varchar(50);
CURSOR C1 IS
select emp.e#,
emp.name,
count(drv.e#) + count(mec.e#) as cnt
from trkemployee emp
left join trkdriver drv on drv.e# = emp.e#
left join trkmechanic mec on mec.e# = emp.e#
group by emp.e#, emp.name;
BEGIN
OPEN C1;
LOOP
FETCH C1 INTO empnum, empname, cnt;
EXIT WHEN C1%NOTFOUND;
if cnt > 1 then
dbms_output.put_line(to_char(empnum)||' NOK');
else
dbms_output.put_line(to_char(empnum)||' OK');
end if;
END LOOP;
CLOSE C1;
END;
/
我正在考虑如何使用数据库中的约束来强制执行此业务规则:
create table trkemployee
(
E# NUMBER(12) NOT NULL,
NAME VARCHAR(50) NOT NULL,
DOB DATE ,
ADDRESS VARCHAR(300) NOT NULL,
HIREDATE DATE NOT NULL,
emp_type char(1) NOT NULL,
constraint trkemployee_pkey primary key(e#, emp_type),
constraint chk_emp_type check (emp_type in ('d','m'))
);
create table TRKDRIVER
(
e# number(12) not null,
emp_type char(1) default 'd' not null,
l# number(8) not null,
status varchar(10) not null,
constraint trkdriver_pkey primary key(e#),
constraint trkdriver_fkey foreign key(e#,emp_type) references trkemployee(e#, emp_type),
constraint check_drv_type check (emp_type = 'd')
);
create table trkmechanic
(
e# number(12) not null,
emp_type char(1) default 'm' not null,
l# number(8) not null,
status varchar(10) not null,
experience varchar(10) not null,
constraint trkmechanic_pkey primary key(e#),
constraint trkmechanic_fkey foreign key(e#,emp_type) references trkemployee(e#, emp_type),
constraint check_mec_type check (emp_type = 'm')
);
我们的想法是将员工类型包含在其外键中,并确保从属表不能使用错误的类型。插入新的trkemployee时,必须指定类型。详细信息表上的检查约束将拒绝任何类型错误的行。
要将员工从一种类型“移动”到另一种类型(如果可能的话),必须先删除旧的详细信息行。只有这样,员工的类型才能更改为其他类型。最后可以插入新的细节。
答案 1 :(得分:1)
好的,这个功能有很多错误。
如a_horse_with_no_name注释,如果没有参数,则删除()
,根据定义,函数需要RETURN语句。
然而,并非全部:
我会猜测你想要一个程序,因为我看不到你能回来的东西。我还猜想如果你不想对你的第一个IF做任何事情,那么你可以将其他选择放入光标。
最后,我假设您将其传递给您要检查的员工的e#
。
由于所有3个表在e#
上都是唯一的,因此您可以使用LEFT OUTER JOINS将所有SQL放在单个游标中
create or replace function verify ( Pemployee trkemployee.e#%type
) return varchar2 is
l_mechanic trkemployee.e#%type;
l_driver trkemployee.e#%type;
begin
-- Doing things in SQL is more efficient.
select m.e#, d.e#
into l_mechanic, l_driver
from trkemployee e
left outer join trkmechanic m
on e.e# = m.e#
left outer join trkdriver d
on e.e# = d.e#
;
if l_driver is not null and l_mechanic is not null then
return 'BAD';
else
return 'OK';
end if;
end;
/
答案 2 :(得分:0)
混合a_horse_with_no_name
和Ben
给出的答案,然后稍微“简化”给出 -
CREATE OR REPLACE FUNCTION Verify
return varchar2 IS
BEGIN
FOR c_rec in (select emp.e#,
count(drv.e#) + count(mec.e#) as cnt
from trkemployee emp
left join trkdriver drv on drv.e# = emp.e#
left join trkmechanic mec on mec.e# = emp.e#
group by emp.e#)
LOOP
if c_rec.cnt > 1 then
return ('BAD '||c_rec.e#);
else
return ('OK '||c_rec.e#);
end if;
END LOOP;
EXCEPTION
WHEN ....THEN
--as the wise man once said "Do some meaningful exception handling here"...
END;
/
对于DECLARE
,OPEN
,FETCH
和CLOSE
光标,看起来有点过分。
或PROCEDURE
喜欢
CREATE OR REPLACE PROCEDURE Verify
IS
BEGIN
FOR c_rec in (select emp.e#,
count(drv.e#) + count(mec.e#) as cnt
from trkemployee emp
left join trkdriver drv on drv.e# = emp.e#
left join trkmechanic mec on mec.e# = emp.e#
group by emp.e#)
LOOP
if c_rec.cnt > 1 then
dbms_output.put_line('BAD '||c_rec.e#);
else
dbms_output.put_line('OK '||c_rec.e#);
end if;
END LOOP;
EXCEPTION
WHEN ....THEN
--as the wise man once said "Do some meaningful exception handling here"...
END;
/