1

我正在尝试编写一个 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;
/

任何帮助,将不胜感激。谢谢。

4

3 回答 3

1

好吧,这个功能有很多问题。

正如 a_horse_with_no_name 所指出的,如果您没有参数,则删除()并且根据定义,函数需要 RETURN 语句。

然而,这还不是全部:

  1. 不能在 IF 语句中使用 SELECT。
  2. 您必须将结果放入变量中或将其作为游标的一部分引用。
  3. 函数或过程中不需要 DECLARE 块。
  4. 您的 IF 语句需要一个 THEN ...但是,我认为您不需要这个,因为您只是在 ELSE 中做某事。
  5. 您正在将游标的结果提取到一个不存在的变量中。

我会冒险猜测您想要一个程序,因为我看不到您可以返回什么。我还猜想,如果您不想对第一个 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;
/
于 2012-11-27T21:55:54.423 回答
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。否则,即使游标没有获取任何内容,您也会停留在循环中。

如果您采用我的简化语句,您可以遍历所有员工并打印 OK,也不是 Not OK,具体取决于计数:

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 时,必须指定类型。明细表上的检查约束将拒绝任何类型错误的行。

要将员工从一种类型“移动”到另一种类型(如果可能的话),必须首先删除旧的明细行。只有这样员工的类型才能更改为其他类型。最后可以插入新的细节。

于 2012-11-27T21:34:26.053 回答
0

a_horse_with_no_name将and给出的答案混合起来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;
/

看起来有点过分了 to DECLARE, OPEN,FETCHCLOSEcursor 。

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;
/
于 2012-11-27T23:03:15.123 回答