PL/SQL之儲存過程修改學生學號及觸發器實現一致性
(1)建立三個表:Student(S#, Sname, age), Course(C#, cname, credit), SC(s#, c#, score),其中SC的S#和C#都是外來鍵,分別引用Student表的S#和Course表的C#。請首先在各自表中插入若干條記錄,然後用儲存過程實現修改指定學生的學號。
(2)建立兩個表:系表:Dept(D#, Dname, S_Count) ,其中S_count是每個系的學生人數;學生表:Stu(S#, Sname, age, D#) 其中D#是引用Dept(D#)的外來鍵。請用觸發器實現S_count和學生表中實際人數的一致性。
開發工具:PL/SQL Developer
1.建表:
Student(S#, Sname, age)表,對應SQL語句如下:
create table STUDENT
(
S# VARCHAR2(10) not null,
Sname VARCHAR2(10),
age NUMBER,
constraint S# primary key (S#)
);
Course(C#, cname, credit)表,對應SQL語句如下:
create table COURSE
(
C# VARCHAR2(10) not null,
cname VARCHAR2(20),
credit NUMBER ,
constraint C# primary key (C#)
);
SC(s#, c#, score)表,對應SQL語句如下:
create table SC
(
S# VARCHAR2(5) not null,
C# VARCHAR2(5) not null,
score NUMBER not null,
constraint SC_PK primary key (S#, C#),
constraint C#_FK_COURSE foreign key (C#)
references COURSE (C#) on delete cascade ,
constraint S#_FK_STUDENT foreign key (S#)
references STUDENT (S#) on delete cascade
);
3.插入資料:
這裡由於需要一次性插入多條資料,而MySQL的多行插入語法INSERT INTO 某表 VALUES(各個值),VALUES(各個值);這樣會報錯,因為ORACLE不支援這種寫法;如果多個INSERT INTO VALUES(各個值);這樣以“;”隔開一同執行也是不行的,ORACLE也是不支援。
但是在這裡我們可以利用ORACLE 9i版本以後的語法,即”INSERT ALL INTO a表 VALUES(各個值) INTO a表 VALUES (其它值) INTO a表 VALUES(其它值) ….再跟一個SELECT 語句“。後邊跟的SELECT 語句我們可以從虛擬表裡查如 SELECT 1 FROM DUAL。
Student(S#, Sname, age)表,對應SQL語句如下:
insert all into student(s#,sname,age) values ('001','小A',20)
into student(s#,sname,age) values ('002','小B',21)
into student(s#,sname,age) values ('003','小C',20)
into student(s#,sname,age) values ('004','小D',21)
into student(s#,sname,age) values ('005','小E',20)
select 1 from dual;
select * from student for update;
建表結果如下:
Course(C#, cname, credit)表,對應SQL語句如下:
insert all into course(c#,cname,credit) values ('1','資料庫',3.5)
into course(c#,cname,credit) values ('2','人工智慧',3.5)
into course(c#,cname,credit) values ('3','體系結構',3.5)
into course(c#,cname,credit) values ('4','平行計算',3.5)
into course(c#,cname,credit) values ('5','資訊保安',3)
select 1 from dual;
select * from course for update;
建表結果如下:
SC(s#, c#, score)表,對應SQL語句如下:
insert all into sc(s#,c#,score) values ('001','1',90)
into sc(s#,c#,score) values ('002','1',100)
into sc(s#,c#,score) values ('003','1',80)
select 1 from dual;
select * from sc for update;
建表結果如下:
4.編寫儲存過程,實現修改指定學生的學號:
由於我們在SC表中聲名了一個references Student(S#)的外來鍵約束和一個references Cousre(C#)的外來鍵約束,當我們修改Student表中的S#欄位時,需要更新SC表中的外來鍵約束。遺憾的是,在Oracle環境下,不能設定外來鍵為update級聯,所以只能進行人工處理了。
思路是先遮蔽SC表的外來鍵約束,然後修改Student表中的學號,並且在保證一致性的情況下更新所有SC中該學生的學號,最後恢復(或新增)SC表外來鍵約束。
儲存過程實現如下:
create or replace procedure Revise_S#(s#_old in varchar2, s#_new in varchar2) is
begin
execute immediate 'alter table SC disable constraint s#_fk_student';
update student set s# = s#_new where s#=s#_old;
update sc set s# = s#_new where s#=s#_old;
execute immediate 'alter table SC enable constraint s#_fk_student';
end Revise_S#;
編寫SQL語句呼叫該儲存過程:
call Revise_S#('001','011');
select * from student;
select * from sc;
結果如下:
實現結果正確!
三.實驗(2)過程
1.建表:
系表:Dept(D#, Dname, S_Count),對應SQL語句如下:
create table DEPT
(
D# VARCHAR2(20) not null,
Dname VARCHAR2(20),
S_Count NUMBER,
constraint D# primary key (D#)
);
學生表:Stu(S#, Sname, age, D#),對應SQL語句如下:
create table STU
(
S# VARCHAR2(10) not null,
Sname VARCHAR2(10),
age NUMBER,
D# VARCHAR2(20),
constraint S# primary key (S#)
)
2.插入資料
系表:Dept(D#, Dname, S_Count),初始各個系的學生數設為0,對應SQL語句如下:
insert all into dept(d#,dname,s_count) values ('1','數學系',0)
into dept(d#,dname,s_count) values ('2','物理系',0)
into dept(d#,dname,s_count) values ('3','計算機系',0)
select 1 from dual;
select * from dept for update;
3.建立觸發器
為保持S_count和學生表中實際人數的一致性,使用觸發器的new和old變數特性,具體實現如下:
create or replace trigger keep_s_count
after insert or delete or update
on stu
for each row
begin
if deleting then
update dept set s_count = s_count - 1
where dept.d# = :old.d#;
end if;
if inserting then
update dept set s_count = s_count + 1
where dept.d# = :new.d#;
end if;
if updating('d#') then
update dept set s_count = s_count - 1
where dept.d# = :old.d#;
update dept set s_count = s_count + 1
where dept.d# = :new.d#;
end if;
end keep_s_count;
4.資料驗證
插入學生資訊:
insert all into stu(s#,sname,age,d#) values ('001','小A',20,'1')
into stu(s#,sname,age,d#) values ('002','小B',21,'2')
into stu(s#,sname,age,d#) values ('003','小C',20,'3')
select 1 from dual;
select * from stu for update;
select * from dept;
結果如下:
數學系、物理系和計算機系的人數都變為了1,插入正確。
刪除學生資訊:
delete from stu where s#=001;
select * from dept;
結果如下:
刪去數學系學生之後,dept對應行的人數變為0,刪除正確。
修改學生資訊:
update stu set d#='3' where s#='002';
select * from dept;
結果如下:
修改物理系學生資訊為計算機系後,dept表對應的計算機系人數增加了,修改正確。