1

従業員がドライバーとメカニックの両方になることはできないという制約を実装するPLSQL関数を作成しようとしています。つまり、TRKEMPLOYEEの同じE#をTRKDRIVERとTRKMECHANICに同時に含めることはできません。DBの内容がその制約に違反している場合は、メカニックとドライバーの両方であるすべての従業員の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

関数を作成していますが、RETURN宣言がありません。結果を返したくない場合は、を使用しますcreate or replace procedure

さらに、パラメータがない場合は、角かっこを削除する()と、DECLAREキーワードも正しくありません。


テーブルをチェックする方法は実際には効率的ではありません。1つのクエリで、ビジネス上の制約に違反しているすべての従業員の数を取得できます。

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

私の簡略化したステートメントを使用すると、すべての従業員を1回ループして、カウントに応じて[OK]または[NotOK]を出力できます。

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 に答える
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を1つのカーソルに置くことができます。

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 に答える
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が必要なのはやり過ぎのようです。FETCHCLOSE

またはの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 に答える