SQL入門(3):定義約束/斷言assertion/觸發器trigger


本文介紹數據庫的完整性

完整性控制程序: 指定規則,檢查規則 (規則就是約束條件)

動態約束 intergrity constraint::=(O,P,A,R)

O : 數據集合, 約束的對象 ?: 列, 多列的元組集合

P: 謂詞條件: 什么樣的約束?

A: 觸發條件: 什么時候檢查?

R: 響應動作: 不滿足怎么辦?

按照約束對象分類:

(1)完整性約束條件: 施加在某一列上, 比如sage<25 and sage<40

(2)關系完整性約束條件: 施加在表上, 涉及多列, 2<=hours/credit<=16

按照約束來源分類:

(1)結構約束: 主鍵約束, 外鍵約束,是否允許空值等 primary key, foreign key, not null

(2) 內容約束:  取值范圍, check(sage<25 and sage<40)

按照約束狀態分類:

(1) 靜態約束: 要求DB在任何時候都要滿足的約束

(2) 動態動態: DB改變狀態時要滿足的約束, 例如salary 只能加不能減, 不能由1000改為500.---> 觸發器

SQL支持如下幾種約束: 

靜態約束中的列完整性 與表完整性,  動態約束中的觸發器

(一) 靜態約束

實現: create table

(1) col_constr 列約束 (一種域約束類型, 對單一列的值進行約束)

not null 列值非空

primary key 主鍵

not null + unique 就是非空+唯一性 ,實際上就是一個主鍵

check (search_condition) 列值滿足的條件, 

references tablename(colname) , colname 是tablename 的主鍵

on delete[ cascade| set null], 則刪除被引用表的某一列v值時, 要將本表該列

值為v 的記錄刪除 或者 列值更新為null, 缺省為無操作. 

例: 創建一個表 student

create table student (sno char(8) not null unique, sname char(10), 
--not null unique 表示主鍵
ssex char(2) constraint ctssex check(ssex='' or ssex=''),  
-- ctssex 是約束constraint 的名字. 之后可以單獨處理
sage integer check(sage>=1 and sage<150),  -- 沒有定義約束名, 則之后不能單獨處理
dno char(2) references dept(dno) on delete cascade, -- dno 在dept表中 是主鍵, 
sclass char(6));

on delete cascade 表示如果dept表中的某個'01'系被刪除了,

那么在student 表中該系所有學生的dept 值為null, 如果沒有加這個, 那么dept表中的刪除操作對

student表沒有影響.

create table course (cno char(3), cname char(10), chours integer,
credit float(1) constraint ctcredit check(credit>=1.0 and credit <=6.0),
tno char(3) references teacher(tno) on delete cascade);
-- 或者通過alter
alter table course add constraint
cs_tno foreign key(tno) references  teacher(tno) on delete cascade; 
-- 移除約束
alter table course drop constraint cs_tno;

補充: unique 和not null 

create table tbl1(name1 varchar(10), num1 varchar(10), 
constraint cs_num1 unique(num1));
-- 或者
create table tbl1(name1 varchar(10), num1 varchar(10) unique);
-- 之后再添
alter table tbl1 add constraint  cs_num1 unique(num1);
alter table tbl1 drop constraint ;
-- 非空約束
create table tbl1(name1 varchar(10), num1 varchar(10) not null);
-- 新增非空約束
alter table tbl1 modify num1 not null;
-- 刪除非空約束 不是用drop
alter table tbl1 modify num1 null;

 

(2) table_constr 表約束, 用於多列或者元組的值進行約束

create table student ( sno char(5) not null unique,sname char(5),
ssex char(2) constraint ctssex check(ssex='' or ssex='') ,
sage integer check(sage>1 and sage<150).
dno char(3) references dept (dno) on delete cascade,
sclass char(5),primary key(sno)); 

--primary key(sno) 可以放在sno 這一列的后面, 也可以放在最后這里, 看成是表約束

create table course (cno char(3), cname char(10), chours integer,
credit float(1) constraint ctcredit check(credit>=1.0 and credit <=6.0),
tno char(3) references teacher(tno) on delete cascade,
primary key (cno), constraint ctcc check(chours/credit=12));
-- 嚴格約束12課時對應1個學分
create table sc(sno char(5), cno char(3),
score float(1) constraint ctscore check(score>=0.0 and score<=100.0),
foreign key(sno) references student(sno) on delete cascade,
foreign key(cno) references course(cno) on delete cascade);

注意: check 后面的條件可以是select from where 語句

