【2019-2020春學期】數據庫作業13:SQL練習8 - CHECK / CONSTRAINT / TRIGGER / PROCEDURE/ FUNCTION

實體完整性

1、定義實體完整性
關係模型的實體完整性在CREATE TABLE中用PRIMARY KEY定義。對單屬性構成的碼有兩種說明方法,一種是定義爲列級約束條件,另一種是定義爲表級約束條件。對多個屬性構成的碼只有一種說明方法,即定義爲表級約束條件。
【例5.1】將Student表中的Sno屬性定義爲碼

CREATE TABLE Student
	(Sno CHAR(9) PRIMARY KEY,  /*在列級定義主碼*/
	 Sname CHAR(20) NOT NULL,
	 Ssex CHAR(2),
	 Sage SMALLINT,
	 Sdept CHAR(20)
	 )

或者

CREATE TABLE Student
	(Sno CHAR(9),
	 Sname CHAR(20) NOT NULL,
	 Ssex CHAR(2),
	 Sage SMALLINT,
	 Sdept CHAR(20),
	 PRIMARY KEY(Sno)  /*在表級定義主碼*/
	 )

在這裏插入圖片描述
【例5.2】將SC表中的Sno、Cno屬性組定義爲碼

CREATE TABLE SC
	(Sno CHAR(9) NOT NULL,
	 Cno CHAR(4) NOT NULL,
	 Grade SMALLINT,
	 PRIMARY KEY(Sno,Cno) /*只能在表級定義主碼*/
	 )

在這裏插入圖片描述

參照完整性

1、定義參照完整性
關係模型的參照完整性在CREATE TABLE中用FOREIGN KEY短語定義哪些列爲外碼,用REFERENCES短語指明這些外碼參照哪些表的主碼。
例如,關係SC中一個元組表示一個學生的選修的某門課程的成績,(Sno,Cno)是主碼。Sno,Cno分別參照Student表的主碼和Course表的主碼
【例5.3】定義SC中的參照完整性
此處用到了Course表,所以我們需要新建一個Course表

CREATE TABLE Course
	(Cno CHAR(4) NOT NULL,
	 Cname CHAR(40),
	 Ccredit SMALLINT,
	 PRIMARY KEY(Cno), /*在表級定義實體完整性*/
	 )

在這裏插入圖片描述

CREATE TABLE SC
	(Sno CHAR(9) NOT NULL,
	 Cno CHAR(4) NOT NULL,
	 Grade SMALLINT,
	 PRIMARY KEY(Sno,Cno), /*在表級定義實體完整性*/
	 FOREIGN KEY(Sno) REFERENCES Student(Sno), /*在表級定義參照完整性*/
	 FOREIGN KEY(Cno) REFERENCES Course(Cno)
	 )

在這裏插入圖片描述
2、參照完整性檢查和違約處理
【例5.4】顯式說明參照完整性的違約處理示例

CREATE TABLE SC
	(Sno CHAR(9) NOT NULL,
	 Cno CHAR(4) NOT NULL,
	 Grade SMALLINT,
	 PRIMARY KEY(Sno,Cno), /*在表級定義實體完整性,Sno、Cno都不爲空值*/
	 FOREIGN KEY(Sno) REFERENCES Student(Sno)/*在表級定義參照完整性*/
		ON DELETE CASCADE  /*當刪除Student表中的元組時,級聯刪除SC表中對應的元組*/
		ON UPDATE CASCADE, /*當更新Student表中的Sno時,級聯更新SC表中的相應元組*/
	 FOREIGN KEY(Cno) REFERENCES Course(Cno)
		ON DELETE NO ACTION   /*當刪除Course表中的元組造成與SC表不一致的時候,拒絕刪除*/
		ON UPDATE CASCADE     /*當更新Course表中的Cno時,級聯更新SC表中的相應元組*/
	 )

用戶定義完整性

1、屬性上的約束條件
在CREATE TABLE中定義屬性的同時,可以根據應用要求定義屬性上的約束條件,即屬性值限制,包括:
列值爲非空(NOT NULL)
列值唯一(UNIQUE)
檢查列值是否滿足一個條件表達式(CHECK短語)
【例5.5】在定義SC表時,說明Sno、Cno、Grade屬性不允許爲空值

