本文章的素材与知识来自李国良老师和王珊老师。
数据操纵语言DML(Data Manipulation Language)
SELECT
一.SELECT的语法与构成
1.语法
2.构成
二.投影
投影操作可以选择表中的若干列,主要体现在SELECT子句后的列表达式。
1.列表达式
2.列的重复行
(1)ALL(默认)
(2)DISTINCT
[ALL|DISTINCT] 是跟在SELECT的后面,只需要写一次就会针对所有元组做去重操作;而不是跟在每一列的前面,写多次。
错误写法:SELECT DISTINCT Sno , DISTINCT Sname FROM SC;
正确写法:SELECT DISTINCT Sno , Sname FROM SC;
3.列的别名
(1)给列起的别名唯一作用是:在结果展示的时候将列名换为别名;即使给列起了别名,在SQL语句中也无法使用,只能用列的原名。(与给表起别名要区分开)
4.示例1------查询全部
5.示例2------广义投影
可以对列执行计算
三.选择
选择操作可以筛选满足条件的元组,主要体现在WHERE子句后的查询条件。
1.WHERE子句常用的查询条件
2.比较
(1)对于不等运算符 "!=" 和 "<>" ,两者作用相同。不过有极少数数据库只支持"<>"而不支持"!="。
3.确定范围(BETWEEN AND)
(1)BETWEEN AND是包含首尾的。
4.确定集合(IN)
5.空值(IS NULL)
(1)"IS"不能用"="代替。只能是"IS NULL"和"IS NOT NULL",不可以"= NULL"和"!= NULL"。
6.逻辑运算(AND、OR、NOT)
7.字符串运算
(1)_ :表示一个任意字符。例如"_a"表示:以a结尾,大小为2个字符的任意字符串。
(2)% :表示任意多个(包括0个和1个)任意字符。例如"%a"表示:以a结尾,大小至少为1个字符的任意字符串。
(3)ESCAPE:用于指定转义标识符 ,例如"ESCAPE /"则指定"/"为转义标识符,转义标识符后面跟着的任意一个字符就是普通字符 ,不具有任何含义。常用于匹配串本身就含有"_"或"%"的情况。
(4)字符串类型数据在存储时需注意:
- char类型在插入时会保留首部的空格,自动删除尾部的所有空格。
- varchar类型在插入时会保留首部和尾部的所有空格。
(5)在字符串匹配上,"like"和"="的差异
- "like"支持模糊匹配,而"="只支持完全匹配。
- 对于字符串的完全匹配,使用"like"或"="都可以。
四.聚集
1.常见聚集函数
2.注意事项
(1)指定DISTINCT时,先消除重复取值的列,再用聚集函数作用。默认是ALL,即不去重。
(2)SUM()和AVG()只能作用在数值列;MAX()和MIN()作用在非数值列时按字典序排序。
3.示例
五.分组
分组操作使用GROUP BY实现,配合HAVING可以完成分组后的再筛选。
1.定义
2.聚集函数与GROUP BY
(1)不使用GROUP BY进行分组,则聚集函数作用于整个查询结果。
(2)使用GROUP BY进行分组,则聚集函数单独作用于每一个组。
(3)示例
3.HAVING
(1)HAVING子句用来对GROUP BY分组后的每一个组进行再筛选。
(2)示例
六.排序
1.定义
2.示例
七.连接
1.定义
(1)最常用的是等值连接和自然连接
(2)WHERE子句可以同时完成查询和连接
(3)连接查询一定要记得写连接条件。
(4)连接操作是基于笛卡尔积做条件筛选,不会自动对重复元组进行去重。
2.自身连接与表的重命名
(1)在一条SQL语句中给表起了别名,则在该条SQL语句中只能用表的别名,不能用原名。
(2)在一条SQL语句中给列起了别名,在该条SQL语句中还是只能用列的原名,不能用别名。
3.连接的三种写法
4.多种连接方式
八.嵌套查询
1.定义
(1)父查询与子查询
(2)子查询中不能使用ORDER BY子句:因为子查询的结果是用于父查询的,最终排序结果还是取决于父查询,因此子查询做ORDER BY是没有意义的。
(3)父查询对子查询的查询条件
2.不相关子查询
3.相关子查询
4.嵌套查询的类型1------带有IN谓词的子查询
(1)示例
(2)上述查询用连接查询也能实现,要记得把连接条件写全,三张表有两个连接条件
5.嵌套查询的类型2------带有比较运算符的子查询
6.嵌套查询的类型3------带有ANY或ALL谓词的子查询
(1)ANY
(2)上述例子也可以用聚集函数实现
(3)ALL
7.嵌套查询的类型4------带有EXISTS谓词的子查询
(1)定义
(2)示例
(3)用EXIST实现全称量词
(4)用EXIST实现逻辑蕴涵
8.嵌套查询与连接查询的关系
(1)使用嵌套查询能实现的,连接查询也能实现。
(2)嵌套查询的系统效率高于连接查询:因为连接查询一定会对连接的两个或多个表在内存中做笛卡尔积,占据系统内存;而嵌套查询是逐层进行的,每次只会对一个表进行操作,不会产生笛卡尔积。
九.集合操作
十.WHERE、聚集函数、GROUP BY、HAVING的关系
1.WHERE和聚集函数
(1)在WHERE子句中不能使用聚集函数:聚集函数是作用于WHERE子句的查询结果的,而WHERE子句在执行时还没有得到结果,因此不能使用聚集函数。
2.GROUP BY和HAVING
(1)有HAVING则一定要有GROUP BY:HAVING是对GROUP BY的分组结果进行筛选,若没有GROUP BY分组,则HAVING就没有作用对象。
(2)有GROUP BY不一定要有HAVING:可以只对结果进行GROUP BY分组,不对分组结果进行再筛选。
3.WHERE和GROUP BY
(1)WHERE和GROUP BY的使用并不冲突:WHERE是对查询做条件筛选,GROUP BY是对查询结果做分组。
4.WHERE和HAVING
WHERE和HAVING都是对查询结果做条件筛选,但两者作用不同:
(1)作用对象不同:
- WHERE子句作用于表或者视图,面对表或视图的所有元组。
- HAVING子句作用于GROUP BY的分组结果,面向每一个分组的所有元组。
(2)执行顺序不同:
- WHERE是对查询做初次筛选,最先执行。
- HAVING是对分组结果做二次筛选,在WHERE、GROUP BY之后执行。
(3)使用条件不同:
- WHERE没有使用条件。
- HAVING必须要伴随GROUP BY使用。
(4)对于聚集函数的使用不同:
- WHERE子句不允许使用聚集函数。
- HAVING子句允许使用聚集函数。
5.聚集函数和GROUP BY
(1)如果未对查询结果进行GROUP BY分组,则聚集函数作用于整个查询结果。
(2)如果对查询结果进行GROUP BY分组,则聚集函数单独作用于每一个分组。
6.聚集函数的出现位置及其对GROUP BY的依赖
(1)聚集函数允许出现的位置:SELECT子句、HAVING子句、ORDER BY子句。
(2)如果SELECT子句中只有聚集函数,则可以不使用GROUP BY分组。
(3)如果SELECT子句中既有聚集函数,也有非聚集函数,则必须使用GROUP BY分组,且GROUP BY分组的列必须包含SELECT中的非聚集函数列。原因看第7点。
7."对于使用了GROUP BY分组的查询语句,所有在SELECT子句中出现的列,要么出现在SELECT子句的聚集函数中,要么还必须同时出现在GROUP BY子句中;不能只在SELECT子句中单独出现"
(1)例子:
假设查询语句: SELECT sno,grade FROM sc GROUP BY sno;
该语句会查询sc表中的sno列和grade列,并将查询结果按sno值进行分组,sno值相同的元组会被合并到一组。
而sc表中会出现一个sno对应多个grade的情况,即有若干列都是sno相同但grade不同。
那些sno相同的列随着GROUP BY sno被合并到一个元组后,不同的grade也会被挤入一个单元格内,就违背了1NF。
(2)理解:
GROUP BY其实就是把多个元组合并为一个元组,随着合并必然会出现多个不同值挤入一个单元格的情况,为了不违背1NF,有三种解决方案:
- 不将这些违背1NF的列投影出来,即不出现在SELECT子句中。
- 通过聚集函数将一个单元格的多个值合并为一个值,即出现在SELECT子句的聚集函数中。
- 对这一列的每一个值分一个组,即出现在GROUP BY子句中。
(3)示例
8.执行顺序
WHERE > GROUP BY > 聚集函数 > HAVING > ORDER BY。