《数据库理论》 低端学习笔记(二)
3.6 空值
如果算术表达式的任一输入为空则该表达式的值也为空。
对于比较问题,创造出了地三个逻辑值:unknow。
SQL将任何的有关空值的计算结果视为unknow。
对于逻辑运算中的unknow。有如下规则:
and : true and unknow = unknow; false and unknow = false ; unknow and unknow = unknow 。
or :true or unknow = true ; false or unknow = unknow ; unknow or unknow = unknow 。
not :not unknow = unknow。
某些的SQL实现还允许我门用子句 is unknow 和 not unknow ;来测试一个表达式的结果是否为unknow。
当一个查询语句写为 select distinct 时,重复的元组将被去除。为了达到这个目的比较两个元组的属性时,如果两个值非空且值相等后者两个都是空值,那么他们是相同的。
如果元组在所有属性上的取值都是相等的那么它们就被当作相同的元组,即使某些值为空。上述方式还适用于集合运算。
3.7 聚合函数
聚合函数以一个或者多个作为输入返回一个值。SQL提供了五个固有的聚合函数:
平均值 : avg
最小值 : min
最大值 : max
总和 : sum
计数 : count
sum和avg的输入必须是数字集。其他的可以应用在非数字集上。
如果我们想找出某一系的教师的平均工资(之前给出的数据库定义的含义是一所大学的数据库)。那么书写如下
1 select avg (salary) as avg_salary 2 from instructor 3 where dept_name = 'Comp.Sci';
这里的我们还把新的属性命名为 avg_salary 如果不进行手动命名。那么 SQL会加一个任意名称给它。
在某些情况下我们希望在计算聚合函数时能去除重复的分组。这时我么可以在聚合表达式中使用distinct。如
select count(distinct ID) from teaches where semest = 'Spring' and year = '2010';
我们可以用count(*)放在select子句中来统计from子句中所提到关系的元组数。但是SQL 不允许在count(*) 中使用distinct,在min何max 中使用distinct时合法的尽管并无差别。我们可以使用all代替distinct来保留重复的元组,但是all时默认的所以一般没有必要这么做。
group by 子句中给出的一个或者多个属性用来构造分组。在group by 子句的所有属性上取值相同的元组将被分到一个组中,如果省略group by子句那么整个关系将被视为一个组
注意:
在SQL查询并进行分组时一个很重要的事情就是保证出现在select子句中的当没有被聚合的属性一定要出现在group by子句中。换而言之就是,select中没有聚合的属性只能来自group by子句中。
有时候,对分组的限定条件比对元组的限定条件更好用。例如我们只对教师的平均工资超过40000美元的系感兴趣,这里的条件不是针对单个元组的,而是正对group by 子句构成的分组,为了进行这样的查询我们有了 having 子句。having子句中的谓词在形成分组后才起作用。因此可以使用聚合函数 如
select dept_name , avg (salary) as ave_salary from instructor group by dept_name having avg (salary) > 40000;
与select子句类似 任何出现在having子句中但没有被聚合的属性必须处想在group by子句中,否则查询就被当成时错误的。
包含聚合、group by、having子句的查询含义可以通过下述的操作顺序来定义:
- 与不带聚集查询的情况类似,最先根据from 子句来计算出一个关系。
- 如果出现where子句。where子句中的谓词将应用到from子句的结果关系上。
- 如果出现了group by子句,where谓词的元组将被group by子句分组。如果没有group by子句,满足where子句的元组将被分为一个分组。
- 如果处想了having子句,它将应用到每个分组上;不满足的分组将被抛弃。
- select子句利用剩下的分组产生出查询结果的元组,即在每个分组上应用聚集函数来得到单个结果元组
空值在聚集运算的处理上带来了麻烦如下面的代码
select sum (salary) from instructor ;
由于一些运足在salary上取空值,而SQL并不认为总和应该时null,而是认为sum以应该忽略空值。
聚集函数中 出了count(*) 以外的所有聚集函数都应该忽略输入集合的空值,。由于空值被忽略,有可能造成输入集合为空集。规定count的空集运算值为0.其他情况所有的聚集函数在输入空集时返回一个空值。
在SQL:1999中引入了布尔(boolean)数据类型,它可以取true、false、unknow三个值。有两个聚集函数:some 和 every,用来处理布尔值的集合。
3.8 嵌套子查询
where子句中的子查询
SQL 允许测试元组在关系中的资格。连接词in 测试元组是否是集合中的成员,集合是由select子句产生的一组值构成的。连接词 not in 测试元组是否不是集合中的成员。
in 和 not in 同样适合枚举集合 如
select distinct name from instructor where name not in ('Mozart' , 'Einstein');/*枚举型*/
查询出了名字不是Mozart 和 Einstein的教师的姓名。
SQL还提供了 “>some”来表示 至少比某一个要大 ,它可以房放在 in 和 not in 的相同位置 ,来进行查询嵌套。同时也提供了 <some 、 <=some 、 >=some 、 =some 和 <>some 。其中 =some 和 in相同,但是 <>some 却并不同于 not in 。
类似some SQL还提供了 all在这种关系中来表示所有(吐糟下 为甚不是every),于是就有了 <all 之类的关键字。其中的 <>all 等价于 not in,但是 =all 不等价于 in 。
SQL还提供一个测试集合是否存在元组的测试。exists结构在作为参数的子查询非空时返回true值。
相关子查询:
在包含了子查询的查询中在相关名称上可以应用作用域规则。
我们可以用 not exists 结构来查询结果中是否不存在元组。
这个略难理解 我贴个其他人的博客大家可以去看下,有助于理解exists 对于初学者 也有助于理解查询语句的运行机制:
http://www.cnblogs.com/netserver/archive/2008/12/25/1362615.html
我的理解就是,先进行内部查询,得出内部查询的结果,之后外部查询满足可from和之前的where谓词的元组 a 被exists结构带到内部查询中,如果内部查询的结果中有和a 相同的元组,就返回true 这样使的外部的select语句正常打印出这个元组的一部分,如果内查询的结果中没有和a 相同的元组,就返回false 这样就阻止了外部查询的select子句。这样貌似和 in 结构使相同的。 但是两者在不同情况下的执行效率不同,根据上面引用的文章来看,是:“IN适合于外表大而内表小的情况;EXISTS适合于外表小而内表大的情况。”。
SQL提供一个布尔函数用于测试一个子查询中的结果中是否存在重复元组。如果作为参数的子查询结果中没有重复的元组,unique结构将返回true值。同样的这个也好难理解,我是这样理解,在内查询中得出了结果后 unique结构会把外查询中满足了from和之前的where谓词的元组a依次的带入内查询中,如果发现a元组在内查询中没有相同的元组,或者a 在内查询中有相同的元组而且在内查询的结果中只出现一次,就返回true。这样select语句就可以把这个元组按照要求打印(原谅我用打印这个词吧,我是想象成打印在控制台上了)出其中的一部分。而如果在内查询的结果中和a元组相同的元组个数不止一个(select 的结果中默认是 all),那么就返回false ,就阻止了外查询的select子句处理这个元组。
exists 也称为空关系测试 同时提供了 not exists 来测试是否不存在元组。
unique 也称为重复关系测试,而且尚未广泛实现。同样提供了 not unique 。而对一个关系中unique测试结果为false的定义是,当且仅当关系中存在着两个元组t1和t2 ,且t1 = t2 。由于t1 或 t2 的某个域为空时,判断t1 = t2 为假,所以尽管一个元组有多个副本,只要该元组中有一个属性为空,unique测试就可能为真(注意可能)。
SQL 语句允许子查询放在任何返回单个值的表达式能出现的任何位置,只要这个子查询返回的是包含单个属性的单个元组。
如果子查询有可能返回多个元组,或者多个属性的元组,那么在编译的时候不会出现错误,但是在运行是会产生错误。