[英]Creating a procedure with PLSQL
我正在嘗試編寫一個 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;
/
任何幫助,將不勝感激。 謝謝。
您正在創建一個函數,但缺少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 時,必須指定類型。 詳細表上的檢查約束將拒絕任何類型錯誤的行。
要將員工從一種類型“移動”到另一種類型(如果可能的話),必須首先刪除舊的詳細信息行。 只有這樣,員工的類型才能更改為其他類型。 最后可以插入新的細節。
好吧,這個功能有很多問題。
正如 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;
/
混合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;
/
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.