SQL提供了连接运算的其他形式,包括能够指定显式的连接谓词(join predicate),能够在结果中包含被自然连接排除在外的元组。
例子:student 和 takes 两个关系。
join…using 子句,它是一种自然连接的形式,只需要在指定属性上的取值匹配。
on 条件允许在参与连接的关系上设置通用的谓词。
该谓词的写法与 where 子句谓词类似,只不过使用的是关键词 on 而不是 where。
与 using 条件一样, on 条件出现在连接表达式的末尾。
包含 on 条件的连接表达式:
select *
from student join takes on student.ID = takes.ID;
上述 on 条件表明:如果一个来自 student 的元组和一个来自 takes 的元组在ID上的取值相同,那么它们是匹配的。
等价于
select *
from student, takes
where student.ID = takes.ID;
只显示一次ID值的查询如下:
select student.ID as ID, name, dept_name, tot_cred,
course_id, sec_id, semester, year, grade
from student join takes on student.ID = takes.ID;
查询结果如下:
on 条件可以表示任何SQL谓词,从而使用on条件的连接表达式就可以表示比自然连接更为丰富的连接条件。
假设显示一个所有学生的列表,显示他们的ID,name,dept_name和tot_cred,及他们选修的课程。
select *
from student natural join takes;
外连接(outer join)运算与我们已经学过的连接运算类似,但通过在结果中创建包含空值元组的方式,保留了那些在连接中丢失的分组。
上述例子在图1和图2的情形下,snow学生未选课将不会出现在natual join结果中。
为保证snow出现在结果中,可在连接结果中加入一个元组,它在来自student关系的所有属性上的值被设置为学生snow的相应值。
在所有余下的来自takes关系属性上的值被设为null。
这些属性是course_id, sec_id, semester, year
有三种外连接:
此前学习的不保留未匹配元组的连接运算称为内连接(inner join)运算。
计算左外连接运算:
首先,计算出内连接的结果;
然后,对内连接的左侧关系中任意一个与右侧关系中任何元组都不匹配的元组 t ,向连接结果中加入一个元组r。
r的构造如下:
// 结果包含snow,在snow对应的元组中,在那些只出现在takes关系模式中的属性上取空值
select *
from student natural left outer join takes;
使用外连接运算的例子:
// 找出一门课程也没有选修的学生
select ID
from student natural left outer join takes
where course id is null;
右外连接和左外连接是对称的。
等价的右外连接
select *
from takes natural right outer join student;
得到的结果是一样的,差别是元组中各个属性出现顺序不一致。
全外连接是左外连接与右外连接类型的组合。
在内连接结果计算出来后,左侧关系中不匹配右侧关系任何元组的元组被添上空值并加到结果中。
右侧关系中不匹配左侧关系任何元组的元组也被添上空值并加到结果中。
全外连接的例子:
查询 “显示Comp. Sci.系所有学生及他们在2009年春季选修的所有课程段的列表。2009年春季开设的所有课程段都必须显示,即使没有Comp. Sci.系的学生选修这些课程段 ”
select *
from (select *
// 学生集合
from student
// 选出Comp. Sci系的
where dept_name= ’Comp. Sci’)
// 全外连接
natural full outer join
(select *
// 选课信息
from takes
// 选出2009,Spring的被选课程
where semester = ’Spring’ and year = 2009);
on 子句可以和外连接一起使用。
select *
from student left outer join takes on student.ID = takes.ID;
on 和 where 在外连接中的表现是不同的。
其原因是外连接只为那些对相应内连接结果没有贡献的元组补上空值并加入结果。
on 条件是外连接声明的一部分,但where子句却不是。
把前述查询中的 on 子句谓词换成 where子句,并使用on条件 true:
select *
from student left outer join takes on true
where student.ID = takes.ID;
SQL中把常规连接称作内连接。
这样连接子句可用 inner join 来替换 outer join ,说明使用的是常规连接。
关键词inner是可选,当 join 子句中没有使用 outer 前缀,默认的连接类型是 inner join 。
select *
from student join takes using (ID);
等价于
select *
from student inner join takes using (ID);
类似地,natural join 等价于 natural inner join 。
考虑一个职员需要知道教师的标识,姓名,所在系名,但没权限看到教师的工资值。
select ID, name, dept name
from instructor;
除了安全考虑,还可能希望创建一个比逻辑模型更符号特定用户直觉的个人化的关系集合。
// 希望有一个关于Physics系在2009秋季学期开设的所有课程段的列表
// 包括每个课程段在那栋建筑,那个房间授课
select course.course_id, sec_id, building, room_number
// 课程和开设课程笛卡尔积
from course, section
// 针对每个课程,
// 此课程的每个开设信息,与课程结合形成一个结果元组
// 结果元组满足物理系,2009,秋季,则被选入结果元组
where course.course_id = section.course_id
and course.dept_name = ’Physics’
and section.semester = ’Fall’
and section.year = ’2009’;
可以获得上述查询的结果,将其存储下来,然后把存下关系提供给用户。
SQL允许通过查询定义"虚关系",它在概念上包含查询的结果。
虚关系并不预先计算并存储,而是在使用虚关系的时候才通过执行查询被计算出来。
任何像这种不是逻辑模型的一部分,但作为虚关系对用户可见的关系称为视图(view)。
在SQL中用create view定义视图。
为定义试图,需给视图一个名称,需提供计算视图的查询。
create view 命令的格式为
create view v as <query expression>;
其中可以是任何合法的查询表达式,v表示试图名。
重新考虑需访问instructor关系中除salary外的所有数据的职员。
// 可把视图关系faculty提供给职员
// 此视图定义如下
create view faculty as
select ID, name, dept_name
from instructor;
视图关系在概念上包含查询结果中的元组,但并不进行预计算和存储。
数据库系统存储与视图关系相关联的查询表达式。
视图关系被访问时,其中的元组通过计算查询结果创建出来的。
视图关系是在需要时候创建的。
// 列出Physics系在2009,秋季开设的所有课程段
// 每个课程段在哪栋建筑,哪个房间授课
create view_physics_fall_2009 as
select course.course_id, sec_id, building, room_number
from course, section
where course.course_id = section.course_id
and course.dept_name = ’Physics’
and section.semester = ’Fall’
and section.year = ’2009’;
一旦定义了一个视图,就可以用视图名指代该视图生成的虚关系。
使用视图physics_fall_2009,可用下面的查询找到所有于2009,秋季,在Watson大楼开设的Physics课程:
select course_id
from physics_fall_2009
where building= ’Watson’;
在查询中,视图名可出现在关系名可出现的任何地方。
视图的属性名可按下述方式显式指定:
// 给出了每个系中所有教师的工资总和
// 显式指定视图各个属性名
create view departments_total_salary(dept_name, total_salary) as
select dept_name, sum (salary)
from instructor
group by dept_name;
直觉上,任何给定时刻,视图关系中的元组集是该时刻视图定义中的查询表达式的计算结果。
如果一个视图关系被计算并存储,一旦用于定义该视图的关系被修改,视图就会过期。
为避免这一点,视图常这样实现:
定义一个视图时,数据库系统存储视图的定义本身,
不存储该视图查询表达式的执行结果。
无论何时执行查询,视图关系均被重新计算。
一个视图可能被用到定义另一个视图的表达式中。
如下定义视图physics_fall_2009_watson,
// 列出了于2009,秋季,在Watson大楼开设的所有Physics课程的标识和房间号
create view physics_fall_2009_watson as
select course_id, room_number
from physics_fall_2009
where building= ’Watson’;
等价于
create view physics_fall_2009_watson as
(select course_id, room_number
from (select course.course_id, building, room_number
from course, section
where course.course_id = section.course_id
and course.dept name = ’Physics’
and section.semester = ’Fall’
and section.year = ’2009’)
where building= ’Watson’;
特定数据库允许存储视图关系,但它们保证:如用于定义视图的实际关系改变,视图也跟着修改。这样的视图称为物化视图(materialized view)。
例:
考察视图departments_total_salary。
如上述视图是物化的,它的结果会存放在数据库中。
如果一个instructor元组被插入到instructor关系中,或从instructor关系中删除,
定义视图的查询结果就会变化,结果是物化视图的内容也必须更新。
类似地,如一位教师的工资被更新,departments_total_salary中对应于该教师所在系的元组必须更新
保持物化视图一直在最新状态的过程称为物化视图维护(materialized view maintenance),或通常简称视图维护(view maintenance)。
当构成视图定义的任何关系被更新时,可马上进行视图维护。
然而,某些数据库系统在视图被访问时才执行视图维护。
还有一些系统仅采用周期性物化视图更新方式。
频繁使用视图的应用将会从视图的物化中获益。
需要快速响应基于大关系上聚集计算的特定查询也会从创建与查询相对应的物化视图中受益良多。
在这种情况下,聚集结果很可能比定义视图的大关系要小得多,其结果是利用物化视图来回答查询就很快,它避免读取大的底层关系。
SQL没有定义指定物化视图的标准方式,但很多数据库系统提供各自的SQL扩展来实现这项任务。
用视图表达的数据库修改必须被翻译为对数据库逻辑模型中实际关系的修改。
设此前的faculty被提供给一个职员。
// 该职员可这样写
insert into faculty
values (’30765’, ’Green’, ’Music’);
这个插入必须表示为对instructor关系的插入,因为instructor是数据库系统用于构造图faculty的实际关系。
然而,为把一个元组插入instructor,必须给出salary值。
存在两种合理的解决方法来处理该插入:
通过视图修改数据库的另一类问题发生在这样的视图上
create view instructor_info as
select ID, name, building
// 每个教员和每个建筑的组合
from instructor, department
where instructor.dept_name= department.dept_name;
考虑如下通过该视图的插入:
insert into instructor_info
values (’69987’, ’White’, ’Taylor’);
假设没有标识为69987的教师,也没有位于Taylor大楼的系。
则向instructor和department关系插入元组的唯一可能的方法是:
向instructor中插入元组('69987', 'White', null, null),
并向department中插入元组(null, 'Taylor', null)
但这个更新并没有产生所需的结果因为视图关系instructor_info中仍然不包含元组('69987', 'White', 'Taylor') 。
因此,通过空值来更新instructor和department关系,以得到对instructor_info所需的更新是不可行的。
由于如上所述的种种问题,除了一些有限的情况外,
一般不允许对视图关系进行修改。
一般说来,如果定义视图的查询对下列条件都能满足,我们称SQL视图是可更新的(updatable)(即视图上可以执行插入,更新,删除):
下面的视图上允许执行 update,insert,delete 操作:
create view history instructors as
select *
from instructor
where dept name= ’History’;
即使是在可更新的情况下,问题仍然存在。
假设向上述视图添加非历史系教师,由于它不满足视图所要求的选择条件,它不会出现在视图history_instructors中。
可以通过在视图定义的末尾包含 with check option 子句的方式来定义视图。
事务(transaction)由查询和(或)更新语句的序列组成。
一个SQL语句被执行,隐式地开始了一个事务。
下列SQL语句之一会结束一个事务:
一旦某事务执行了Commit work,它的影响就不能用rollback work来撤销了。
数据库系统保证在发生诸如某条SQL语句错误、断电、系统崩溃这些故障的情况下,如一个事务还没有完成commit work,其影响将被回滚。
在断电和系统崩溃的情况下,回滚会在系统重启后执行。
例子:
假设student关系中每个元组在tot_cred属性上的取值需要保持在最新状态,
只要学生成功修完一门课,该属性值就更新。
如takes更新后,student更新时发生故障。数据库中数据就是不一致的。
一个事务或者在完成所有步骤后提交其行为,或者在不能成功完成其所有动作的情况下回滚其所有动作,
通过这种方式数据库提供了对事务具有原子性(atomic)的抽象,原子性也就是不可分割性。
很多SQL实现中,默认方式下每个SQL语句自成一个事务,且一执行完就提交。
SQL:1999 标准的一部分,允许多条SQL语句包含在关键字 begin atomic . . . end之间。
所有在关键字之间的语句构成了一个单一事务。
完整性约束保证授权用户对数据库所做的修改不会破坏数据的一致性。因此,完整性约束防止的是对数据的意外破坏。
例:
- 教师姓名不能为null。
- 任意两位教师的教师标识不同。
- course 中每个系须在department关系中有一个对应的系名。
- 一个系的预算必须大于0.00 美元。
完整性约束通常被看成是数据库模式设计过程的一部分,它作为用于创建关系 create table 命令的一部分被声明。
完整性约束也可通过使用 alter table table-name add constraint 命令施加到已有关系上,其中 constraint 可以是关系上的任意约束。
执行上述命令时,系统首先保证关系满足指定的约束。
如果满足,那么约束被施加到关系上。否则,拒绝执行上述命令。
create table 命令还可以包括完整性约束语句。
允许的完整性约束包括:
通过限定属性name和budget的域来排除空值:
name varchar(20) not null
budget numeric(12,2) not null
not null 声明禁止在该属性上插入空值。
SQL禁止在关系模式的主码中出现空值。
SQL还支持下面这种完整性约束:
unique (Aj1, Aj2, . . . , Ajm)
unique 指明Aj1, Aj2, . . . , Ajm形成了一个候选码;
即在关系中没有两个元组能在所有列出的属性上取值相同。
应用于关系声明时,check§ 子句指定一个谓词P,关系中的每个谓词都必须满足谓词P。
通常用 check 子句来保证属性值满足指定的条件,实际上创建了一个强大的类型系统。
例:
在创建department的create table命令中,check(budget>0)保证budget取值是正数。
create table section
(course_id varchar (8),
sec_id varchar (8),
semester varchar (6),
year numeric (4,0),
building varchar (15),
room_number varchar (7),
time_slot_id varchar (4),
primary key (course_id, sec_id, semester, year),
check (semester in (’Fall’, ’Winter’, ’Spring’, ’Summer’)))
根据SQL标准,check 子句中的谓词可以是包括子查询在内的任意谓词。
我们常常希望保证在一个关系中给定属性集上的取值也在另一关系的特定属性集的取值中出现。这称为参照完整性(referential integrity)。
course 表的定义中有一个声明 “foreign key (dept_name) references department”。
这个外码声明表示,在每个课程元组中指定的系名必须在 department 关系中存在 。
更一般地,令关系r1和r2的属性集分别为R1和R2,主码分别为K1和K2。
如要求对r2中任意元组 t2,均存在r1中元组t1使得t1.K1 = t2.a,我们称R2的子集 α \alpha α为参照关系r1中K1的外码(foreign key)。
这种要求称为参照完整性约束(referential-intergrity constraint)或子集依赖(subset dependency)。
r2中 α \alpha α上的取值集合必须是r1中k1上的取值集合的子集。
为使参照完整性约束有意义, α \alpha α和K1必须是相容的属性集;
也就是说,要么 α \alpha α等于K1,要么它们须包含相同数目的属性,并且对应的属性类型必须相容。
不同于外码约束,参照完整性约束通常不要求K1是r1的主码;其结果是,r1中可能有不止一个元组在属性K1上取值相同。
默认情况下,SQL中外码参照的是被参照表中的主码属性。
SQL还支持一个可显式指定被参照关系的属性列表的references子句。
这个指定的属性列表必须被声明为被参照关系的候选码,要么使用primary key约束,要么使用 unique 约束。
可用如下简写作为属性定义的一部分,且声明该属性为外码:
dept_name varchar(20) references department
当违反参照完整性约束时,通常的处理是拒绝执行导致完整性破坏的操作(即执行更新操作的事务被回滚)。
在foreign key子句中可指明:如被参照关系上的删除或更新动作违反了约束,那么系统需采取一些步骤通过修改参照关系中的元组来恢复完整性约束,而不是拒绝这样的动作。
create table course
(...
foreign_key (dept_name) references department
on delete cascade
on update cascade,
...)
如删除 department 中的元组导致此参照完整性约束被违反,
由于有 on 语句,删除不被系统拒绝,而是对 course 关系作"级联"删除。
类似地,如果更新被参照字段时违反了约束,更新操作不被拒绝,
而是将 course 中参照的元组的 dept_name 字段也改为新值。
SQL还允许 foreign key 子句指明除了cascade 外的其他动作,如约束被违反:
可将参照域(这里为dept_name)设置为null(用set null 代替cascade),或设置为域的默认值(用set default)。
如存在涉及多个关系的外码依赖链,则在链一端所做的删除或更新可能传至整个链。
空值使得SQL中参照约束的语义复杂化了。
外码中的属性允许为 null ,只要它们没有被声明为 not null。
如果给定元组中外码的所有列上均取非空值,则对该元组采用外码约束的通常定义。
如果某外码列为 null ,则该元组自动被认为满足约束。
事务可能包含几个步骤,在某一步之后完整性约束也许会暂时被违反。但是后面某一步也许就消除此违反。
SQL标准允许将 initially deferred 子句加入到约束声明中。
这样完整性约束不是在事务的中间步骤上检查,而是在事务结束时检查。
一个约束可被指定为可延迟的(deferrable),这意味着默认情况下,它会被立即检查,但是在需要时,可以延迟检查。
对声明为可延迟的约束,执行 set constraints constraint-list defered 语句作为事务的一部分,
会导致对指定约束的检查被延迟到事务结束时执行。
如果一个数据库支持在 check 子句中出现子查询,就可在关系 section 上声明如下所示的参照完整性约束:
heck (time_slot_id in (select time_slot_id from time_slot))
这个 check 检测在 section 关系中每个元组的time_slot_id 的确是在time_slot 关系改变时也需要检测(如在 time_slot 关系中,当一个元组被删除或修改的情况下)。
在我们大学模式上,另一个自然的约束是:每个课程段都至少需要一位教师来讲授。
一种方案是声明section关系的属性集(course_id, sec_id, semester, year)作为外码,它参照了 teaches 关系中的相应属性。
如数据库系统支持在 check 约束中出现子查询的话,可以使用与 time_slot 属性类似的 check 约束来强制实现上述约束。
一个断言(assertion)就是一个谓词,它表达了我们希望数据库总能满足的一个条件。
域约束和参照完整性约束是断言特殊形式。
例子:
SQL中断言的形式:
create assertion <assertion-name> check <predicate>
用SQL写出第一个约束的示例:
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’ )
当创建断言时,系统要检查其有效性。
如果断言有效,则今后只有不破坏断言的数据库修改才被允许。如果断言较复杂,则检查会带来相当大的开销。因此,使用断言应该特别小心。
目前,还没有一个广泛使用的数据库系统支持check子句的谓词中使用子查询或 create assertion 结构。
SQL标准支持与日期和时间相关的几种数据类型:
date ’2001-04-25’
time ’09:30:00’
timestamp ’2001-04-25 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 来提取。
SQL定义了一些函数以获取当前日期和时间。
例如:
current_date 返回当前日期
current_time 返回当前时间(带有时区)
localtime 返回当前的本地时间(不带时区)
时间戳【日期+时间】由 current_timestamp 及 localtimestamp返回。
SQL还支持 interval 类型,它允许在日期、时间和时间间隔上进行计算。
SQL允许为属性指定默认值,如下:
create table student
(ID varchar (5),
name varchar (20) not null,
dept_name varchar (20),
tot_cred numeric (3,0) default 0,
primary key (ID));
下面的插入语句说明了在插入操作中如何省略 tot_cred 属性的值:
insert into student(ID, name, dept_name)
values (’12789’, ’Newman’, ’Comp. Sci.’);
在关系的属性上所创建的索引(index)是一种数据结构,它允许数据库系统高效地找到关系中那些在索引属性上取给定值的元组,而不用扫描关系中的所有元组。
很多数据库支持如下创建索引:
create index studentID_index on student(ID);
上述语句在 student 关系的属性 ID 上创建了一个名为 studentID_index 的索引。
SQL提供字符数据的大对象数据类型(clob)和二进制数据的大对象数据类型(blob)。
例如,可以声明属性:
book review clob(10KB)
image blob(10MB)
movie blob(2GB)
一个应用常用一个SQL查询来检索出一个大对象的"定位器",然后在宿主语言中用这个定位器来操纵对象,应用本身也是用宿主语言书写的。
SQL支持两种形式的用户定义数据类型。
可以用 create typep 子句来定义新类型:
create type Dollars as numeric(12,2) final;
create type Pounds as numeric(12,2) final;
可以把 department 表定义为:
create table department
(dept_name varchar (20),
building varchar (15),
budget Dollars);
一种类型的数值可被转换(也即 cast)到另一个域:
cast (department.budget to numeric(12,2))
SQL提供了drop type 和 alter type 子句来删除或修改之前创建过的类型。
域(domain),它可以在基本类型上施加完整性约束。
create domain DDollars as numeric(12,2) not null;
DDollars 域可作为属性类型。
类型和域之间两个大的差别:
当把 check 子句应用到域上时,允许模式设计者指定一个谓词,被声明为来自该域的任何变量都必须满足这个谓词。
create domain YearlySalary numeric(8,2)
constraint salary_value_test check(value >= 29000.00);
作为另一个例子,使用 in 子句可以限定一个域只包含指定的一组值:
create domain degree_level varchar(10)
constraint degree_level_test
check (value in ('Bachelors', 'Masters', or 'Doctorate'));
创建与现有某个表的模式相同的表。
SQL提供了一个 create table like 的扩展来支持这项任务:
create table temp_instructor like instructor;
当书写一个复杂查询时,把查询的结果存储成一个新表通常是很有用的;这个表通常是临时的。
create table t1 as
// 新表所参照的表
(select *
from instructor
where dept_name= ’Music’)
// 表示新表使用前面表的数据初始化
with data;
create table… as 语句与 create view 语句非常相似,并且都用查询来定义。
两者主要的区别在于当表被创建时表的内容被加载,但视图的内容总是反映当前查询的结果。
当代数据库系统提供了三层结构的关系命名机制。
最顶层由目录(catalog)构成,每个目录都可包含模式(schema)。
每个用户有一个默认的目录和模式,这个组合对用户来说是唯一的。
要在数据库上做任何操作,用户(或程序)都必须先连接到数据库。
为了唯一标识出一个关系,须使用一个名字,它包含三部分,例如:
// 目录.模式.关系
catalog5.univ_schema.course
当名字的目录被认为是连接的默认目录时,可省略目录部分。
如果一个关系存在于特定用户的默认模式中,那么连模式的名字也可以省略。
如果catalog5是默认目录并且 univ_schema是默认模式,我们可以只用 course。
当有多个目录和模式可用时,不同应用和不同用户可以独立工作而不必担心命名冲突。
默认目录和模式是为每个连接建立的SQL环境(SQL environment)的一部分。
可以用 create schema 和 drop schema 语句来创建和删除模式。
对数据的授权包括:
除了在数据上授权之外,用户还可被授予在数据库模式上的权限,例如,可以允许用户创建、修改或删除关系。
拥有某些形式的权限的用户还可以把这样的权限转授(授予)给其他用户,或者撤销(收回)一种此前授出的权限。
最大的授权形式是被授予数据库管理员的。
数据库管理员可以授权新用户、重构数据库,等等。
SQL标准包括select、insert、update、delete权限。
权限所有权限(all privileges)可以用作所有允许权限的简写形式。
一个创建了新关系的用户将自动被授予该关系上的所有权限。
grant 语句用来授予权限。此语句的基本形式为:
grant <权限列表>
on <关系名或视图名>
to <用户/角色列表>;
权限列表使得一个命令可以授予多个权限。
关系上的 select 权限用于读取关系中的元组。
grant select on department to Amit, Satoshi;
关系上的 update 权限允许用户修改关系中的任意元组。
grant update (budget) on department to Amit, Satoshi;
关系上的 insert 权限允许用户往关系中插入元组。
关系上的 delete 权限允许用户从关系中删除元组。
用户名 public 指系统的所有当前和将来的用户。
在默认情况下,被授予权限的用户/角色无权把此权限授予其他用户/角色。
SQL允许用授予权限,来指定权限的接受者可以进一步把权限授予其他用户。
revoke语句来收回权限。
revoke <权限列表>
on <关系名或视图名>
from <用户/角色列表>;
收回前面我们所授予的那些权限,我们书写下列语句:
revoke select on department from Amit, Satoshi;
revoke update (budget) on department from Amit, Satoshi;
在数据库中建立一个角色集,可以给角色授予权限,就和给每个用户授权的方式完全一样。
任何可以授予给用户的权限都可以授予给角色。给用户授予角色就跟给用户授权一样。
在SQL中创建角色如下所示:
create role instructor;
基于角色授予权限
grant select on takes
to instructor;
角色可授予给用户,也可授予给其他角色
grant dean to Amit;
create role dean;
grant instructor to dean;
grant dean to Satoshi;
一个用户或一个角色的权限包括:
创建视图
create view geo_instructor as
(select *
from instructor
where dept_name = ’Geology’);
实际select instructor需要用户有此权限
select *
from geo_instructor;
创建视图的用户不需要获得该视图上的所有权限。
SQL标准为数据库模式指定了一种基本的授权机制:
只有模式的拥有者才能够执行对模式的任何修改,诸如创建或删除关系,增加或删除关系的属性,以及增加或删除索引。
SQL提供了一种 references 权限,允许用户在创建关系时声明外码。
SQL的 references 权限可以与 update 权限类似的方式授予到特定属性上。
grant 允许Mariano创建,可参照department的码dept_name的关系:
grant references (dept_name) on department to Mariano;
在默认方式下,被授予权限的用户/角色无权把得到的权限再授予另外的用户/角色。
如果在授权时允许接受者把得到的权限再传递给其他用户,可在 grant 命令后附加 with grant option 子句。
如果希望授予 Amit 在 department 上的 select 权限,且允许Amit将该授权授予其他用户,可以写:
grant select on department to Amit with grant option;
一个对象(关系/视图/角色)的创建者拥有该对象上的所有权限,包括给其他用户授权的权限。
例子:
考虑大学数据库中 teaches 关系上更新权限的授予。
假设最初数据库管理员将 teaches 上的更新权限授给用户U1、U2 和 U3,他们接下来又将这一授权传递给其他用户。
指定权限从一个用户到另一个用户的传递可以表示为授权图(authorization graph)。
从一个用户/角色那里收回权限可能导致其他用户/角色也失去该权限。这一行为称作级联收回。
revoke 语句可以申明 restrict 来放在级联收回:
revoke select on department from Amit, Satoshi
此时如果存在级联收回,系统返回一个错误,且不执行收权动作。
可用cascade替换restrict来表示需要级联收回。
然而,cascade可以省略,因为它是默认行为。
下面的 revoke 语句仅仅收回 grant option ,而并不是真正收回 select 其权限:
revoke grant option for select on department from Amit;
SQL允许权限由一个角色授予,而不是由用户来授予。
SQL有一个与会话所关联的当前角色概念。
默认情况下,一个会话所关联的当前角色是空的(某些特殊情况除外)。
一个会话所关联的当前角色可通过执行 set role role_name 来设置。
指定的角色必须已经授予给用户,否则 set role 语句执行失败。
如果要在授予权限时将授权人设置为一个会话所关联的当前角色,并且当前角色不为空的话,可在授权后加上如下子句:
granted by current_role
假设将角色 instructor(或其他权限) 授给 Amit 是用granted by current_role 子句实现的,当前角色被设置为 dean 而不是授权人,那么,从 Satoshi 处收回角色/权限(包括角色 dean)就不会导致收回以角色 dean 为授权人所授予的权限。
学习参考资料:
《数据库系统概念》第6版