Oracle 使用PLSQL创建过程

Oracle 使用PLSQL创建过程,oracle,plsql,sqlplus,Oracle,Plsql,Sqlplus,我正在尝试编写一个PLSQL函数,该函数实现一个约束,即员工不能同时是驾驶员和机械师,换句话说,来自TRKEMPLOYEE的同一个E#不能同时在TRKDRIVER和TRKMECHANIC中。如果DB的内容违反了该约束,请列出所有机械师和司机的E#和姓名。上述表格如下所示: TRKEMPLOYEE(E# NUMBER(12) NOT NULL NAME VARCHAR(50) NOT NULL, DOB

我正在尝试编写一个PLSQL函数,该函数实现一个约束,即员工不能同时是驾驶员和机械师,换句话说,来自TRKEMPLOYEE的同一个E#不能同时在TRKDRIVER和TRKMECHANIC中。如果DB的内容违反了该约束,请列出所有机械师和司机的E#和姓名。上述表格如下所示:

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;
/

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

您正在创建一个函数,但缺少
返回
声明。如果不想返回结果,请使用
创建或替换过程

此外,如果没有任何参数,请删除括号
()
,并且
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;
此外,未找到C1%时,
退出
应该在
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')
);
其思想是在其外键中包含employee类型,并确保依赖表不能使用错误的类型。插入新trkemployee时,必须指定类型。明细表上的检查约束将拒绝任何类型错误的行


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

好的,这个函数有很多错误

作为一个没有名字的马,如果没有参数,则删除
()
,根据定义,函数需要一个RETURN语句

然而,这并不是全部:

  • 不能在IF语句中使用SELECT
  • 您必须将结果放入变量中,或将其作为 光标
  • 函数或过程中不需要DECLARE块
  • 你的IF语句需要一个。。。但是,我认为你不需要这个,因为你只是在做别的事情
  • 您正在将光标的结果提取到一个不存在的变量中
  • 我冒昧地猜测你想要一个程序,因为我不知道你能返回什么。我还想,如果你不想用第一个选项做任何事情,那么你可以把其他的选项放到光标中

    最后,我假设您将要检查的员工的
    e#
    传递给它

    由于所有3个表在
    e35;
    上都是唯一的,因此可以使用左外部联接将所有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;
    /
    

    a_horse\u给出的答案与\u 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;
    /
    
    必须
    声明
    打开
    获取
    关闭
    光标,这看起来是一种过激行为

    或类似的
    过程

    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;
    /
    

    我不确定我是否理解您的数据模型。TRKDRIVER和TRKMECHANIC表存储什么?他们是否有TRKEMPLOYEE没有的其他属性?为什么不在TRKEMPLOYEE中设置一个标志来标记员工、司机或技工?我省略了表格的一些细节,因为我认为它们与当时的问题无关。我已经添加了这些字段以及我问题的全部细节。顺便说一句:你可以通过使用物化视图在这个模型中添加一个约束。这个解决方案是否可行取决于这些表更新的频率和它们的大小。谢谢,但我没有提到一个伪约束:它必须在PLSQL中完成,而不是简单地修改表的模式,因为这是我作业的一部分。这个问题要求我使用PLSQL。你预先编辑的代码与我想要的非常接近(除了错误,尽管基本上是复制粘贴,但我仍然会收到错误),但是我不想打印“NOK”,而是想打印有问题的E#和名称。这行吗如果cnt>1,则选择E#FROM TRKEMPLOYEE WHERE E#IN(选择E#FROM TRKDRIVER)@user1857460如果要打印名称和
    E#
    ,只需将它们添加到基本光标即可。无需运行新的select语句。下面是一个可编译的示例:我已经尝试了这个示例。此SQL语句有一个问题:选择emp.e#,emp.name,count(drv.e#)+count(mec.e#)作为trkemployee emp left join trkdriver drv on drv.e#=emp.e#left join trkmechanic mec on mec.e#=emp.e#group by emp.e#;上面写着“不是一组表达”。我自己也不知道那句话有什么不对。@user1857460:对不起,
    groupby
    子句中缺少
    emp.name
    列。