【高效学数据库】事务、完整性约束、数据安全

本专栏将从基础开始,循序渐进的讲解数据库的基本概念以及使用,希望大家都能够从中有所收获,也请大家多多支持。
专栏地址: 数据库必知必会
如果文章知识点有错误的地方,请指正!大家一起学习,一起进步。
如果感觉博主的文章还不错的话,还请关注、点赞、收藏三连支持一下博主哦

事务

事务(transaction)由查询和更新语句的序列组成。SQL标准规定当一条SQL语句被执行,就隐式地开始了一个事务。下列SQL语句之一会结束一个事务:

  • Commit work:提交当前事务,也就是将该事务所做的更新在数据库中持久保存。在事务被提交后,一个新的事务自动开始。

  • Rollback work:回滚当前事务,即撤销该事务中所有SQL语句对数据库的更新。这样,数据库就恢复到执行该事务第一条语句之前的状态

一个事务例子:

update account set balance = balance–100 
where account_number =‘A-101;
update account set balance = balance + 100
where account_number =‘A-201;
COMMIT WORK;

如果其中一个更新成功,另一个更新失败,会数据库中导致数据不一致 问题, 因此,这两个更新要么全部成功,要么全部失败。

事务具有以下四个性质:

  1. 原子性(atomic)

原子性说的是数据要么一起成功,要么一起失败。

  1. 一致性(consistency)

事务的一致性是指事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处以一致性状态。比如:如果从A账户转账到B账户,不可能因为A账户扣了钱,而B账户没有加钱

  1. 隔离性(isolation)

一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务是不能互相干扰的。

  1. 持久性(durability )

一个事务一旦提交,它对数据库中数据的改变就应该是持久性的。

在很多SQL实现中,默认方式下每个SQL语句自成一个事务,且一执行完就提交,如果一个事务要执行多条SQL语句,就必须关闭单独SQL语句的自动提交, 如何关闭自动提交也依赖于特定的SQL实现。

完整性约束

完整性约束保证授权用户对数据库所做的修改不会破坏数据的一致性。

完整性约束的例子有:

  • 教师姓名不能为null
  • 任意两位教师不能有相同的教师标识
  • course关系中的每个系名必须在department关系中有一个对应的系名
  • 一个系的预算必须大于0.00美元

在sql中完整性主要包括域完整性、实体完整性(主键的约束)、参照完整性(外键的约束) 和用户定义的完整性约束。

单个关系上的约束

  • not null (非空约束)
  • unique (不能有重复的)
  • check(<谓词>)

例:

CREATE TABLE instructor2
 ( ID char(5) primary key ,
 name varchar(20) not null,
 dept_name varchar(20), 
 salary numeric(8,2) not null  check (salary>= 0));

域约束(mysql中没有)

域约束是完整性约束的最基本形式,可用于检测插入到数据库中的数据的合法性。从现有数据类型可以创建新的域

create domain Dollars as numeric(12, 2) not null
create domain Pounds as numeric(12,2); 
create table instructor
( ID char(5) primary key,
 name varchar(20), 
 dept_name varchar(20), 
 salary Dollars, 
 comm Pounds
 );

check子句也可以应用到域上。

例1:check子句可以保证教师工资域中只允许出现大于给定值的值

create domain YearlySalary numeric(8,2)
constraint salary_value_test check(value >= 29000.00);
# YearlySalary 域有一个约束来保证年薪大于或等于29 000.00美元
# constraint salary_value_test 子句是可选的,它用来将该约束命名为salary_value_test。

例2:使用in子句可以限定一个域只包含指定的一组值

假设存在关系r和s:r(A, B, C), s(B, D),则在关系r上的属性B称作参照s的外码,r也称为外码依赖的参照关系,s叫做外码被参照关系

create domain degree_level varchar(10)
constraint degree_level_test
check (value in (’Bachelors’, ’Masters’, or ’Doctorate’));

参照完整性

外码(外键)的介绍如下:

image-20220523081857341

主键、候选键和外键可在SQL的create table语句中用如下方式指明:

  • primary key子句包含一组构成主码的属性
  • unique子句包含一组构成候选码的属性
  • foreign key子句包含一组构成外码的属性以及被修改外码所参照的关系名

默认地,外码参照被参照关系中的主键

foreign key (dept_name) references department

可以使用如下的简写形式定义单个列为外键

dept_name varchar (20) references department

被参照关系中的属性可以被明确指定,但是必须被声明为主键或候选键

image-20220523082412203

例:

create table classroom
 (building varchar (15),		#楼号
 room_number varchar (7),	#房间号
 capacity numeric (4,0),
 primary key (building, room_number));
 
create table department
 (dept_name varchar (20),
 building varchar (15),
 budget numeric (12,2) check (budget > 0),
 primary key (dept_name)) ;
 
 create table course
 (course_id varchar (8),
 title varchar (50),
 dept_name varchar (20),
 credits numeric (2,0) check (credits > 0),
 primary key (course_id ),
 foreign key (dept_name) references department);
 