CREATE TABLE SC
	(Sno CHAR(9) NOT NULL,
	 Cno CHAR(4) NOT NULL,
	 Grade SMALLINT NOT NULL,
	 PRIMARY KEY(Sno,Cno), /*在表級定義實體完整性,Sno、Cno都不爲空值*/
	 )

【例5.6】建立部門表DEPT,要求部門名稱Dname列值取值唯一,部門編號Deptno列爲主碼

CREATE TABLE DEPT
	(Deptno NUMERIC(2),
	 Dname CHAR(9)UNIQUE NOT NULL,
	 Location CHAR(10),
	 PRIMARY KEY(Deptno), /*在表級定義實體完整性,Sno、Cno都不爲空值*/
	 )

【例5.7】Student表的Ssex只允許取“男”或“女”

CREATE TABLE Student
	(Sno CHAR(9) PRIMARY KEY,
	Sname CHAR(8)NOT NULL,
	Ssex CHAR(2)CHECK(Ssex IN('男','女')),
	Sage SMALLINT,
	Sdept CHAR(20)
	 )

檢測:

INSERT INTO Student(Sno,Sname,Ssex,Sdept,Sage)
VALUES('201215121','佟陸陸','女','IS',20);
SELECT * FROM Student

在這裏插入圖片描述

INSERT INTO Student(Sno,Sname,Ssex,Sdept,Sage)
VALUES('201215122','佟伊','M','IS',20);
SELECT * FROM Student

在這裏插入圖片描述
報錯原因就是在Student表中Ssex有約束條件,輸入的內容不符合約束條件所以報錯了
在這裏插入圖片描述
【例5.8】SC表的Grade的值應該在0和100之間

CREATE TABLE SC
	(Sno CHAR(9),
	Cno CHAR(4),
	Grade SMALLINT CHECK(Grade>=0 AND Grade<=100),
	PRIMARY KEY(Sno,Cno),
	FOREIGN KEY(Sno) REFERENCES Student(Sno),
	FOREIGN KEY(Cno) REFERENCES Course(Cno),
	 )

2、元組上約束條件的定義
與屬性上約束條件的定義類似,在CREATE TABLE語句中可以用CHECK語句定義元組上的約束條件,即元組級的限制,同屬性值限制相比,元組級的限制可以設置不同屬性之間的取值的相互約束條件
【例5.9】當學生的性別是男的時候,其名字不能以Ms.打頭

CREATE TABLE Student
	(Sno CHAR(9),
	Sname CHAR(8) NOT NULL,
	Ssex CHAR(2),
	Sage SMALLINT,
	Sdept CHAR(20),
	PRIMARY KEY(Sno),
	CHECK(Ssex='女' OR Sname NOT LIKE 'Ms.%')
	 )

完整性約束命名子句

1、完整性約束命名子句
CONSTRAINT<完整性約束條件名><完整性約束條件>
完整性約束條件包括NOT NULL、UNIQUE、PRIMARY KEY、FOREIGN KEY、CHECK短語
【例5.10】建立學生登記表Student,要求學號在90000-99999之間,姓名不能取空值,年齡小於30,性別只能是“男”或“女”

CREATE TABLE Student
	(Sno NUMERIC(6)
		CONSTRAINT C1 CHECK(Sno BETWEEN 90000 AND 99999),
	Sname CHAR(20)
		CONSTRAINT C2 NOT NULL,
	Sage NUMERIC(3)
		CONSTRAINT C3 CHECK(Sage<30),
	Ssex CHAR(2)
		CONSTRAINT C4 CHECK(Ssex IN('男','女')),
		CONSTRAINT StudentKey PRIMARY KEY(Sno)
	 )

【例5.11】建立教師表TEACHER,要求每個教師的應發工資不低於3000元。應發工資列Sal與扣除項Deduct之和

CREATE TABLE TEACHER
	(Eno NUMERIC(4) PRIMARY KEY,
	Ename CHAR(10),
	Job CHAR(8),
	Sal NUMERIC(7,2),
	Deduct NUMERIC(7,2),
	Deptno NUMERIC(2),
	CONSTRAINT TEACHERKey FOREIGN KEY(Deptno)
	REFERENCES DEPT(Deptno),
	CONSTRAINT C1 CHECK(Sal+Deduct>=3000)
	 )

