Table of Contents
4.4.5 参照完整性 (referential integrity)
第四章 中级SQL
4.1 连接表达式
4.1.1 连接条件
- natural join子句: 自然连接,在两个关系都包括的属性上要取值匹配
- join...using子句:是自然连接的一种形式,只需要在指定属性上的取值匹配。
- on 条件:允许在参与连接的关系上设置通用的谓词,例如:
select * from student join takes on student.ID = takes.ID;
-- 等价于
select * from student natural join takes;
-- 等价于
select * from student, takes where student.ID = takes.ID;
4.1.2 外连接 outer join
有三种形式的外连接(outer join):
- 左外连接(left outer join): 只保留出现在左外连接运算之前(左边)的关系中的元组
- 右外连接(right outer join): 只保留出现在右外连接运算之后(右边)的关系中的元组
- 全外连接(full outer join): 保留出现在两个关系中的元组
内连接(inner join):不保留未匹配元组的连接运算被称作内连接运算。
-- 不指定outer时,默认是内连接
select * from student (inner) join takes using (ID);
-- 左外连接
select * from student natural left outer join takes;
-- 右外连接
select * from student natrual right outer join takes;
-- 全外链接
select * from student natural full outer join takes;
-- on子句可以与外连接一起用
select * from student left outer join takes on student.ID = takes.ID;
--等价于
select * from student left outer join on true where student.ID = takes.ID;
4.1.2 连接类型和条件
在连接时,可以将任意的连接类型和任意的连接条件相组合。
4.2 视图
视图(view):视图可以向用户隐藏特定的数据;不进行预先计算和存储,在使用时通过执行查询被计算出来。
虚关系:虚关系并不预先计算并存储,而是在使用时通过执行查询被计算出来。
4.2.1 视图定义
-- create view v as <query expression>; e.g:
create view faculty as select ID, name, dept_name from instructor;
4.2.2 SQL查询中使用视图
在任何时刻,视图关系中的元组集是该时刻视图定义中的查询表达式的计算结果。
通常来说,当我们定义一个视图时,数据库系统存储视图的定义本身,而不存储定义该视图的查询表达式的执行结果。
一旦视图关系出现在查询中,它就被已存储的查询表达式代替。因此,无论何时执行这个视图,视图关系都会被重新计算。
4.2.3 物化视图
物化视图(materialized view): 如果用于定义视图的实际关系发生改变,视图也跟着修改。这样的视图称为物化视图。
物化视图维护(materialized view maintenance)/视图维护(view maintenance):保持物化视图一直在最新状态的过程称为物化视图维护。
物化视图维护方式:
1. 数据库系统在底层关系变化时,总是立即更新物化视图的内容;
2. 周期性更新物化视图;
4.2.4 视图更新
一般来说,除了一些有限的情况之外,不允许对视图关系进行修改。不同数据库系统指定了不同的条件以允许对视图关系进行修改。
(注意:此处说的对视图进行更新,是指对视图进行更新,并要求同时对视图来源的底层关系进行更新)
一般来说,如果满足下列条件,我们就说视图是可以更新的:
- from子句中只有一个数据库关系
- select子句中只包括关系的属性名,不包含任何表达式、聚集或distinct声明
- 任何没有出现在select子句中的属性都可以取空值,即:这些属性没有not null约束,并且也不构成主码的一部分
- 查询中不含有group by或having子句
即便视图是可更新的,也会存在其他的问题。如:插入一条数据,但是该数据不满足视图的查询条件,那么这条数据也不会出现在视图中。
此时,可以再视图定义的末尾加上with check option来拒绝这类更新。
4.3 事务
事务(transaction)由查询和(或)更新语句的序列组成。
- commit work:提交当前事务,也就是该事务所做的更新在数据库中持久保存。在事务提交之后,一个新的事务自动开始
- rollback work:回滚当前事务,即撤销该事务中的所有SQL语句对数据库的更新。这样,数据库就恢复到执行该事务第一条语句之前的状态
一旦事务进行了commit,它的影响就不能被rollback了。
事务具有原子性(atomic):原子性也就是不可分割性,即:事务的所有步骤要么都成功并提交,要么都不成功进行回滚。
begin atomic...end可以用来创建一个由多条语句组成的事务。
4.4 完整性约束
完整性约束保证授权用户对数据库所做的修改不会破坏数据的一致性。完整性约束防止的是对数据的意外破坏。
完整性约束通常被看成是数据库模式设计过程的一部分,它作为用于创建关系的create table的命令的一部分被声明。
也可以通过alter table table-name add constraint命令追加约束。
4.4.1 - 4.4.4 单个关系上的约束
- not null:禁止被修饰属性上出现空值
name varchar(20) not null budget numberic(10,2) not null
- unique: 声明候选码,即在关系中没有两个元组能够在所有列出的属性上取值相同
unique(A1,A2,...,An)
- check (<谓词>):指定关系中的所有元组都要满足给定谓词
check(budget>0) check(semester in ('Fall','Winter','Spring','Summer'))
4.4.5 参照完整性 (referential integrity)
参照完整性:保证一个关系中给定属性集上的取值也在另一关系的特定属性集 的取值中出现。
外码(foreign key):令关系r1和r2的属性集分别为R1和R2,主码分别为K1和K2.如果要求对r2中任意元组t2,均存在r1中元组t1,使得t1.K1=t2.alpha, 我们称R2的子集alpha为参照关系r1中K1的外码。
参照完整性约束(referential integrity constraint)/ 子集依赖(subset dependency):在上述情况中,要求r2中alpha上的取值集合必须是r1中K1上的取值的集合的子集。但是与外码不同,参照完整性约束不要求K1是r1的主码,、。
--定义外码约束,要求被参照属性是被参照关系的主码
foreign key (dept_name) references department
-- 定义参照完整性约束,不要求被参照属性是被参照关系的候选码
dept_name varchar(20) references department
/*注意: 当违反参照完整性时,通常的处理是拒绝执行该操作。 但是使用foreign key是可以明确指明若违反完整性约束,可以采取一些步骤来修改参照关系中的元组来回复完整性操作,而不是拒绝操作*/
-- on delete cascade子句规定如果删除department中的元组导致完整性约束被破坏,则级联删除course
create table course( ... foreign key(dept_name) references department on delete cascade/set null/set default on update cascade, ... )
4.4.6 事务中对完整性约束的违反
默认情况下,执行每个语句时都会立即检查完整性约束。
initially deferred子句:该子句加入到约束声明中,可以实现完整性约束不是在事务的中间步骤上检查,而是在事务结束的时候时候检查。
set constraints constraint-list deferred: 可以声明事务延迟检查给定约束到事物结束时执行。
举例:
person(name,spouse),其中name为主码,spouse为外码。 约束要求spouse必须为person中出现的name。 插入A-B,A和B为夫妻关系,则不管先插入A还是先插入B都会违反外码约束。
4.4.7 复杂check条件与断言
check子句中的谓词可以是包含子查询。
复杂check条件在确保数据完整性上十分有用,但是其检测开销会很大。
断言(Assertion):一个断言就是一个谓词,他表达了我们希望数据库满足的一个条件。域约束和参照完整性约束是断言的特殊形式。
create assertion <assertion-name> check <predicate>;
例如:
/*断言:对于student关系中的每个元组,他在属性tot_cred上的取值必须等于该生所成功修完课程的学分总和*/
create assertion credits_earned_constraint
check (not exists (
select ID from student where tot_cred<>
(select sum(credits) from takes natural join course
where student.ID = takes.ID and grade is not null and grade<>'F')));
4.5 SQL的数据类型与模式
4.5.1 SQL的日期和时间类型
- date:日历时间,包括年(四位)、月、日。
- time:一天中的时间,包括小时、分、秒。可以用变量time(p)来指定秒的小数点后的数字位数。
- timestamp:date和time的组合。可以用变量timestamp(p)来指定秒的小数点后的数字位数。如果指定with timezone,则时区信息也会被存储。
date '2001-04-05' time '09:30:00' timestamp: '2001-04-05 10:29:01.45'
cast e as t: 可以将一个字符串e转换为日期或时间类型t,类型t可以是date,time和timestamp中的一种。要求字符串必须符合正确的格式。
extract (field from d): 从date或time值d中提取出单独的域。域包括: year, month, day, hour, minute, second; timezone_hour, timezone_minute
current_date: 返回当前日期
current_time:返回当前时间(带有时间)
localtime:返回当前本地时间(不带时区)
时间戳:current_timestamp(带时区),localtimestamp(本地日期和时间,不带时区)
4.5.2 默认值
default关键字用于指定默认值。
create table student(
ID varchar(5) ,
name varchar(20) not null,
dept_name varchar(20),
tot_cred numberic(3,0) default 0,
primary key (ID)
);
4.5.3 创建索引
在关系属性上所创建的索引(index)是一种数据结构。
它允许数据库系统高效的查询关系中那些在索引属性上取给定值的元组,而不用扫描关系中的所有元组。
如果用户提交的SQL查询可以从索引的使用种获益,那么SQL查询处理器会自动使用索引。
// 在student上根据属性ID创建索引 create index studentID_index on student(ID);
4.5.4 大对象类型
clob:字符数据的大对象数据类型
blob:二进制数据的大对象数据类型
(lob代表 Large OBject)
注意:把整个大对象放入内存中是非常低效和不现实的。一个应用通常用一个SQL查询来检索出一个大对象的”定位器“,然后再宿主语言中利用定位器来操作对象。
book_review clob(10KB) image blob(10MB) movie blob(2GB)
4.5.5 用户定义的类型
SQL支持两种形式的用户定义数据类型:
- 结构化数据类型(structured data type): 允许创建具有嵌套记录结构、数组和多重集的复杂数据类型。
- 独特类型(distinct type):(注意,不同数据库支持的内容不同)
-- 会进行强类型检查,Pounds类型的值不能赋给Dollars类型的属性
create type Dollars as numberic(12, 2) final; create type Pounds as numberic(12, 2) final;
-- 将一种类型的值域转为另一种类型
cast (department.budget to numberic(12,2))
--删除类型和修改类型 drop type alter type
--域domain:在基本类型上施加完整性约束;domain也可以作为属性类型
create domain DDollars as numberic(12,2) not null;
--声明来自该域的任何变量都要满足给定谓词
create domain YearlySalary numberic(8,2) constraint slary_value_test check(value>=290000.00);
--类型和域的两大差别:
/* 1. 在域上面可以声明约束,也可以为域类型变量定义默认值;但是类型定义上不能声明约束或默认值 2. 域并不是强类型的。因此一个域类型的值可以被赋给另一个域类型,只要它们的基本类型是相容的 */
4.5.6 create table的扩展
创建与现有的某个表的模式相同的表:
// 创建一个与instructor具有相同模式的新表
create table temp_instructor like instructor;
with data子句:
列的名称和数据类型是从查询结果中推导出来的。
-- SQL:2003标准定义,省略with data时,只会创建表,但是不会载入数据
-- 但是很多数据库的实现都支持自动载入,即使省略with data
with data create table t1 as (select * from instructor where dept_name='Music')
-- 不同数据库实现的类似的语法 create table ... as create table ... like
4.5.7 模式、目录与环境
文件系统的目录:/users/avi/db-book/chapter3.tex
当代数据库系统采取三层结构的关系命名机制:顶层由目录(catalog)构成;每个目录都可以包含模式(schema);关系和视图都包含在模式中。
用户操作数据库的过程:
- 连接数据库:此时用户需要提供用户名和密码。
- 通常情况下,每个用户都有一个默认的目录和模式,这个组合对用户来说是唯一的。
- 当用户连接到数据库系统时,将为该连接设置好默认的目录和模式。
为了标识一个关系,必须使用一个名字,它由三部分组成:
catalog5.univ_schema.course
目录.模式.关系名
其中目录部分被认为是连接时的默认目录时,可以省略。
当有多个目录和模式存在时,不同应用和不同用户可以独立工作而不必担心命名冲突。
一个应用的不同版本可以再同一个数据库系统上运行。
SQL环境:默认目录和模式是为每个连接建立的SQL环境的一部分。SQL环境还包括用户标识(也称授权标识)。
所有通常的SQL语句,包括DDL和DML,都在一个模式的环境中运行。
--创建和删除模式
create schema drop schema
4.6 授权
权限(privilege)
- 对数据的授权包括:
- 授权读取数据
- 授权插入新数据
- 授权更新数据
- 授权删除数据
当用户提交更新或查询时,SQL执行先基于用户曾获得的权限检查此查询或更新是否是授权过的。如果没有经过授权,那么将拒绝执行。
- 对模式的授权:
- 允许用户创建、修改和删除关系
- 权限转授的权利:
- 允许用户给其他用户授权、或撤销授权
最大的授权形式是被授予数据库管理员权限。它可以授权新用户、重构数据库等。
4.6.1 权限的授予与收回
SQL标准包括select, insert, update, 和delete权限。
- select: 允许读取关系中的数组
- insert:允许插入新数据,但是不允许更新已有数据
- 也可以指定属性列表,对关系的操作只能针对这些属性,系统将其他属性设置为默认值或null。
- update:允许更新数据。
- 允许用户修改关系中的任意元组
- 可以授予关系上的所有属性,也可以只授予到某些属性。
- delete: 允许删除元组
授予权限:
grant <权限列表> on <关系名或视图名> to <用户/角色列表>
-- 授予Amit, Satoshi对department进行查询的权限
grant select on department to Amit, Satoshi
-- 授予Amit 更新 department budget属性;
-- 省略属性列表代表对所有属性都有更新权限
grant update(budget) on department to Amit
-- 用户名public值系统的所有当前用户和未来用户
注意:SQL允许对整个关系,或者关系的属性进行授权,但是不允许对一个关系的指定元组进行授权。
收回权限:revoke
revoke<权限列表> on <关系名或视图名> from <用户/角色列表>
-- 收回授权
revoke select on department from Amit;
revoke update(budget) on department from Amit;
4.6.2 角色
角色(Role):在数据库中创建一个角色集,可以给角色授予权限。每个数据库用户被授予一组他有权扮演的角色(也可以是空的)。
-- 创建角色
create role role-name;
-- 给角色授权
grant select on <table/view-name> to <role-name>;
-- 授予用户角色
grant <role-name> to <user-name>
-- 授予角色角色
grant <role-name A> to <role-name B>
一个用户或者一个角色的权限包括:
- 直接授予该用户/角色的权限
- 所有授予给用户/角色所拥有的角色的权限
4.6.3 视图的授权
创建视图的用户不会默认获得该视图上的所有权限。他得到的权限不会超过他已有权限的额外授权。
例如:如果一个创建视图的用户,在用来定义视图的关系上没有update权限的话,那么它也不能得到视图上的update权限。
如果用户创建一个视图,而此用户在该视图上不能得到任何权限,那么该创建请求会被拒绝。
视图的创建者必须在视图参考关系上具有select权限。
在函数或者过程上也可以授予execute权限,以允许用户执行函数或者过程。
4.6.4 模式的授权
SQL标准为数据库模式指定了一种基本的授权规则:只有模式的拥有者才能够执行对模式的任何修改,如创建和删除关系、增加或删除属性、增加或者删除索引。
SQL提供了一种references权限,允许用户在创建关系时声明外码。
references权限授予:
-- 允许用户Mariano创建这样的关系,它能够参照department关系的码dept_name
grant references (dept_name) on department to Mariano;
4.6.5 权限的转移
获得某些形式权限的用户可能被允许将此授权传递给其他用户。
默认方式下,被授予的用户/角色无权把得到的权限再授予给另外的角色/用户。
但是,可以在授权时指定是否允许接受者把权限传递给其他用户(with grant option);
-- 授予Ami select权限,并允许Ami把select权限授予给其他用户
grant select on department to Ami with grant option;
授权图:授权图可以指定一个用户到另一个用户的传递。该图中的顶点是用户,根节点是DBA(数据库管理员)。
用户具有权限的充分必要条件:当且仅当存在从授权图的根到代表该用户顶点的路径。
4.6.6 权限的收回
级联收回:从一个用户/角色那里收回权限可能会导致其他用户/角色也失去该权限,这一行为称为级联收回。
例如:从U1出收回权限,由于U4是从U1那里获取到的权限,因此U4的权限也会被收回。
一些狡猾的用户可能企图通过相互授权来破坏权限回收规则。例如,U1授权给U2,U2再授权给U1。但是若DBA收回U1的权限,那么U1和U2的权限都不会被保留,因为他们都没有到根的路径。
对大多数数据库系统,级联收回是默认的行为。
使用revoke可以声明restrict来防止级联收回。
-- 此时,如果存在级联收回,则系统返回一个错误,且不执行收权动作
revoke select on department from Ami restrict;
-- 指定级联收回(默认情况也是cascade,可以省略)
revoke select on department from Ami cascade;
-- 收回grant option权限
revoke grant option for select on department from Ami restrict;
级联收回在许多情况下是不合适的。
比如:Satoshi具有dean角色,他将instructor角色授予Ami,后来Satoshi离开了大学,dean角色被收回。那么如果Ami继续在该大学作为教职工,那么instructor角色不应该被收回!!!
针对这种情况,SQL允许权限由一个角色赋予,而不是由用户来授予。
// 默认情况下,一个会话所关联的当前角色是空的(特殊情况除外) // 一个会话所关联的角色可以通过set role role-name来设定 // 要求指定的角色必须已经授权给当前用户,不然会执行失败 set role role-name; // 如果当前角色不为空的话,可以在授予权限的时候 设置授予该权限的是当前用户的角色而不是用户本身 grant instructor to Ami granted by current_role;
总结
- SQL支持:内连接、外连接(左外连接,右外连接,全外连接)
- 视图关系可以定义为包含查询结果的关系。视图可以隐藏不必要的信息,可以把信息从多个关系收集到一个单一的视图中
- 事务是一个查询和更新的序列,他们共同执行某项任务。事务可以被提交或者回滚。当一个事务被回滚时,该事务执行的所有更新所带来的影响将被撤销。
- 完整性约束保证授权用户对数据库所做的改变不会导致数据一致性的破坏
- 参照完整性约束保证出现在一个关系的给定属性集上的值同样出现在另一个关系的特定属性集上
- 域约束指定了在一个属性上可能取值的集合。这种约束也可以禁止在特定属性集上使用空值。
- 断言是描述性表达式,它指定了我们要求总是为真的谓词
- SQL数据定义语言提供对定义诸如date和time那样的固有域类型以及用户定义域类型的支持
- 通过SQL授权机制,可以按照在数据库中不同数据值上数据库用户所允许的访问类型对他们进行区分。
- 获得了某种形式授权的用户可能允许将此授权传递给其他用户。但是,对于权限怎样在用户间传递我们必须小心,以保证这样的权限再将来的某个时候可以被收回
- 角色有助于根据用户在组织机构中所扮演的角色,把一组权限分配给用户。