create table instructor
 (ID varchar (5),
 name varchar (20), not null
 dept_name varchar (20),
 salary numeric (8,2), check (salary > 29000),
 primary key (ID),
 foreign key (dept_name) references department);

SQL中的级联操作

语法格式如下

create table course( 
. . .
foreign key(dept_name) references department
 [ on delete cascade]
 [ on update cascade]
. . . );

由于有了与外码声明相关联的on delete cascade子句,如果删除 department中的元组导致了此参照完整性的约束被违反,则删除并不被系统拒绝,而是对course关系作“级联”删除,即删除了被删除系的元组,级联更新也与此类似(on delete cascade从mysql3.23.50开始可用,on update cascade从mysql4.0.8开始可用)。

如果存在涉及多个关系的外码依赖链,则在链一端所做的删除或更新可能传至整个链,但是,如果一个级联更新或删除导致对约束的违反则不能通过进一步的级联操作解决,系统会终止该事务,即该事务所做的所有改变及级联动作将被撤销。

参照完整性只在事务结束时检查,中间步骤可以破坏参照完整性, 只要后续步骤解消这种破坏即可。除级联操作之外的其他选择:

  • on delete set null
  • on update set null

外键属性上的空值使SQL的参照完整性语义变得复杂, 最好用not null来防止。

总结

MySQL通过外键约束实现数据库的参照完整性,外键约束条件可在创建外键时指定,table的存储引擎只能是InnoDB,因为只有这种存储模式才支持外键。

外键约束条件有以下4种:

(1)cascade方式:在父表上update/delete记录时,同步update/delete子表的匹配记录,删除/更新父表的某条记录,子表中引用该值的记录会自动被删除/更新。

On delete cascade从mysql3.23.50开始可用,on update cascade从mysql4.0.8开始可用

(2)set null方式:在父表上update/delete记录时,将子表上匹配记录的列设为null 要注意子表的外键列不能为not null

On delete set null从mysql3.23.50开始可用;,on update set null从mysql4.0.8开始可用 。

(3)restrict方式:如果在子表中有引用,则不允许在主表中进行更新或删除(mysql默认是这种模式)

(4)No action方式:和restrict一样,如果在子表中有引用,则不允许在主表中进行更新或删除(mysql默认是这种模式)

案例如下:

例1:级联模式(改变主表数据,从表数据随着更新)

  • 创建分组表
create table t_group
(id int not null primary key,
 name varchar(10)
)
  • 插入两条数据
insert into t_group values(1,'Group_1');
insert into t_group values(2,'Group_2');
  • 创建用户表:
create table t_user
 (id int not null primary key,
name varchar(10),
groupid int,
foreign key(groupid) references t_group(id)
on delete cascade on update cascade);
  • 插入两条数据
insert into t_user values(1,'dayu',1);
insert into t_user values(2,'duoduo',3);
  • 当前数据信息如下:
mysql> select * from t_group;
+----+---------+
| id | name    |
+----+---------+
|  1 | Group_1 |
|  2 | Group_2 |
+----+---------+
 
mysql> select * from t_user;
+----+--------+---------+
| id | name   | groupid |
+----+--------+---------+
|  1 | dayu   |       1 |
|  2 | duoduo |       2 |
+----+--------+---------+
  • 从主表中更新一个记录
update t_group set id=3 where id=2;

改变主表数据,从表数据随着更新:

select * from t_user; 
+----+--------+---------+
| id | name   | groupid |
+----+--------+---------+
|  1 | dayu   |       1 |
|  2 | duoduo |       3 |
+----+--------+---------+
  • 从主表中删除一个记录
delete from t_group where id=3; 

子表中对应的记录自动被删除:

select * from t_user; 
+----+------+---------+
| id | name | groupid |
+----+------+---------+
|  1 | dayu |       1 |
+----+------+---------+

例2:置空模式(set null,主表记录被删除,从表中对应的值设置为NULL)

  • 创建分组表
create table t_group
(id int not null primary key,
 name varchar(10)
)
  • 插入两条数据
insert into t_group values(1,'Group_1');
insert into t_group values(2,'Group_2');
  • 创建用户表:
create table t_user_1
 (id int not null primary key,
name varchar(10),
groupid int,
foreign key(groupid) references t_group(id)
on delete set null on update set null);
  • 插入两条数据
insert into t_user_1 values(1,'dayu',1);
insert into t_user_1 values(2,'duoduo',2);
insert into t_user_1 values(3,'huanhuan',2);
insert into t_user_1 values(4,'maiqi',1);
  • 当前数据信息如下:
mysql> select * from t_group;
+----+---------+
| id | name    |
+----+---------+
|  1 | Group_1 |
|  2 | Group_2 |
+----+---------+
 
