本文只介绍一些基础中级SQL 对于其中各个语句涉及到的具体的底层原理,会在后续阅读书的后续章节更新 文章中的sql语句都是SQL标准规定的,具体到某些数据库中,可能会存在出入 文章的一些操作、实例都是在Mysql下执行的
二、中级SQL
1.连接表达式
1.1 自然连接
自然连接运算作用于两个关系,并产生一个关系作为结果。自然连接只考虑在两个关系的模式中都出现的哪些属性上取值相同的元组。
r natural g
select attr_id,attr_name,attr_group_id
from pms_attr natural join pms_attr_attrgroup_relation;
可以指定一个属性名列表:
r1 join r2 using(A1,A2) :要求 r1.A1 与 r2.A1,r1.A2 与 r2.A2 相同,即使r1、r2有相同的属性A3 也不需要相同。
1.2 外连接
假设在 r 表中的一个元组除了在 r 表中存在外,在 g 表中并不存在相同的属性。这种元组在自然连接时,会被排除掉。
而使用外连接,可以保留此元组,并将该元组 g 表中的属性设置为空。
三种形式的外连接,分别决定保留哪个关系的元组:
- 左外连接(left outer join):只保留出现在左外连接运算之前(左边)的关系中的元组。
- 右外连接(right outer join):只保留出现在右外连接运算之前(右边)的关系中的元组。
- 全外连接(full outer join):保留出现在两个关系中的元组。
为了与外连接区分,将之前学习的不保留未匹配元组的连接运算称为内连接运算。
natural left/full/right outer join
1.3 连接条件
on 连接条件在一般情况下与 where 用处相同,但是在与外连接相邻时,on 是外连接语句的一部分,但 where 子句 却不是。
1.4 连接类型与条件
在SQL 中把常规连接称为内连接
当join 子句中没有使用 outer 前缀时,缺省的连接类型是内连接。从而
select *
from student join takes using(ID);
等价于:
select *
from student inner join takes using(ID);
类似地,自然连接等价于自然内连接:
连接类型 | 连接条件 |
---|
inner join | natural | left outer join | on | right outer join | using (A1,A2,…,An) | full outer join | |
2.视图
我们可以将查询的结果保存下来,保存为视图方便我们经常调用或者适用于不同权限的人员访问。
有点类似于我们之前学习的 with 子句,但是 with 子句只能在一个特定的查询中使用。
2.1 视图定义
create view v as <查询表达式> ,查询表达式可以是任何合法的查询表达式。视图名称用v表示。
2.2 在SQL查询中使用视图
因为表达式 sum(salary) 并没有一个名称,所以在视图定义时指定了该属性名
create view departments_total_salary(dept_name,total_salary) as
select dept_name,sum(salary)
from instructor
group by dept_name;
**注意 :**为了避免一旦修改定义该视图的关系时视图就会过期。我们通常这样定义视图:数据库系统存储视图本身的定义,而不存储定义该视图的查询表达式的求值结果
可以看到视图的定义知识一条创建视图查询语句,所以当我们修改原来关系中的属性类型或属性值时,视图中相应的数据也会修改。因为在每次查询视图时都是在“重新创建视图”
顺理成章地,可以基于视图查询的结果创建视图
2.3 物化视图
物化视图:某些数据库允许存储视图关系。如果用于定义视图的实际关系发送改变,则视图也跟着修改以保存最新。
如果该视图时物化的,数据库将其结果存储在数据库中,从而允许使用视图的查询可以通过使用预计算的视图结果来更快地运行,而不是重新计算视图的结果。
物化视图维护(视图维护):保持物化视图一直在最新状态的过程。
2.4 视图更新
假设我们向视图中添加一个元组(A1,A2,…,An),但是A1属性不存在视图中,但存在于定义视图的关系中,那么是否能够插入成功?
由于上述情况的种种问题,除了一些有限的情况,并不允许对视图关系进行更新。
只有在满足以下条件时,才可以对视图进行更新:
- from 子句中只有一个数据库关系
- select 子句中只包含关系的属性名,并不包含任何表达式、聚集或distinct声明
- 没有出现在 select 子句中的任何属性都可以取 null 值;也就是说,这些属性没有非空约束,也不构成主码的一部分
- 查询中不含有group by 或 having 子句
定义视图的关系:
3.事务
事务:由查询和(或)更新语句的序列组成。
SQL标准规定当一条SQL语句被执行时,就隐式开始了一个事务。下列语句之一会结束该事务:
- commit (work):提交当前事务。它使事务执行的更新在数据库中成为永久性的。在事务被提交后,一个新的事务就会自动开始。
- rollback (work):回滚当前事务。它会撤销事务中SQL语句执行的所以更新。因此,数据库状态被恢复到它执行该事务的第一条语句之前的状态
如果在一个事务的执行期间检测到某种错误,事务回滚是有效的。在断电或其他系统崩溃时,回滚会在系统重启时执行。
一个事务或者在完成其所有步骤后提交其操作,或者在不能成功完成其所有动作的情况下回滚其所有动作,通过这种方式数据库提供了对事务具有原子性的抽象,原子性也就是不可分割性。要么事务的所有影响被反映到数据库中,要么任何影响也没有(在回滚之后)。
在大多数的数据库中的缺省模式下,每条SQL自成一个事务,且语句一旦执行完成就提交该事务。如果一个事务需要多条SQL语句组成,就必须关闭单条SQL语句的这种自动提交。很多数据库都提支持关闭自动提交的命令。
4.完整性约束
完整性约束:保证授权用户对数据库所做的修改不会导致数据一致性的丢失。
像我们之前学习的主码、外码、not null 都属于完整性约束
4.1 非空约束
非空约束禁止对该属性插入空值。它是域约束的一个实例。
主码默认实现非空约束。
4.2 唯一约束
unique(A1,A2,...,An)
唯一性声明在关系中没有两个元组能在所有列出的属性上取值相同
唯一性的属性 允许为空(除非声明非空),因为空值不等于其他的任何值。
4.3 check子句
当应用于关系时,check§子句指定了一个谓词P,关系中的每个元组都必须满足谓词P。
create table student
(id varchar(8),
age varchar(8),
sex varchar(8),
check(age >= 18));
4.4 引用完整性
foreign key(dept_name) references department
这个语句声明:对于每个课程元组,元组中指定的系必须在 department 关系中存在。
有些系统(包括Mysql)并行指定引用关系的属性
还可以在外码子句中声明级联删除、级联更新。
foreign key(dept_name) references department
on delete cascade
on update cascade;
如果删除外码引用的关系中的一个元组违反了这种引用完整性约束,则系统并不拒绝该删除,而是对当前关系进行**“级联”**删除,即删除引用了被删除系的元组。
4.5 给约束赋名
我们可以在约束的前面使用constraint + name 进行赋名
constraint minsalary check(salary > 20000)
也可以使用约束名删除约束:
alter table instructor drop constraint minsalary
4.6 事务中对完整性约束的违反
事务中对完整性约束违反的例子:
SQL 标准允许将initally deferred 子句加入约束声明中;这样约束就不是在事务的中间步骤上检查,而是在事务结束的时候检查。
对于约束可以设置延迟执行,但在大多数数据库都不支持,所有不再介绍。
4.7 断言
一个断言(assertion)就是一个谓词,它表达了我们希望数据库总能满足的一个条件。
create assertion credits_earned_constraint check
(not exists(select ID
from student
where tot_cred <> (select coalesce(sum(credits),0)
from takes natural join course
where student.ID = takes.ID
and grade is not null and grade<> 'F')));
5.SQL 的数据类型与模式
5.1 SQL 中的日期和时间类型
SQL 标准支持与时间与日期相关的几种数据类型:
- 日期(date):日历日期,包括年(四位)、月和日
- 时间(time):一天中的时间,时、分、秒。可以用变量time§来指定秒小数点后的数字位数(缺省值为0)。通过指定 time with timezone 还可以把时区信息与时间一同存储
- 时间戳(timestamp):date和time 的结合。可以用tiemstamp§来指定秒小数点后的数字位数(缺省值为0)。通过指定 with timezone 可以存储时区信息
date ‘2020-9-15’
time ‘17:38:24’
timestamp ‘2020-9-15 17:38:24.45’
一些函数:year(date\timestamp)、month(date\timestamp)等等可以返回对应日期、时间的年月日等。
- current_date:返回当前日期
- current_time:返回当前时间(带有时区)
- localtime:返回当前的本地时间(不带时区)
- current_timestamp:时间戳(带有时区)
- localtimestamp:本地时间戳(不带时区)
如果x和y都是 date 类型,那么 x-y 就是一个时间区间,其值为 x与y 间隔的日期
5.2 类型转换和格式化函数
cast(e as t) :将表达式 e 转换为 类型 t
select cast(ID as numeric(5)) as inst_id
from instructor
order by inst_id
5.3 缺省值
SQL 允许为属性指定缺省值
create table student
(...
tot_cred numeric(3,0) default());
tot_cred 的默认值为0
5.4 大对象类型
SQL为字符数据(clob)和二进制数据(blob)提供了大对象数据类型。lob => large object
book_review clob(10kb)
image blob(10MB)
movle blob(2GB)
5.5 自定义类型
支持两种形式的自定义类型:独特类型、结构化数据类型。
在这里只介绍独特类型。
create type Dollars as numeric(12,2) final;
create table department
(dept_name varchar(20),
building varchar(15),
budget Dollars);
独特类型创建的属性具有强类型检查,因此表达式(department.budget + 20)将不会被接受。
其实早在SQL引入 自定义类型 之前,SQL 就有与自定义类型相近的概念:域(domain)。
- 在域上可以施加完整性约束,也可以为域类型的变量定义定义缺省值(但在自定义类型上都不行)。
- 域不是强类型的,一个域类型的值可以被赋给另一个域类型,只要它们的基本类型是相容的。
create domain YearlySalary numeric(8,2)
constraint salary_value_test check(value >= 29000.00)
5.6 生成唯一码值
唯一码值会根据插入的元组自动递增,插入元组时不必设置唯一码值。
唯一码值一般都是主码,并且需要设置非空。
在Mysql 中,id bigint(20) auto_increment 来设置唯一码值。
5.7 create table 扩展
create table r like t 创建一个和 t 表结构相同的 r 表。
create table r as (select * from t where name = 'Merist') with data 自定义从 t 表中获取到的属性与值创建一个 r 表。
with data :是否创建时添加数据。
create table ... as 与 create view 非常相似,但是create view 的实质是一条查询语句,它的所有属性、数据都是动态获取的。
create table ... as 创建的是表,一旦建成,与原表没有任何关系。
5.8 模式、目录与环境
在SQL 中,规定数据给的体系结构依次按照包含关系由 目录、模式组成
在Mysql 中,“数据库”对应“目录”,“表、视图”对应模式。
6 SQL中的索引定义
关系属性上的索引是一种数据结构,它允许数据库高效地找到在关系中具有该属性指定值的那些元组,而不扫描关系的所有元组。
create index <索引名> on <关系名>(<属性列表>) SQL查询处理器将使用定义的索引来查询所需的元组,而不是读取整个关系。
drop index <索引名> 删除索引
7.授权
对数据的授权:
- 授权读取数据
- 授权插入新数据
- 授权更新数据
- 授权删除数据
7.1 权限的授权与收回
SQL 标准包括 选择(select)、插入(insert)、更新(update)、删除(delete)权限。
授权语句:
grant <权限列表>
on <关系名或视图名>
to <用户或角色列表>
收权语句:
revoke <权限列表>
on <关系名或视图名>
to <用户或角色列表>
7.2 角色
在SQL标准中,规定了一个角色集来存放用户及其权限的数据。其中的每个用户就叫做角色。
在Mysql中,数据库所有用户权限信息默认存储在数据库mysql 中的 user 表中。可以使用查询语句查询用户的权限。
7.3 权限的转移
grant select on r to username with grant option
一个用户具有权限的充要条件是:当且仅当存在从授权图的根(即代表数据库管理员的节点)到代表该用户的节点的路径。
7.4 权限的收回(级联收回)
为了防止用户通过互相授权来破坏权限的收回规则,在默认情况下,数据库的收回都遵守级联收回。
级联收回:从一个用户、角色那里收回权限可能导致其他用户、角色也失去该权限。
授权也可以声明限定(restrict)来防止级联收回:
revoke select on department from Amit,Satoshi restrict
关键字cascade 可以替代restrict 来使用级联回收,不过一般省略。
7.5 行级授权
我们已经学习过的授权类型适用于关系或视图级别。一些数据库系统在关系中的特定元组级别提供了细粒度的授权机制。
例如,假设我们希望允许学生在takes关系中查看他自己的数据,但不允许查看其他用户的那些数据。如果数据库支持,我们可以使用行级授权来强制实施此类限制。下面我们将描述 Oracle 中的行级授权;PostgreSQL 和 SQL Server 也使用概念上类似的机制来支持行级授权,但使用的是不同的语法。
Oracle 虚拟私有数据库( Virtual Private Database , VPD )功能支持如下所示的行级授权。它允许系统管理员将函数与关系相关联;该函数返回一个谓词,该谓词会自动添加到使用该关系的任何查询中。该谓词可以使用sys_context函数,它返回代表正在执行查询的用户的标识。对于我们的示例,学生需要访问他们在takes关系中的数据,我们将指定以下谓词与takes关系相关联:
ID=sys_context('USERENV','SESSIONUSER')
系统将此谓词添加到使用takes关系的每个査询的where子句中。其结果是,每名学生只能看到ID值与其ID 相匹配的那些takes元组。
VPD提供了关系的特定元组或行的级别上的授权,因此被称为行级授权( row - level authorization )机制。如上所述添加谓词的一个隐患是:它可能会显著改变查询的含义,例如,如果一个用户编写查询来查找所有课程的平均成绩,则他最终会得到他的成绩的平均值,而不是所有成绩的平均值。虽然系统会为重写的查询提供“正确”的答案,但用户可能认为该答案与其所提交的查询不对应。
|