2、修改表中的完整性限制
可以使用ALTER TABLE語句修改表中的完整性限制
【例5.12】去掉例5.10Student表中對性別的限制

ALTER TABLE Student
	DROP CONSTRAINT C4

【例5.13】修改表Student中的約束條件,要求學號在900000-999999之間,年齡由小於30改爲小於40

ALTER TABLE Student
	DROP CONSTRAINT C1
ALTER TABLE Student
	ADD CONSTRAINT C1 CHECK(Sno BETWEEN 900000 AND 999999)
ALTER TABLE Student
	DROP CONSTRAINT C3
ALTER TABLE Student
	ADD CONSTRAINT C3 CHECK(Sage<40)

觸發器

觸發器也叫做事件-條件-動作規則。當特定的系統事件(如對一個表的增、刪、改操作,事物的結束等)發生時,對規則的條件進行檢查,如果條件成立則執行規則中的動作,否則不執行該動作。規則中的動作體可以很複雜,可以涉及其他表和其他數據庫對象,通常是一段SQL存儲過程
SQL使用CREATE TRIGGER命令建立觸發器,其格式爲:
CREATE TRIGGER<觸發器名> //每當觸發事件發生的時候,該觸發器被激活
{BEFORE|AFTER}<觸發事件>ON<表名> //指明觸發器激活的時間是執行觸發事件前或後
REFERENCING NEW|OLD ROW AS<變量> //REFERENCING指出引用的變量
FOR EACH{ROW|STATEMENT} //定義觸發器的類型,指明動作體執行的頻率
[WHEN<觸發條件>]<觸發動作體> //僅當觸發條件爲真時才執行觸發動作體
【例5.21】當對錶SC的Grade屬性進行修改時,若分數增加了10%,則將此次操作記錄到另一個表SC_U(Sno,Cno,Oldgrade,Newgrade)中,其中Oldgrade是修改前的分數,Newgrade是修改後的分數
在進行該操作之前還需要對Student表,Course表,SC表添加數據
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
先定義表SC_U

CREATE TABLE SC_U
	(Sno CHAR(9) PRIMARY KEY,  /*在列級定義主碼*/
	 Cno CHAR(9),
	 Oldgrade SMALLINT,
	 Newgrade SMALLINT
	 )
CREATE TRIGGER SC_T
AFTER UPDATE OF Grade ON SC
REFERENCING
	OLDROW AS OldTuple,
	NEWROW AS NewTuple
FOR EACH ROW
WHEN(NewTuple.Grade>=1.1*OldTuple.Grade)
	INSERT INTO SC_U(Sno,Cno,OldGrade,NewGrade)
	VALUES(OldTuple.Sno,OldTuple.Cno,OldTuple.Grade,NewTuple.Grade)

在此處出現報錯:“AFTER”附近有語法錯誤。”
T-SQL的寫法與標準SQL的寫法是不同的。以下是T-SQL的寫法
這時候應該改爲

/*新建表SC_U,記錄修改的記錄*/
CREATE TABLE SC_U(
	Sno CHAR(9),
	Cno CHAR(4),
	Oldgrade SMALLINT,
	Newgrade SMALLINT
)

/*新建觸發器SC_T,學生分數增加10%,觸發器啓動,自動在SC_U裏記錄信息*/
CREATE TRIGGER SC_T
ON SC
FOR UPDATE
AS
	declare @OLD SMALLINT
	declare @NEW SMALLINT
	declare @SNO CHAR(9)
	declare @CNO CHAR(4)
IF(UPDATE(Grade))
	BEGIN
	select @OLD =Grade FROM DELETED
	select @NEW =Grade FROM INSERTED
	select @SNO =Sno FROM SC
	select @CNO =Cno FROM SC
	
	INSERT INTO SC_U(Sno,Cno,Oldgrade,Newgrade)
	VALUES (@SNO,@CNO,@OLD,@NEW)
END

這時當我們修改數據進行檢測,第一個數據增加大於10%,第二個數據增加小於10%,第三個數據不變


UPDATE SC
SET Grade=90
WHERE Sno='201215121' AND Cno='1'

UPDATE SC
SET Grade=61
WHERE Sno='201215121' AND Cno='2'

SELECT * FROM SC_U

在這裏插入圖片描述
在這裏插入圖片描述
【例5.22】將每次對錶Student的插入操作所增加的學生個數記錄到表Student-InsertLog中

/*新建表StudentInsertLog,存儲學生人數*/
CREATE TABLE StudentInsertLog(
	Numbers INT
)

/*新建表StudentInsertLogUser存儲用戶名和操作時間*/
CREATE TABLE StudentInsertLogUser(
	UserName NCHAR(10),
	DateAndTime datetime
)
/*新建觸發器Student_Count,插入新的學生記錄的時候,觸發器啓動,自動在StudentInsertLog裏記錄學生的人數*/
CREATE TRIGGER Student_Count
ON Student
AFTER 
INSERT
AS
	INSERT INTO StudentInsertLog(Numbers)
	SELECT COUNT(*) FROM Student
/*新建觸發器Student_Time,當插入新的學生記錄時,觸發器啓動,自動在StudentInsertLogUser記錄用戶名和操作時間*/
CREATE TRIGGER Student_Time
ON Student
AFTER
INSERT
AS
	declare @UserName nchar(10)
	declare @DateTime datetime

	select  @UserName=system_user
	select  @DateTime=CONVERT(datetime,GETDATE(),120)

	INSERT INTO StudentInsertLogUser(UserName,DateAndTime)
	VALUES(@UserName,@DateTime)
/*測試觸發器效果*/
INSERT 
INTO Student
VALUES('201215122','王超','男',19,'CS')
SELECT * FROM Student
SELECT * FROM StudentInsertLogUser
SELECT * FROM StudentInsertLog

在這裏插入圖片描述
在這裏插入圖片描述
【例5.23】定義一個BEFORE行級觸發器,爲教師表Teacher定義完整性規則,教授的工資不低於4000元,如果低於4000元,自動改爲4000元
首先我們需要先建立一張Teacher表,並且爲Teacher表添加數據

/*新建表Teacher,記錄教師信息*/
CREATE TABLE Teacher(
	Tno CHAR(9),
	Tname CHAR(9),
	Job CHAR(9),
	Sal SMALLINT
)
INSERT
INTO Teacher(Tno,Tname,Job,Sal)
VALUES('201201','溫寧','講師',3000)
INSERT
INTO Teacher(Tno,Tname,Job,Sal)
VALUES('201202','焦炎','教授',3000)
INSERT
INTO Teacher(Tno,Tname,Job,Sal)
VALUES('201203','王超','教授',5000)

SELECT *
FROM Teacher

在這裏插入圖片描述
標準SQL寫法:

/*新建觸發器Insert_Or_Update_Sal,教授Sal小於4000就自動改爲4000*/
CREATE TRIGGER Insert_Or_Update_Sal
BEFORE INSERT OR UPDATE ON Teacher
REFERENCING NEW row AS newTuple
FOR EACH ROW
BEGIN
	IF(newtuple.Job='教授')AND(newtuple.Sal<4000)
		THEN newtuple.Sal=4000
	END IF
END

T-SQL寫法:

/*新建觸發器Insert_Or_Update_Sal,教授工資低於4000,觸發器啓動,自動修改爲4000*/
CREATE TRIGGER Insert_Or_Update_Sal
ON Teacher
FOR UPDATE,INSERT
AS
	IF UPDATE(Sal)
	BEGIN
	declare @TNO CHAR(9)
	declare @TNAME CHAR(9)
	declare @JOB CHAR(9)
	declare @SAL SMALLINT

	select @SAL = Sal FROM INSERTED
	select @TNO =Tno FROM Teacher
	select @TNAME =Tname FROM Teacher
	select @JOB =Job FROM Teacher
	
	IF(@SAL<4000 AND @JOB='教授')
	UPDATE Teacher
	SET SAL=4000
	WHERE Sal<4000 AND Job='教授'
END

接下來進行測試:

INSERT 
INTO Teacher
VALUES('201504','張臣扉','教授',2500)


SELECT * FROM Teacher

在這裏插入圖片描述
刪除觸發器語法:
DROP TRIGGER <觸發器名> ON <表名>
【例8.8】利用存儲過程實現下面的應用:從賬戶1轉指定數額的款項到賬戶2中,假設賬戶關係爲Account(Accountnum,Total)

/*建立新表Account,寫入兩個用戶*/
DROP TABLE IF EXISTS Account;
CREATE TABLE Account
(
accountnum INT,
total FLOAT 
)

INSERT INTO Account VALUES(101,50)
INSERT INTO Account VALUES(102,100)
SELECT * FROM Account

在這裏插入圖片描述

/*建立存儲過程*/
IF(exists(select * from sys.objects where name='Proc_TRANSFER'))
	DROP PROCEDURE Proc_TRANSFER
GO
CREATE PROCEDURE Proc_TRANSFER
@inAccount INT,@outAccount INT,@amount FLOAT
/*定義存儲過程TRANSFER,參數爲傳入賬戶,轉出賬戶,轉賬額度*/
AS	
BEGIN TRANSACTION TRANS
	DECLARE /*定義變量*/
	@totalDepositOut Float,
	@totalDepositIn Float,
	@inAccountnum INT;
	/*檢查轉出賬戶的餘額*/
	SELECT @totalDepositOut=total FROM Account WHERE accountnum=@outAccount;
	/*如果轉出賬戶不存在或者是賬戶裏沒有存款*/
	IF @totalDepositOut IS NULL
		BEGIN
			PRINT '轉出賬戶不存在或者賬戶中沒有存款'
			ROLLBACK TRANSACTION TRANS; /*回滾事務*/
			RETURN;
		END;
	/*如果賬戶存款不足*/
	IF @totalDepositOut<@amount
		BEGIN
			PRINT '賬戶存款不足'
			ROLLBACK TRANSACTION TRANS; /*回滾事務*/
			RETURN;
		END
	/*檢查轉入賬戶的狀態*/
	SELECT @inAccountnum=accountnum FROM Account WHERE accountnum=@inAccount;
	/*如果轉入的賬戶不存在*/
	IF @inAccountnum IS NULL
		BEGIN
			PRINT '轉入賬戶不存在'
			ROLLBACK TRANSACTION TRANS;
			RETURN;
		END;
	/*如果條件都沒有異常,開始轉賬*/
	BEGIN
		UPDATE Account SET total =total -@amount WHERE accountnum=@outAccount;
		/*修改轉出賬戶餘額,減去轉出額*/
		UPDATE Account SET total =total +@amount WHERE accountnum=@inAccount;
		/*修改轉入賬戶餘額,加上轉出額*/
		PRINT '轉賬完成,請取走銀行卡'
		COMMIT TRANSACTION TRANS;
		RETURN;
	END

測試:

/*測試,正常情況*/
EXEC	Proc_TRANSFER
		@inAccount = 101,	--轉入賬戶
		@outAccount = 102,	--轉出賬戶
		@amount = 50		--轉出金額

SELECT * FROM Account

在這裏插入圖片描述

/*測試,餘額不足情況*/
EXEC	Proc_TRANSFER
		@inAccount = 101,	--轉入賬戶
		@outAccount = 102,	--轉出賬戶
		@amount = 100		--轉出金額

在這裏插入圖片描述

/*測試,轉入賬戶不存在情況*/
EXEC	Proc_TRANSFER
		@inAccount = 100,	--轉入賬戶
		@outAccount = 102,	--轉出賬戶
		@amount = 50		--轉出金額

在這裏插入圖片描述

/*測試,轉出賬戶不存在情況*/
EXEC	Proc_TRANSFER
		@inAccount = 102,	--轉入賬戶
		@outAccount = 103,	--轉出賬戶
		@amount = 50		--轉出金額

【例8.9】從賬戶01003815868 轉10000元到01003813828賬戶中

SELECT * FROM Account

INSERT INTO Account VALUES(01003815868,20000)
INSERT INTO Account VALUES(01003813828,10000)
EXEC	Proc_TRANSFER
		@inAccount = 01003813828,	--轉入賬戶
		@outAccount = 01003815868,	--轉出賬戶
		@amount = 10000		--轉出金額

SELECT * FROM Account

在這裏插入圖片描述

心得:

觸發器真的有點麻煩,我終於寫完它了(感動(T^T))

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章