mysql> select * from t_user;
+----+----------+---------+
| id | name     | groupid |
+----+----------+---------+
|  1 | dayu     |       1 |
|  2 | duoduo   |       2 |
|  3 | huanhuan |       2 |
|  4 | maiqi    |       1 |

  • 操作t_group
update t_group set id=3 where id=1;

#结果如下
select * from t_user_1;
+----+----------+---------+
| id | name     | groupid |
+----+----------+---------+
|  1 | dayu     |    NULL |
|  2 | duoduo   |       2 |
|  3 | huanhuan |       2 |
|  4 | maiqi    |    NULL |
+----+----------+---------+

delete from t_group where id=2;   #从主表中删除一个记录
#结果如下
select * from t_user_1;  //子表中对应的属性值被自动设置为NULL
+----+----------+---------+
| id | name     | groupid |
+----+----------+---------+
|  1 | dayu     |    NULL |
|  2 | duoduo   |    NULL |
|  3 | huanhuan |    NULL |
|  4 | maiqi    |    NULL |
+----+----------+---------+

例3:禁止模式(no action/restrict),如果在子表中有引用,则不允许在主表中进行更新或删除(mysql默认是这种模式)

  • 创建分组表
create table t_group
(id int not null primary key,
 name varchar(10)
)
  • 插入两条数据
insert into t_group values(1,'Group_1');
insert into t_group values(2,'Group_2');
  • 创建用户表:
create table t_user_2
 (id int not null primary key,
name varchar(10),
groupid int,
foreign key(groupid) references t_group(id)
on delete no action on update restrict);
  • 插入两条数据
insert into t_user_2 values(1,'dayu',1);
insert into t_user_2 values(2,'duoduo',2);
insert into t_user_2 values(3,'huanhuan',2);
insert into t_user_2 values(4,'maiqi',1);
  • 当前数据信息如下:
mysql> select * from t_group;
+----+---------+
| id | name    |
+----+---------+
|  1 | Group_1 |
|  2 | Group_2 |
+----+---------+
 
mysql> select * from t_user;
+----+----------+---------+
| id | name     | groupid |
+----+----------+---------+
|  1 | dayu     |       1 |
|  2 | duoduo   |       2 |
|  3 | huanhuan |       2 |
|  4 | maiqi    |       1 |

  • 操作t_group
update t_group set id=3 where id=1;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t_user_2`, CONSTRAINT `t_user_2_ibfk_1` FOREIGN KEY (`groupid`) REFERENCES `t_group` (`id`) ON DELETE NO ACTION)

delete from t_group where id=2;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`test`.`t_user_2`, CONSTRAINT `t_user_2_ibfk_1` FOREIGN KEY (`groupid`) REFERENCES `t_group` (`id`) ON DELETE NO ACTION)

例4:主从表删除

#删除从表没有限制,如下:
drop table t_user_2;
Query OK, 0 rows affected (0.01 sec)

#如果存在引用的从表,则主表不能随意删除:
drop table t_group;
ERROR 1217 (23000): Cannot delete or update a parent row: a foreign key constraint fails

数据安全性(不常用)

在数据库系统级保证数据库的安全,可以通过登陆验证或者授权机制使特定用户读写特定数据,这里只讨论授权机制。

对数据的授权包括:

  • 读权限 – 允许读,但不允许更新数据
  • 插入权限 – 允许插入新数据, 但不允许更新现有数据
  • 修改权限 – 允许修改, 但不允许删除数据
  • 删除权限 – 允许删除数据

对修改数据库模式的授权包括:

  • 索引权限 – 允许创建和删除索引
  • 资源权限 – 允许创建新关系
  • 修改权限 – 允许增加或删除关系的属性
  • 删除权限 – 允许删除关系

假设,一个工作人员只需要知道一个给定系(比如Geology系)里所有员工的工资,但无权看到其他系中员工的相关信息,可以不允许其对instructor关系直接访问,但授予对视图 geo_instructor的访问权限,该视图仅由属于Geology系的那些instructor 元组构成,定义视图的语言如下:

create view geo_instructor as
(select *
from instructor
where dept_name = ’Geology’);

grant语句可用于授权,格式如下:

GRANT <privilege list> 
ON <relation name or view name> TO <user list>;

< user list >是用户ID。

sql中的权限如下:

  • select:允许读关系,或查询视图
  • 例如:授予用户U1,U2,U3 对instructor关系的select权限: grant select on instructor to U1 ,U2 ,U3
  • insert :允许插入元组
  • update :允许修改元组
  • delete :允许删除元组
  • references :创建关系时允许声明外键
  • all privileges :所有权限

补充:

with grant option:允许用户把被授予的权限再转授给其他用户

例如:授予U1对instructor的select权限并允许 U1将此权限授予其他用户

grant select on instructor to U1 with grant option

猜你喜欢

转载自blog.csdn.net/Learning_xzj/article/details/124961196