第3章 SQL
SQL查询语言概览
SQL语言主要有以下的几个部分:
- 数据定义语言(Data Definition Language,DDL):SQL DDL提供定义关系模式,删除关系以及删除关系模式的命令。
- 数据操纵语言(Data Manipulation Language,DML):SQL DML提供从数据库中查询信息,以及在数据库中插入元组、删除元组和修改元组的能力。
- 完整性(Integrity):SQL DDL包括定义完整性约束的命令,保存在数据库汇中的数据必须满足所有定义的完整性约束。
- 视图定义(View definition):SQL DDL包括定义视图的命令。
- 事务控制(transaction control):SQL包括定义事务的开始和结束的命令。
- 嵌入式SQL和动态SQL(embedded SQL and dynamic SQL):嵌入式和动态SQL定义SQL语句如何嵌入到通用编程语句,例如C、C++和Java中。
- 授权(authorization):SQL DDL包括定义对关系和视图的访问权限的命令。
SQL数据定义
定义的信息包括:
- 每个关系的模式
- 每个数据的取值类型
- 完整性约束
- 每个关系维护的索引集合
- 每个关系的安全性和权限信息
- 每个关系在磁盘上的物理存储结构
基本类型
SQL标准支持多种固有类型:
- char(n):固定长度的字符串,用户指定长度。也可以使用全程character。
- varcharn(n):可变长度的字符串,用户指定最大长度。等价于character varying。
- smallint:小整数类型(和机器相关的整数类型的子集)。
- numeric(p,d):定点数,精度由用户指定。这个数有p为数字,其中d位数字在小数点右边。
- real,double precision:浮点数与双精度浮点数,精度与机器有关。
- float(n):精度至少为n为浮点数。
每个类型都可能包含一个被称作空值的特殊值。
char数据类型存放固定长度的字符串。当比较一个char类型和一个varchar类型的时候,也许会期望在比较值之前自动在varchar类型后面加上额外的空格以使长度一致。
SQL也提供nvarchar类型来存放使用Unicode表示的多语言数据。
基本模式定义
create table 命令通用形式的
create table r
(,
,
<完整性约束>
)
create table 命令后面你用分号结束。
- primary key():primary-key声明表示属性构成关系的主码,主码属性必须非空且唯一。
- foreign key() references:表示关系中任意元素在数据上的取值必须与关系中某个元组上主码属性上的取值。
- not null:一个属性上的not null约束表明在该属性中不允许空值。
使用insert命令将数据加载到关系中
使用delete命令从关系中删除元组
如果想要从一个SQL关系库中去掉一个关系,我们可以使用drop table命令。
要比
更强的语句。后者保留关系r,删除r中的所有元素,前者删除r的所有元组,还删除r的模式。
使用alter table命令为已有的关系增加属性。关系中所有元组在新的属性上的取值为null
SQL查询的基本结构
SQL查询的基本结构由三个字句构成:select、from和where。查询输入是在from字句中列出的关系。这些关系上进行where和select子句中指定的运算,然后产生一个关系作为结果。
单关系查询
找出所有教师的名字
如果我们想要强行删除重复的元素,在select后加入关键词distinct。
如果不想去除重复的元素,可使用all关键词。
select子句还可带含有+、-、*、/运算符的表达式。
where子句允许我们只选出那些在from子句的子句结果关系中满足特定谓词的元组。
找出所有在Computer Science系并且工资超过70000美元的教师的名字。
where子句中允许使用逻辑连词and、or和not,比较运算符为<、<=、>=、=和<>
多关系查询
找出所有教师的姓名、以及他们所在系的名称和系所在建筑的名称。
注意dept_name属性即出现在instructor关系中,也出现在department中,关系名被用作前缀来说明我们使用的是那个属性,相反属性name和building只出现在一个关系中,因而不需要把关系名作为前缀。
一个SQL查询可以包括三种类型的子句:select子句、from子句和where子句。每种子句的作用如下:
- select子句用于列出查询结果中所需要的属性
- from子句是一个查询求值中需要访问的关系列表
- where子句是一个作用在from子句中关系的属性上的谓词
一个典型的SQL查询具有如下形式
尽管各子句必须以select、from、where 的次序写出,但是理解查询所有运算的最容易的方式是以运算的顺序来考察各子句。首先是from,然后是where,最后是select。
通过from子句定义了一个在该子句中所列出关系上的笛卡尔积。可以用集合理论来形式化定义
此结果关系具有来自from子句中所有关系的所有属性。
where子句中的谓词用来限制笛卡尔积所建立的组合,只留下那些对所需答案有意义的组合。
这个语句表明需要teaches.ID和instructor.ID相等的集合。
通常来说,一个SQL查询的含义可以理解如下:
- 为from子句中列出的关系产生笛卡尔积
- 在步骤1的结果上应用where子句中指定的谓词
- 对于步骤2结果中每个元组,输出select子句中指定的属性
上述步骤的顺序有助于明白一个SQL查询的结果应该是怎样的,而不是这个结果是怎样执行的。在SQL的实际实现中不会执行这种形式的查询,会通过(尽可能)只产生满足where子句谓语的笛卡尔积元素来优化执行。
当书写查询时,需要小心设置合适的where子句条件。
自然连接
SQL还支持几种另外的方式使得来自两个或多个关系的信息可以被连接(join)起来。
**自然连接(natural join)**运算作用于两个关系,并产生一个关系作为结果。不同于两个关系上的笛卡尔积,它将第一个关系的每个元组与第二个关系的所有元组都进行连接。自然连接只考虑哪些在两个关系模式中都出现的属性上取值相同的元组对。
可以简洁写作:
在一个SQL查询的from子句中,可以用自然连接将多个关系结合在一起
为了发扬自然连接的优点,同时避免不必要的相等属性带来的危险,SQL提供一种自然连接的构造形式,允许用户来指定需要哪些相等。
join … using运算中需要给定一个属性名列表,其两个输入中都必须指定名称的属性。考虑运算,它与和的自然连接类似,只不过并且成立的前提下。
附加的基本运算
更名运算
SQL提供了一个重要结果关系中属性的方法。形如as子句old_name as new_name。
as子句在重要关系时特别有用。重命名关系的一个原因是把一个长的关系名替换成短的。另外一个原因就是为了适当于需要比较同一个关系中的元组的情况。
像S和T那样被用来重命名关系的标识符在SQL标准中被称作相关名称(correlation name),但是通常也被称作表别名(table alias)或者相关变量(correlation variable)或者元组变量(tuple variable)。
字符串运算
SQL使用一对单引号来表示字符串,如果单引号是字符串组成的部分,那就用双引号来表示。
在SQL标准中,字符串上的相等运算是大小写敏感的。SQL还允许在字符串上有多种函数,不同的数据库所提供的字符串函数集是不同的。
在字符串上可以使用like操作符来实现模式匹配
- 百分号:匹配任意子串
- 下划线:匹配任意一个字符
模式是大小写敏感的。
select子句中的属性说明
星号*可以用在select子句中表示所有的属性。
表示instructor中的所有属性都被选中。
排列元组的显示次序
SQL为用户体用了一些对关系中元组显示次序的控制。order by 子句就可以让查询结果中元组按排列顺序显示。
按照字母顺序列出Physics系的所有教师
order by子句默认使用升序,desc表示降序,或者asc表示升序。
where子句谓词
为了简化where子句,提供了between比较运算符来说明一个值是小于或者等于某个值,同时大于或等于另一个值。
找出工资在90000美元和100000美元之间的教师的姓名
取代
集合运算
SQL作用在关系上的union、interset和except运算对应于数学集合论中的运算。
并运算
找出在2009年秋季开课或者2010年春季开课的或两个学习都开课的所有课程。
union运算是自动去除重复的。如果想要保留重复,则可以使用union all
交运算
找出在2009年秋季和2010年春季同时开课的所有的课程的集合
同时如果想要保留所以重复,就必须使用interset all
差运算
找出在2009年秋季学期开学但不在2010年春季学期开课的所有课程
如果想要保留所有重复,则必须用except all代替。
空值
SQL涉及空值的任何比较运算的结果视为unknown(既不是is null,也不是is not null)。
由于在where子句的谓词可以对比较结果使用诸如and、or和not的布尔运算,这些布尔运算的定义也被扩展到可以处理unknown值。
- and:true and unknown = unknown ,false and unknown = unknown,unknown and unknown = unknown
- or:true or unknown = true ,false or unknwon = unknown ,unknown or unknown = unknown
- not:not unknown = unknown
聚集函数
聚集函数是以值的一个集合为输入、返回单个值的函数。SQL提供了五个固有聚集函数:
- 平均值:avg
- 最小值:min
- 最大值:max
- 总和:sum
- 计数:count
基本聚集
找出Computer Science系教师的平均工资
该查询的结果是一个具有单属性的关系。可以使用as子句赋个有意义的名称
有些情况下在计算聚集函数前需先删除重复元组。可以使用关键词distinct。
分组聚集
有的时候我们不仅希望将聚集函数作用在单个元组集上,而且也希望将其作用在一组元组集上。
找出每个系的平均工资
当SQL查询使用分组的时候,一个很重要的事情就是需要保证出现在select语句中但没有被聚集的属性只能是出现在group by子句中那些属性。
ID没有出现在group by子句中,但是出现在select子句中,并且没有被聚集。因为在一个特定分组中的每一个教师都有不同的ID,但是每个分组结果输出一个元组,但是无法确定使用哪一个ID。
having子句
对分组的限定条件要比元组限定条件更有用。SQL的having句,having子句中谓词在形成分组后起作用,因此可以使用聚集函数。
与select子句的情况相同,任何出现在having子句中,但是没有被聚集的属性必须出现在group by子句中,否则查询就是错误的。
包含聚集、group by或having 子句的查询的含义可通过下述操作序列来定义:
- 与不带聚集的查询情况类似,最先根据from子句来计算出一个关系。
- 如果出现了where子句,where子句中的谓词将应用到from子句的结果关系上。
- 如果出现了group by子句,满足where谓词的元组通过 group by子句形成分组,如果没有group by子句,满足where谓词的整个元组集被当做一个分组。
- 如果出现having子句,它将应用到每一个分布,如果不满足having子句谓词的分组将别抛弃
- select子句利用剩下的分组产生查询结果中的元组,即在每个分组上应用聚集函数来得到单个结果元组。
对于在2009年讲授的每个课程段,如果该课程段有至少两个学生选课,找出选修该课程的所有学生的总分数的平均值
对空值和布尔值的聚集
聚集函数根据以下原则处理空值:除了**count()**外所有的聚集函数都忽略输入集合中的空值。规定空集的count运算值为0,其他所有聚集函数在输入为空集的情况下返回一个空值。
嵌套子查询
SQL提供嵌套子查询机制,子查询嵌套是另一个查询中的select-from-where表达式。子查询嵌套在where子句中,通常用于对集合的成员资格、集合的比较以及集合的基数进行检查。
集合成员资格
SQL允许测试元组在关系中的成员资格。连接词in测试元组是否是集合中的成员,集合是由select子句产生的一组构成的。连接词not in则测试元组是否不是集合中的成员。
2009年秋季开课的课程集合与2010年春季开课的课程集合
现在我们采用另一种方式来查询这个集合,先查找出2009年的课程,然后在其中查询出2010年的课程。
这种写法有助于用户比较接近自然的方法来思考和查询。
集合的比较
找出满足下面条件的所有教师的名字,他们的工资至少比Biology系某一个教师的工资要高
但是SQL提供了另外一种方式书写上面的查询。短语“至少比某一个要大”在SQL中用**>some**表示
同样的SQL也允许<some、<=some、>=some、=some(in)、<>some(not in)。
对于短语“比所有的都大”用**> all** 来表示
类似,也存在<all、<= all、>=all、=all、<>all(not in)但=all不等价于in。
空关系测试
SQL还有一个特性可测试一个子查询的结果中是否存在元组。exists结构在作为参数的子查询非空时返回true值,使用exists结构,我们还能用另外一种方法书写查询
在2009年秋季学习和2010年春季学期同时开课的所有课程
上述查询还说明SQL一个特性,来自外层查询的一个相关名称可以用在where子句的子查询中。使用来自外层查询相关名称的子查询被称作相关子查询(correlated subquery)。
我们可以用not exists结构测试子查询的结果。
重复元组存在性测试
SQL提供了一个布尔函数,用于测试一个子查询的结果中是否存重复元组,如果作为参数的子查询数组中没有重复的元组。unique结构将返回true值。
找出所有在2009年最多开设一次的课程
unique谓词在空集上计算出真值。
在不使用unique结构的情况下,等价的方式是
from子句中的子查询
SQL允许在from子句中使用子查询表达式,任何select-from-where表达式返回的结果都是关系,因而可以被插入到另一个select-from-where中任何关系可以出现的位置。
找出系统平均工资超过42000美元的那些系中的老师的平均工资
使用from子句中使用子查询的方式:
with子句
with子句提供定义临时关系的方法,这个定义只对包含with子句的查询有效。
找出最大预算值的系
with子句定义了临时关系max_budget,此关系在随后的查询中马上被使用了。with子句在SQL 1999中引入,现在很多的数据库都提供了支持。
我们也能用from子句或where子句中的嵌套子查询书写上述查询。with子句使查询在逻辑上更加清晰,允许在一个查询内的多个地方使用视图定义。
查出所有工资总额大于所有系平均总额的系
标量子查询
SQL允许子查询出现在返回单个值的表达式能够出现的任何地方。只要该子查询只返回包含单个属性的单个元组,这样的子查询称为标量子查询(scalar subquery)。
所有的系以及他们拥有的老师
数据库的修改
删除
删除请求的表达与查询非常的类似,我们只能删除整个元组,而不能只删除某些属性上的值。
其中表示一个谓词,代表一个关系。delete语句首先从r中找出所有的使为真的元组,然后把它们从中删除。如果省略where语句,则r中所有的元组被删除,但是关系依然存在。
下面是SQL删除请求的一些例子
从instructor关系中删除与Finance系教师相关的所有元组
删除所有工资在13000美元到15000美元之间的教师
从instructor关系中删除所有这样的教师元组,他们在Waston大楼的系工作
插入
待插入元组的数据值必须在相应属性的域中,同样,待插入元组的分量数也必须是正确的。
insert语句是的单个元组的插入请求。
插入Computer Science系的信息
SQL允许在insert语句中指定属性。
更新
update语句与使用insert、delete类似,代更新的元组可以用查询语句找到。
所有教师的工资将增长5%
上面的更新语句将在instructor关系中每个元组执行一次。
工资低于70000美元的教师涨工资
对于工资低于平均数的教师涨5%的工资
SQL提供case结构,我们可以使用它在一条update语句中执行前面的两种更新、避免更新次序引发的问题:
case语句的一般格式如下:
标量子查询在SQL更新语句中也非常的有用,我们可以用set子句中,我们把每个student元组的tot_cred属性值设为该该生成功学完的课程的总和。我们可以假设如果一个学生在某门课程的成绩既不是F,也不是空,那么他成功学完了这门课程。
注意子查询使用来update语句的相关变量S,如果一个学生没有成功学完任何课程,上述更新的语句将其tot_cred属性值为空,如果想要将这个属性的值设置为0,可以用另一条update语句来替换成0。将子查询中**select sum(credits)**子句替换成下面的语句