4.1 连接表达式
4.1.1 连接条件
on是另一种形式的连接,其中可以指定任意的连接条件。on条件允许在参与连接的关系上设置通用的谓词,该谓词的写法与where子句谓词类似。
1 select* 2 from student join takes on student.ID = takes.ID;
on条件有两个优点,首先,被称为外连接的这类连接来说,on条件的表现与where条件是不同的。其次,如果在on子句中指定连接条件,并在where子句中出现其余的条件,这样的SQL查询通常更容易懂。
4.1.2 外连接
实际上有三种形式的外连接:
- 左外连接(left outer join)只保留出现在左外连接运算之前(左边)的关系中的元组。
- 有外连接(right outer join)只保留出现在右外连接运算之前(右边)的关系中的元组。
- 全外连接(full outer join)保留出现在两个关系中的元组。
不保留未匹配元组的连接运算被称为内连接运算。
右外连接和左外连接是对称的。如来自右侧关系中的不匹配左侧关系任何元组的元组被补上空值,并加入到有外连接的结果中。
1 select * 2 from takes natural right outer join student; 3 4 select * 5 from takes natural left outer join student;
得到的结果是一样的,只不过结果中属性出现的顺序不同。
全外连接是左外连接和右外连接类型的的组合。
on和where在外连接中的表现是不同的,其原因是外连接只为那些对那些对相应内连接结果没有贡献的元组补上空值并加入结果。
on条件是外连接声明的一部分,但where子句却不是。
4.1.3连接类型和条件
为了区分常规连接和外连接,SQL中把常规连接称作内连接。用inner join来代替outer join,说明使用的是常规连接,然而关键字inner是可选的。
类似地,natural join等价于natural inner join。
4.2 视图
SQL允许通过查询来定义“虚关系”,它在概念上包含查询的结果。虚关系并不是预先计算并存储,而是在使用虚关系的时候才通过执行查询被计算出来。
任何像这种不是逻辑模型的一部分,但作为虚关系对用户可见的关系称为视图。在任何给定的实际关系集合上能够支持大量视图。
4.2.1 试图定义
create view命令定义视图。
create view v as <query expression>
4.2.2 SQL查询中使用视图
一旦定义了一个视图,我们就可以用视图名指代该视图生成的虚关系。一个视图可能被用到定义另一个视图的表达式中。
1 create view departments_total_salary(dept_name, total_salary) as 2 select dept_name, sum(salary) 3 from instructor 4 group by dept_name;
4.2.3 物化视图
特定的数据库系统允许存储视图关系,但是如果用于定义视图的实际视图的实际关系改变,视图也跟着修改,这样的视图被称为物化视图。
保持物化视图一直在最新状态的过程称为物化视图维护,或者通常简称视图维护。
4.2.4 视图更新
尽管对查询而言,视图是一个有用的工具,但如果我们用它们来表达更新,插入或删除,它们可能带来严重的问题。困难在于,用视图表达的数据库修改必须被翻译为对数据库逻辑模型中实际关系的修改。
除了一些有限的情况之外,一般不允许对视图关系进行修改。
一般来说,如果定义视图的查询对下列条件都能满足,我们称SQL视图是可更新的:
- from子句中只有一个数据库关系。
- select子句中只包含关系的属性名,不包含任何表达式、聚集或distinct声明。
- 任何没有出现在select子句中的属性可以取空值;即这些属性上没有not null约束,也不构成主码的一部分。
- 查询中不包含group by或having子句。
在默认情况下,SQL允许执行上述更新,但是可以通过在视图定义的末尾包含with check option子句的方式来定义视图。这样,如果向视图中插入一条不满足视图的where子句条件的元组,数据库系统将拒绝该插入操作。
4.3 事务
事务由查询和更新语句的序列组成。SQL标准规定当一条SQL语句被执行,就隐式地开始了一个事务。下列SQL语句之一会结束一个事务:
- Commit work:提交当前事务,也就是将事务所做的更新在数据库中持久保存。在事务被提交后,一个新的事务自动开始。
- Rollback work:回滚当前事务,即撤销该事务中所有SQL语句对数据库的更新。
关键词work在两条语句中都是可选的。
一旦某事务执行了commit work,它的影响就不能用rollback work来撤销了。
在很多SQL实现中,默认方式下每个SQL语句自成一个事务,且一执行完就提交。如果一个事务要执行多条SQL语句,就必须关闭单独SQL语句的自定提交。如何关闭自动提价也依赖于特定的SQL实现。
SQL1999标准允许多条SQL语句包含在关键字begin atomic…end之间。所有在关键字之间的语句构成了一个单一事务。
4.4 完整性约束
完整性约束通常被看成是数据库模式设计过程的一部分,它作为用于创建关系的create table命令的一部分被声明。然而,完整性约束也可以通过使用alter table table-name add constraint命令施加到已有关系上,其中constraint可以是关系上的任意约束。当执行上述命令时,系统首先保证关系满足指令约束。如果满足,那么约束被施加到关系上;如果不满足,则拒绝执行上述命令。
4.4.1 单个关系上的约束
允许的完整性约束包括:
- not null
- unique
- check(<谓词>)
4.4.2 not null约束
not null声明禁止在该属性上插入空值。
4.4.3 unqiue约束
unqiue声明指出属性Aj1, Aj2, ... Ajm形成了一个候选码;即在关系中没有两个元组能在所有列出的属性上取值相同。
4.4.4 check子句
当应用于关系声明时,check(P)子句指定一个谓词P,关系中的每个元组都必须满足谓词P。根据SQL标准,check子句中的谓词可以包括子查询在内的任意谓词。
4.4.5 参照完整性
外码声明:foreign key(dept_name) references department。
当参照完整性约束时,通常的处理是拒绝执行导致完整性破坏的操作。但是,在foreign key子句中可以指明:如果被参照关系上的删除或更新动作违反了约束,那么系统必须采取一步步骤通过修改参照关系中的元组来恢复完整性约束,而不是拒绝这样的动作。
1 create table course 2 {... 3 forgein key (dept_name) references department 4 on delete cascade 5 on update cascade, 6 ...};
由于有了与外码声明相关联的on delete cascade子句,如果删除department中的元组导致此参照完整性的约束被违反,则删除并不被系统拒绝,而是对course关系做“级联”删除,即删除参照了被删除系的元组。类似地,如果更新被参照字段时时违反了约束,,则更新操作并不被系统拒绝,而是将course中的参照元组的dept_name字段改为新值。SQL还允许forgein key子句指明除cascade以外的其他动作,如果约束被违反:可将参照域(这里是dept_name)置为null(用set null代替cascade),或者置为域的默认值(用set default)。
如果存在涉及多个关系的外码依赖链,则在链一端所做的删除或更新可能传至整个链。如果一级联更新或删除导致的对约束的违反不能通过进一步的级联操作解决,则系统终止该事务。于是,该事务所做的所有改变及级联动作将被撤销。
4.4.6 事务中对完整性约束的违反
事务可能包含几个步骤,在某一步之后完整性约束也许会暂时被违反,但是后面的某一步也许就会消除这个违反。
为了处理这样的情况,SQL标准允许将initially deferred子句加入到约束声明中;这样完整性约束不是在事务的中间步骤上检查,而是在事务结束时检查。一个约束可以被指定为可延迟的。对于声明为可延迟的约束,执行set constraints constraint_list deferred语句作为事物的一部分,会导致对指定约束的检查被延迟到该事务结束时执行。
4.4.7 复杂check条件与断言
SQL标准所定义,check子句中的谓词可以是包含子查询的任意谓词。如果一个数据库实现支持在check子句中出现子查询,我们就可以在关系section上声明如下参照完整性约束: check (time_slot_id in (select time_slot_id from time_slot))
一个断言就是一个谓词,它表达了我们希望数据库总能满足的一个条件。域约束和参照完整性约束都是断言的特殊形式。断言形式如下:
create assertion <assertion-name> check <predicate>;
1 create assertion credits_earned_constraint check 2 (not exists (select ID 3 from student 4 where tot_cred < > (select sum (credits) 5 from takes natural join course 6 where student.ID = takes.ID 7 and grade is not null and grade < > 'F')))
4.5 SQL的数据类型与模式
4.5.1 SQL中的日期和时间类型
- date:日历日期,包括年(四位)、月和日。
- time:包括小时、分和秒。可以用time(p)表示秒的小数点后的数字位数。通常指定time with timezone,还可以把时区信息连同时间一起存储。
- timestamp:date和time的组合。可以用变量timestamp(p)来表示秒的小数点后的数字位数(默认6位)。如果指定with timezone,则时区信息也会被存储。
1 date '2001 - 04 - 25' 2 time '09:30:00' 3 timestamp '2001 - 04 - 25 10:29:01.45'
4.5.2 默认值
SQL允许为属性指定默认值。(tot_cred)
1 create table student 2 (ID varchar(5), 3 name varchar(20) not null, 4 dept_name varchar(20), 5 tot_cred numeric(3,0) default 0, 6 primary key(ID));
4.5.3 创建索引
create index studentID_index on student(ID);
4.5.4 大对象类型
SQL提供字符数据的大对象类型(clob)和二进制数据类型的大对象数据类型(blob)。
1 book_review clob (10KB) 2 image blob (10MB) 3 movie blob (2GB)
4.5.5 用户定义的类型
SQL支持两种形式的用户定义数据类型。第一种称为独特类型,另一种称为结构化数据类型。
一个好的类型系统应该能够检测出这类赋值或比较,为了支持这种检测,SQL提供了独特类型的概念。可以用create type子句来定义新类型。如:create type Dollars as numeric(12, 2) final;
一种类型的数值可以被转换(也即cast)到另一个域,cast(department.budget to numeric(12, 2))
SQL提供drop type和alter type子句来删除或修改以前创建过的类型。
4.5.6 create table的扩展
应用常常要求创建于现有的某个表的模式相同的表。SQL提供一个create table like的扩展来支持: create table tempp_instructor like instructor;
当书写一个复杂查询时,把查询的结果存储成一个新表通常是很有用的;这个表通常是临时的。SQL2003提供了一种更简单的技术来创建包含查询结果的表。
1 create table tl as 2 (select* 3 from instructor 4 where dept_name = 'Music') 5 with data;
SQL2003标准定义,如果忽略with data子句,表会被创建,但不会载入数据。
4.5.7 模式、目录与环境
默认目录和模式是为每个连接建立的SQL环境的一部分。环境还包括用户标识。所有通常的SQL语句,包括DDL和DML语句,都在一个模式的环境中运行。
可以用create schema和drop schema语句来创建和删除模式。
4.6 授权
4.6.1 权限的授予与收回
SQL标准包括select、insert、update和delete权限。grand语句用来授权,基本形式:
1 grant<权限列表> 2 on <关系名或视图名> 3 to <用户/角色列表> 4 5 grant select on department to Amit, Satoshi;
revoke语句来收回权限。
1 revoke <权限列表> 2 on <关系名或视图名> 3 to <用户/角色列表> 4 5 revoke seleect on department from Amit, Satoshi;
4.6.2 角色
在数据库中建立一个角色集,可以给角色授予权限,就和给每个用户授权的方式完全一样。每个数据库用户被授予一组他有权扮演的角色(也可能是空的)。
1 create role instructor; 2 3 grant select on takes 4 to instructor; 5 6 grant dean to Amit; 7 create role dean; 8 grant instructor to dean; 9 grant dean to Satoshi;
4.6.3 视图的授权
在函数或过程上可以授权execute权限,以允许用户执行该函数或过程。在默认情况下,和视图类似,函数和过程具有其创建者拥有的所有权限。在效果上,该函数或过程的运行就像其被创建者调用那样。
尽管此行为在很多情况下是恰当的,但是它并不总是恰当。从SQL2003开始,如果函数定义有一个额外的sql security invoker子句,那么它就在调用该函数的用户的权限下执行,而不是在函数定义者的权限下执行。这就允许创建的函数库能够与调用者相同的权限下运行。
4.6.4 模式的授权
SQL提供了一种references权限,允许用户在创建关系时声明外码。SQL的reference权限可以与update权限类似的方式手遇到特定属性上。
grant references(dept_name) on department to Mariano;
4.6.5 权限的转移
如果我们在授权时允许接受者把得到的权限在传递给其他用户,我们可以在相应的grant命令后面附加with grant option子句。
grant select on department to Amit with grant option;
4.6.6 权限的收回
从一个用户/角色那里收回权限可能导致其他用户/角色也失去该权限,这一行为称作级联收回。在大多数的数据库系统中,级联时默认行为。然而,revoke语句可以申请restrict来防止级联收回: revoke select on department from Amit, Satoshi restrict;
可以用关键字cascade来替换restrict,以表示需要级联收回;然而cascade可以省略,是默认的。下面的revoke语句仅仅收回grant option,而不是真正收回select权限:revoke grant option for select on department from Amit;
如果要在授权时将授权人设置为一个会话所关联的当前角色,并且当前角色不为空的话,可以在授权语句后面加上:grant by current_role
假设将角色instructor授予Amit是用grant by current_role子句实现的,当前角色被设置为dean而不是授权人(用户Satoshi),那么,从Satoshi处收回角色/权限(包括角色dean)就不会导致收回角色dean作为授予的权限,即使Satoshi是执行该授权的用户;这样,即使Satoshi额权限被收回,Amit仍然能够保持instructor角色。