create table sc(sno char(5) check (sno in (select sno from student)), 
cno char(3), check(cno in (select cno from course)), --相當於外鍵 score float(1) constraint ctscore check(score>=0.0 and score<=100.0);

注意: create table 中的約束條件 可以在后面根據需要進行撤銷 ,也可以追加約束

alter  table  tablename + 

add 追加約束,  也可以增加新的一列

drop 刪除一列的約束,或者刪除一列,

modify 修改

alter table sc drop constraint ctscore; -- 撤銷對score的約束ctscore;
alter table sc
modify ( score float(1) constraint nctscore check(score>0.0 and score<=150.0)); 
-- 修改約束 alter table sc add ( score float(1) constraint nctscore check(score>0.0 and score<=150.0));
-- 增加約束

(3) 斷言 assertion 

一個斷言就是一個謂詞表達式, 它表達了希望數據庫總能滿足的條件, 表約束與 列約束就是一些特殊的斷言.

還有復雜的斷言  create assertion [assertion name] check [predicate]

那么之后數據庫的每一次更新去判斷是否違反該斷言, 斷言測試增加了數據庫維護的負擔, 沒事不要使用!! 

例如: 每位教師同一時間段不能在兩個不同的地方上課.

實例1: 已知下列4張表

borrower(client_name,loan_num) 客戶以及他的貸款

account(account_num,balance) 賬戶和余額

depositor(account_num, client_name) 賬戶與客戶名

loan(loan_num, amount)  每一筆貸款

現在規定: 每一筆貸款 , 要求至少這個借款者的賬戶中有最低余額500元.

create assertion balance_cst check
(not exists (select * from loan
where not exists (select * from borrower b, depositor d, account a
where loan.loan_num=b.loan_num and
b.client_name=d.client_name and 
a.account_num=d.account_num and a.balance>=500)));

實例2: 現有3張表

account(branch_name, account_num, balance) 分行 賬戶 與 余額

loan(branch_name,loan_num,amount) 分行的每一筆貸款

branch(branch_name,..) 分行信息

每一個分行的貸款總量要小於該分行所有賬戶的余額總額 (不存在某一個分行 它的貸款額大於余額)

create assertion sum_cst check
(not exists (select * from branch where (select sum(amount) from loan
where loan.branch_name=branch.branch_name) >=
(select sum(balance) from account where account.branch_name=branch.branch_name)));

 (二) 動態約束

以上 create table 中的表約束與列約束 是靜態約束, 下面介紹動態約束--> 觸發器 trigger

動態約束是一種過程完整性的約束, 相比之下, 之前的create table 的約束是非過程性約束

動態約束是一個程序, 該程序可以在特定的時刻被自動觸發執行: 比如在一次更新之前, 

或者一次更新之后的執行. 

動態約束 intergrity constraint::=(O,P,A,R), O P A R 都需要定義, 再來回顧下 

O : 數據集合, 約束的對象 ?: 列, 多列的元組集合

P: 謂詞條件: 什么樣的約束?

A: 觸發條件: 什么時候檢查?

R: 響應動作: 不滿足怎么辦?

以下是Oracle 的觸發器的語法例子, 在SQL server中 語法略有差別, 但是思路一致. ..

 創建觸發器的基本語法:

create trigger  trigger_name

before| after  [insert | delete|update] [of colname] on tablename 

for each row| for each statement

when [search_condition]

[statement]

[begin  atomic statement; ... end;]  --多個條件

注意: row , as 可以省略! 

實例(1):  當teacher表更新元組時, 控制其工資只能漲不能跌

create trigger teacher_sal-- 觸發器名字
before update of salary on teacher -- 作用在什么表的什么列
referencing new x, old y -- 定義更新前后的值
for each row when(x.salary<y.salary) -- 對每一條記錄都要檢查, 
    begin --如果違反則執行
        raise_application_error(-20003,'invalid salary on update');
        -- Oracle的錯誤處理函數, 提示無效更新
    end;        

實例(2) : student(sno, sname, sumcourse), sumcourse 表示該同學已經學習的課程門數,

初始值是0, 以后每修一門課都要對其+1, 設計一個觸發器自動完成這個功能.

create trigger sumc
after insert on sc -- 對於sc 的新增信息 作出反應
referencing new row newI-- 定義更新后的行=newi
for each row
    begin  -- 執行操作
        update student set sumsourse=sumcourse+1 
        where sno=:newi.sno;  -- 這條記錄(行)對應的學號
    end;        

 

實例(3) :student(sno, sname, sage,ssex,scalss) 中某一個學生變動其主碼sno

,則在sc 表中該同學的學號也要相應改變

create trigger upd_sno
after update of sno on student --指明更新的地方
referencing old oldi, new  newi
for each row
    begin
        update sc set sno=newi.sno where sno=: oldi.sno;
    end;

實例(4) :student(sno, sname, sumcourse)中刪除某一個學生sno時, 在sc 中該學生的

選課記錄也要刪除

create trigger del_sno
after delete on student
referencing old oldi
for each row
    begin
        delete sc where sno=:oldi.sno;
    end;

實例(5) : student(sno, sname, sumcourse)中刪除某一個學生sno時, 在sc 中該學生的sno設置為null

create trigger del_sno
after delete on student
referencing old oldi
for each row
    begin
        update sc set sno=null where sno=:oldi.sno;
    end;    

實例(6) :假設有兩張表, dept(dno,dname,dean) ,該表字段是系號 系名 系主任名, 以及

teacher(tno,tname,dno,salary) . 現在需要控制 在對dept 的dean 做更新的時候,

必須滿足dean 的工資是同一系里最高的, 否則更新報錯.

create trigger dean_sal 
before update of dean on dept  -- 對dept 的dean 做更新的時候
referencing old oldi , new newi -- 更新前后的新 舊定義
for each row when(dean not in (select tname from teacher where dno=:newi.dno and salary> = all(select salary from teacher where dno=:newi.dno )))-- 同系教師工資 begin -- 不滿足條件時 raise_application_error(-20003,'invalid dean on update'); end;

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM