数据库作业13:SQL练习8 - CHECK / CONSTRAINT / TRIGGER / PROCEDURE/ FUNCTION

第五章 数据库完整性
数据库完整性包括:实体完整性,参照完整性,用户定义的完整性

【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)         /*表级定义主码*/
         		); 

实体完整性:主码不能为空。
CREATE TABLE 用PRIMARY KEY来设置主码,完成实体完整性。
在定义列时设定列为主码叫做列级定义主码,而在定义完表后再定义表的主码叫做表级定义主码,两种方式都可。

【5.2】将SC表中的Sno,Cno属性组定义为码

	 CREATE TABLE SC
           (Sno CHAR(9)  NOT NULL, 
            Cno CHAR(4)  NOT NULL,  
            Grade SMALLINT,
            PRIMARY KEY (Sno,Cno)   
           ); 

这个例子中只能在表级定义主码,因为主码是组合式的,单一的属性不能唯一代表一个对象,所以不能使用列级定义主码。

【5.3】定义SC中的参照完整性

	 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)
             );

参照完整性规则:外码要么为空,要么为被参照关系的主码。
CREATE TABLE中用FOREIGN KEY短语定义哪些列为外码,用REFERENCES短语指明这些外码参照哪些表的主码。

参照完整性检查和违约处理:
对于被参照表Student和参照表SC有四种违约方式:
1.在参照表SC中插入元组,此元组的Sno属性值在被参照表中找不到一个元组。其Sno值与其相等,拒绝处理。
2.修改SC表中的一个元组,修改后该院组的Sno属性值在表Student中没有元组,其Sno属性值与之相等,拒绝处理。
3.从Student表中删除一个元组,SC中某些元组的Sno属性值在表Student中没有对应的相同Sno属性值元组。拒绝/级连删除/设置为空值。
4.修改Student表的Sno属性,使得SC表中某些元组的Sno属性在Student表中没有Sno属性对应,拒绝/级连删除/设置为空值。

处理策略:
1拒绝NO ACTION执行:不允许执行此操作,一般为默认策略。
2级联CASCADE操作:当删除和修改被参照表Student中的元组导致与参照表SC不一致时,删除或修改参照表中的所有导致不一致的元组。
3设置为空值:当删除和修改被参照表Student中的元组导致与参照表SC不一致时,将参照表中不一致的地方设置为空。

【5.4】显式说明参照完整性的违约处理示例

	  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) 
         	ON DELETE CASCADE       /*级联删除SC表中相应的元组*/
            ON UPDATE CASCADE,      /*级联更新SC表中相应的元组*/
         FOREIGN KEY (Cno) REFERENCES Course(Cno)	                    
            ON DELETE NO ACTION 	/*当删除course 表中的元组造成了与SC表不一致时拒绝删除*/
            ON UPDATE CASCADE       /*当更新course表中的cno时,级联更新SC表中相应的元组*/
           );

在将Sno,Cno属性作为SC的外码时,在后面加上ON DELECT(UPDATE) CASCADE(NO ACTION);
这里可以看到,若修改删除Student中的数据,则删除修改SC中相应的数据;修改Course中的Cno时,修改SC中的Cno,删除Course中的Cno时,不允许删除。

用户定义完整性:
1.属性上的约束条件:
列值非空(NOT NULL);列值唯一(UNIQUE);检查列值是否满足一个条件表达式(CHECK);
属性上的约束条件检查和违约处理:
插入元组或修改属性的值时,关系数据库管理系统检查属性上的约束条件是否被满足,如果不满足则操作被拒绝执行。

2.元组上的约束条件:
用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属性不允许取空值,Grade属性不允许取空值。

如果在标记定义实体完整性,则此时隐含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)
         );

Dname列的值唯一(UNIQUE), 并且不能取空值(NOT NULL).
Deptno不能为空。
在这里插入图片描述

【5.7】 Student表的Ssex只允许取“男”或“女”。

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

性别只能是 ‘男’或者‘女’。CHECK用来指定列值应该满足的条件。

	INSERT 
	INTO Studen
	VALUES('2014','xig','中',17,'M');
	--INSERT 语句与 CHECK 约束"CK__Studen__Ssex__0B91BA14"冲突。该冲突发生于数据库"Student",表"dbo.Studen", column '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)
           );
	INSERT 
	INTO SC
	VALUES('20121512 ','1',110);
	--INSERT 语句与 CHECK 约束"CK__SC__Grade__0E6E26BF"冲突。该冲突发生于数据库"Student",表"dbo.SC", column 'Grade'。

【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.%')
          );

CHECK定义了Ssex和Sname的约束条件。

【5.10】建立学生登记表Student,要求学号在90000~99999之间,姓名不能取空值,年龄小于30,性别只能是“男”或“女”

	CREATE TABLE Studen
      (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)
        );

在Student表上建立了5个约束条件,包括主码约束(命名为StudentKey)以及C1、C2、C3、C4四个列级约束。
提示:数据库中已存在名为 ‘C1’ 的对象。忘了啥时候有过了,就把C1写成C1_.

约束一栏中没有C2,NULL和NOT NULL 是不被放在约束栏里的。
在这里插入图片描述

完整性约束命名子句:CONSTRAINT <完整性约束条件名><完整性约束条件>
<完整性约束条件>包括NOT NULL、UNIQUE、PRIMARY KEY短语、FOREIGN KEY短语、CHECK短语等。
修改表中的完整性限制:使用ALTER TABLE语句修改表中的完整性限制。

【5.11】建立教师表TEACHER,要求每个教师的应发工资不低于3000元

	      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)
			);

【5.12】去掉例5.10 Student表中对性别的限制

	ALTER TABLE Studen
    DROP CONSTRAINT C4;

【5.13】修改表Studen中的约束条件,要求学号改为在900000~999999之间,年龄由小于30改为小于40

	ALTER TABLE Studen
        DROP CONSTRAINT C1_;
	ALTER TABLE Studen
	        ADD CONSTRAINT C1_ CHECK (Sno BETWEEN 900000 AND 999999);
	ALTER TABLE Studen
	        DROP CONSTRAINT C3;
	ALTER TABLE Studen
	        ADD CONSTRAINT C3 CHECK(Sage<40);

触发器:
任何用户对表的增、删、改操作均由服务器自动激活相应的触发器,触发器可以实施更为复杂的检查和操作,具有更精细和更强大的数据控制能力。

定义触发器格式:

	  CREATE TRIGGER <触发器名>  
       {BEFORE | AFTER} <触发事件> ON <表名>
       REFERENCING NEW|OLD ROW AS<变量>
       FOR EACH  {ROW | STATEMENT}
       [WHEN <触发条件>]<触发动作体>

触发事件:INSERT、DELECT或UPDATE;UPDATE OF<触发列,…>,即进一步指明修改哪些列时激活触发器。
AFTER表示在触发事件的操作执行之后激活触发器,BEFORE表示在触发事件的操作执行之前激活触发器。
FOR EACH ROW:行级触发器(每一行触发一次);FOR EACH STATEMENT:语句级触发器;

【5.21】当对表SC的Grade属性进行修改时,若分数增加了10%则将此次操作记录到下面表中SC_U(Sno,Cno,Oldgrade,Newgrade),其中Oldgrade是修改前的分数,Newgrade是修改后的分数。

	CREATE TRIGGER  SC_T		
	AFTER UPDATE OF Grade ON SC
    REFERENCING
	      OLD row AS OldTuple,
	      NEW row 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”附近有语法错误。

创建好SC_U(Sno,Cno,Oldgrade,Newgrade)。执行上列语句后报错。
在T-SQL中,应写成:

	CREATE TRIGGER SC_T
	ON SC
	AFTER
	UPDATE
	AS 
	--declare @变量名称 变量类型
		declare @Sno char(15)
		declare @Cno char(4)
		declare @Oldgrade SMALLINT
		declare @Newgrade SMALLINT
	
	--IF(UPDATE(Grade)),返回一个布尔值,指示是否对表或视图的指定列进行了 INSERT 或 UPDATE 尝试。测试触发器是否应执行UPDATE操作。
		IF(UPDATE(Grade))
		SELECT @OLDgrade =Grade FROM DELETED
		SELECT @NEWgrade =Grade FROM INSERTED
		SELECT @SNO =Sno FROM SC
		SELECT @CNO =Cno FROM SC
	
		INSERT 
		INTO SC_U(Sno,Cno,OldGrade,NewGrade)  
		VALUES(@Sno,@Cno,@Oldgrade,@Newgrade);

数据测试:
在这里插入图片描述

UPDATE SC
SET Grade=100
WHERE Grade<100;

SELECT *
FROM SC_U

SC_U中结果,只有改后分数大于原来分数10%的记录。
在这里插入图片描述

【5.22】将每次对表Student的插入操作所增加的学生个数记录到表StudentInsertLog中。

	--新建表,存储学生人数
	CREATE TABLE StudentInsertLog
	(
	Numbers INT
	)
	--新建表,存储用户名和操作时间
	CREATE TABLE StudentInsertLogUser
	(
	  UserName nchar(10),
	  DateAndTime datetime
	)
	--新建触发器,当插入新的学生记录时,触发器启动,自动在StudentInsertLog记录学生人数
	CREATE TRIGGER Student_Count
	ON Student  	         
	AFTER
	INSERT
	AS 
	    INSERT 
		INTO StudentInsertLog(Numbers)
		SELECT COUNT(*) 
		FROM Student
	--当插入新的学生记录时,触发器启动,自动在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 ('201215135','王五','男',18,'CS');
	SELECT * FROM Student
	SELECT * FROM StudentInsertLog
	SELECT * FROM StudentInsertLogUser

在这里插入图片描述
在这里插入图片描述

【5.23】定义一个BEFORE行级触发器,为教师表Teacher定义完整性规则“教授的工资不得低于4000元,如果低于4000元,自动改为4000元”

--创建Teacher表
	CREATE TABLE Teacher
	(
		Name CHAR(10),
		Salary SMALLINT,
		Job CHAR(10)
	)
	--加入数据
	INSERT
	INTO Teacher(name,Salary,Job)
	VALUES('清风',3500,'教授');
	INSERT
	INTO Teacher(name,Salary,Job)
	VALUES('朗月',4500,'教授');
	INSERT
	INTO Teacher(name,Salary,Job)
	VALUES('枝枝',3000,'副教授');

--创建触发器
		CREATE TRIGGER Insert_Or_Update_Sal
	ON Teacher
	
	FOR INSERT , UPDATE
	AS
		declare @Salary SMALLINT
		declare @Job CHAR(10)
		
		IF(UPDATE(Salary))
		SELECT @Salary=Salary FROM INSERTED
		SELECT @Job=Job FROM Teacher

		IF (@Job='教授' AND @Salary < 4000)
		UPDATE Teacher
		SET Salary=4000
		WHERE Job='教授' AND Salary<4000
	INSERT 
	INTO Teacher
	VALUES('yuan',3200,'教授');

	SELECT *
	FROM Teacher

得到如下数据:
在这里插入图片描述

存储过程:由过程化SQL语句,经编译和优化后存储在数据库服务器中,可以被反复调用,运行速度较快。

【8.8】利用存储过程来实现下面的应用:从账户1转指定数额的款项到账户2中。

--建立新表Account,写入两个用户
	DROP TABLE IF EXISTS Account;
	
	CREATE TABLE Account
	(
		accountnum CHAR(3),	
		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 = 51		--转出金额
		--账户存款不足
--账户不存在情况
		EXEC	Proc_TRANSFER
		@inAccount = 100,	--转入账户
		@outAccount = 102,	--转出账户
		@amount = 50		--转出金额
--转入账户不存在
			EXEC	Proc_TRANSFER
		@inAccount = 101,	--转入账户
		@outAccount = 103,	--转出账户
		@amount = 50		--转出金额
--转出账户不存在或账户中没有存款

【8.9】从账户01003815868转10000原到01003813828账户中

	DROP TABLE IF EXISTS Account;

	CREATE TABLE Account
	(
	accountnum CHAR(15),	-- 账户编号
	total FLOAT		-- 账户余额
	);
	
	
	INSERT INTO Account VALUES(01003815868,20000)
	INSERT INTO Account VALUES(01003813828,10000)
	EXEC	Proc_TRANSFER
			@inAccount = 01003813828,	--转入账户
			@outAccount = 01003815868,	--转出账户
			@amount = 10000		--转出金额
	
	SELECT * FROM Account

在这里插入图片描述

参考:

添加链接描述
添加链接描述
添加链接描述
添加链接描述
添加链接描述

发布了14 篇原创文章 · 获赞 17 · 访问量 9368

猜你喜欢

转载自blog.csdn.net/karive/article/details/105252302