重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
在上一文章 《 【技术干货】作为测试,你必须知道的MySQL知识(一) 》 中我们已经学了MySQL基础知识。对于 最常用、最重要 的select语句将会在本文与大家分享,带你轻松搞定查询语句。
成都创新互联公司专注于西藏网站建设服务及定制,我们拥有丰富的企业做网站经验。 热诚为您提供西藏营销型网站建设,西藏网站制作、西藏网页设计、西藏网站官网定制、微信小程序服务,打造西藏网络公司原创品牌,更为您提供西藏网站排名全网营销落地服务。
学习select之前,需先知道查询语句 逻辑执行顺序 ,了解了逻辑执行顺序,对我们写select语句有帮助,但是需要注意的是,逻辑顺序并不是真正执行的顺序,因为MySQL 优化器 会将SQL子句按最优的路径执行。如果想要查看实际执行顺序,可使用Explain关键字进行分析,获取对应的执行计划。
查询语句的使用都离不开以下的格式:
需要记住,这是学好查询语句的基础~
以上select的逻辑执行顺序如下(前面的序号):
查询语句中where后面支持多种运算符,进行条件的处理,其中常见运算符包括:比较运算、逻辑运算、模糊查询、范围查询、空判断。
① 等于: =
② 大于:
③ 大于等于: =
④ 小于:
⑤ 小于等于: =
⑥ 不等于: != 或
① and
② or
③ not
【模糊查询语法】:字段名 like 匹配字段字符串
【常见的匹配字段字符串】:%、_、[] 、[^]
① in:表示在一个非连续的范围内
② between ... and ...:表示在一个连续的范围内
① 判断为空:is null
② 判断非空:is not null
【注意】:null与''是不同的。
使用JOIN 来联合多表查询,JOIN 按照功能大致分为如下三类:
① INNER JOIN(内连接) : 获取两个表中字段匹配关系的记录。
② LEFT JOIN(左连接) : 获取左表所有记录,即使右表没有对应匹配的记录。
③ RIGHT JOIN(右连接) : 与 LEFT JOIN 相反,用于获取右表所有记录,即使左表没有对应匹配的记录。
-【举个栗子】:对表t_user与表t_order进行内连接
-【举个栗子】:对表t_user与表t_order进行左连接
-【举个栗子】:对表t_user与表t_order进行右连接
① 内连接: 保留两表中交集的记录;
② 左连接: 左表全部保留,右表关联不上用null表示;
③ 右连接: 右表全部保留,左表关联不上的用null表示。
group by语句是根据一个或多个列对结果集进行分组。在分组的列上我们经常会使用到 聚合函数 ,因此,先了解 常用的聚合函数 :
① sum():求和
② avg():求平均数
③ count():统计记录条数
④ max():求最大值
⑤ min():求最小值
【group by语法】:
可以根据某个字段(column_name)进行分组。如果不指定分组字段,默认将所有记录作为一组。
-【举个栗子】: 在emp表中,有3个字段,一个是员工id(id),一个是部门id(dept_id),一个是员工薪水(salary)
【注意】:
① 使用group by的select语句中的字段,应该 出现在 聚合函数 中,或者 出现在 group by 中;
② having 子句可以使用聚合函数,而 where 子句不能使用。
③从逻辑执行过程可知: where 是没有分组前对所有数据进行过滤, having 是对分组后的数据进行过滤。
子查询是指一个查询语句嵌套在另一个查询语句内部的查询。
-【举个栗子】: 一张user表,有2个字段,一个是名称(name),一个是年龄(age),查询出比小李年龄高的人有哪些?
- >【举个栗子】: 获取员工工资低于所在部门的平均工资的员工信息
要对某个字段进行降序,可以在查询语句后可使用:order by 排序的字段名 desc
要对某个字段进行升序,可以在查询语句后可使用:order by 排序的字段名 asc
【注意】:order by默认情况下是升序,asc可省略不写。
有2个维度的排序时,使用逗号隔开2个排序,优先使用前者进行排序,再使用后者进行排序。
limit:限制查询数据条数,经常在分页的场景中应用。LIMIT子句接受一个或两个参数。参数值都必须为零或正整数。limit的使用格式:
【参数解释】:
limit N : 返回 N 条记录;
offset M : 跳过 M 条记录, 默认 M=0;
limit N,M : 从第 N 条记录开始, 返回 M 条记录。
-【举个栗子】:
【注意】:limit的两个参数中:
① 第1个参数:指要返回的第一行的偏移量。第一行的偏移量为0,而不是1;
② 第2个参数:指要返回的记录行数。
如上面"检索6-15行记录"例子中,第1个参数为5,其实指的是第6行;第2个参数为10,指的是从第6行开始,取10条记录,所以是获取到6-15的记录。
当需要查询出某个字段不重复的记录时,可以使用distinct来过滤重复的记录,格式如下:
使用distinct可以进行单字段去重、多字段去重、所有字段去重、以及与聚合函数的综合使用。
--【举个栗子】: student表中有name、age、weight3个字段字段。
使用distinct之后,只能返回去重的字段,而不能返回其他的字段,可以使用group by进行去重查询,
【注意】:
① distinct 只能在select语句中使用;
② distinct 必须在所有字段的最前面;
③ 多个字段去重时,多个字段组合后一样时才会被去重;
④ 列中有null值,当对该列使用distinct子句,将保留一个null值(所有null值被视为相同的值)。
只要字段值还可以继续拆分,就不满足第一范式。
范式设计得越详细,对某些实际操作可能会更好,但并非都有好处,需要对项目的实际情况进行设定。
在满足第一范式的前提下,其他列都必须完全依赖于主键列。 如果出现不完全依赖,只可能发生在联合主键的情况下:
实际上,在这张订单表中,product_name 只依赖于 product_id ,customer_name 只依赖于 customer_id。也就是说,product_name 和 customer_id 是没用关系的,customer_name 和 product_id 也是没有关系的。
这就不满足第二范式:其他列都必须完全依赖于主键列!
拆分之后,myorder 表中的 product_id 和 customer_id 完全依赖于 order_id 主键,而 product 和 customer 表中的其他字段又完全依赖于主键。满足了第二范式的设计!
在满足第二范式的前提下,除了主键列之外,其他列之间不能有传递依赖关系。
表中的 customer_phone 有可能依赖于 order_id 、 customer_id 两列,也就不满足了第三范式的设计:其他列之间不能有传递依赖关系。
修改后就不存在其他列之间的传递依赖关系,其他列都只依赖于主键列,满足了第三范式的设计!
查询每门课的平均成绩。
查询 score 表中至少有 2 名学生选修,并以 3 开头的课程的平均分数。
分析表发现,至少有 2 名学生选修的课程是 3-105 、3-245 、6-166 ,以 3 开头的课程是 3-105 、3-245。也就是说,我们要查询所有 3-105 和 3-245 的 degree 平均分。
查询所有学生的 name,以及该学生在 score 表中对应的 c_no 和 degree 。
通过分析可以发现,只要把 score 表中的 s_no 字段值替换成 student 表中对应的 name 字段值就可以了,如何做呢?
查询所有学生的 no 、课程名称 ( course 表中的 name ) 和成绩 ( score 表中的 degree ) 列。
只有 score 关联学生的 no ,因此只要查询 score 表,就能找出所有和学生相关的 no 和 degree :
然后查询 course 表:
只要把 score 表中的 c_no 替换成 course 表中对应的 name 字段值就可以了。
查询所有学生的 name 、课程名 ( course 表中的 name ) 和 degree 。
只有 score 表中关联学生的学号和课堂号,我们只要围绕着 score 这张表查询就好了。
只要把 s_no 和 c_no 替换成 student 和 srouse 表中对应的 name 字段值就好了。
首先把 s_no 替换成 student 表中的 name 字段:
再把 c_no 替换成 course 表中的 name 字段:
查询 95031 班学生每门课程的平均成绩。
在 score 表中根据 student 表的学生编号筛选出学生的课堂号和成绩:
这时只要将 c_no 分组一下就能得出 95031 班学生每门课的平均成绩:
查询在 3-105 课程中,所有成绩高于 109 号同学的记录。
首先筛选出课堂号为 3-105 ,在找出所有成绩高于 109 号同学的的行。
查询所有成绩高于 109 号同学的 3-105 课程成绩记录。
查询所有和 101 、108 号学生同年出生的 no 、name 、birthday 列。
查询 '张旭' 教师任课的学生成绩表。
首先找到教师编号:
通过 sourse 表找到该教师课程号:
通过筛选出的课程号查询成绩表:
查询某选修课程多于5个同学的教师姓名。
首先在 teacher 表中,根据 no 字段来判断该教师的同一门课程是否有至少5名学员选修:
查看和教师编号有有关的表的信息:
我们已经找到和教师编号有关的字段就在 course 表中,但是还无法知道哪门课程至少有5名学生选修,所以还需要根据 score 表来查询:
根据筛选出来的课程号,找出在某课程中,拥有至少5名学员的教师编号:
在 teacher 表中,根据筛选出来的教师编号找到教师姓名:
查询 “计算机系” 课程的成绩表。
思路是,先找出 course 表中所有 计算机系 课程的编号,然后根据这个编号查询 score 表。
查询 计算机系 与 电子工程系 中的不同职称的教师。
查询课程 3-105 且成绩 至少 高于 3-245 的 score 表。
查询课程 3-105 且成绩高于 3-245 的 score 表。
查询某课程成绩比该课程平均成绩低的 score 表。
查询所有任课 ( 在 course 表里有课程 ) 教师的 name 和 department 。
查询 student 表中至少有 2 名男生的 class 。
查询 student 表中不姓 "王" 的同学记录。
查询 student 表中每个学生的姓名和年龄。
查询 student 表中最大和最小的 birthday 值。
以 class 和 birthday 从大到小的顺序查询 student 表。
查询 "男" 教师及其所上的课程。
查询最高分同学的 score 表。
查询和 "李军" 同性别的所有同学 name 。
查询和 "李军" 同性别且同班的同学 name 。
查询所有选修 "计算机导论" 课程的 "男" 同学成绩表。
需要的 "计算机导论" 和性别为 "男" 的编号可以在 course 和 student 表中找到。
建立一个 grade 表代表学生的成绩等级,并插入数据:
查询所有学生的 s_no 、c_no 和 grade 列。
思路是,使用区间 ( BETWEEN ) 查询,判断学生的成绩 ( degree ) 在 grade 表的 low 和 upp 之间。
准备用于测试连接查询的数据:
分析两张表发现,person 表并没有为 cardId 字段设置一个在 card 表中对应的 id 外键。如果设置了的话,person 中 cardId 字段值为 6 的行就插不进去,因为该 cardId 值在 card 表中并没有。
要查询这两张表中有关系的数据,可以使用 INNER JOIN ( 内连接 ) 将它们连接在一起。
完整显示左边的表 ( person ) ,右边的表如果符合条件就显示,不符合则补 NULL 。
完整显示右边的表 ( card ) ,左边的表如果符合条件就显示,不符合则补 NULL 。
完整显示两张表的全部数据。
在 MySQL 中,事务其实是一个最小的不可分割的工作单元。事务能够 保证一个业务的完整性 。
比如我们的银行转账:
在实际项目中,假设只有一条 SQL 语句执行成功,而另外一条执行失败了,就会出现数据前后不一致。
因此,在执行多条有关联 SQL 语句时, 事务 可能会要求这些 SQL 语句要么同时执行成功,要么就都执行失败。
在 MySQL 中,事务的 自动提交 状态默认是开启的。
自动提交的作用 :当我们执行一条 SQL 语句的时候,其产生的效果就会立即体现出来,且不能 回滚 。
什么是回滚?举个例子:
可以看到,在执行插入语句后数据立刻生效,原因是 MySQL 中的事务自动将它 提交 到了数据库中。那么所谓 回滚 的意思就是,撤销执行过的所有 SQL 语句,使其回滚到 最后一次提交 数据时的状态。
在 MySQL 中使用 ROLLBACK 执行回滚:
由于所有执行过的 SQL 语句都已经被提交过了,所以数据并没有发生回滚。那如何让数据可以发生回滚?
将自动提交关闭后,测试数据回滚:
那如何将虚拟的数据真正提交到数据库中?使用 COMMIT :
事务的实际应用 ,让我们再回到银行转账项目:
这时假设在转账时发生了意外,就可以使用 ROLLBACK 回滚到最后一次提交的状态:
这时我们又回到了发生意外之前的状态,也就是说,事务给我们提供了一个可以反悔的机会。假设数据没有发生意外,这时可以手动将数据真正提交到数据表中:COMMIT 。
事务的默认提交被开启 ( @@AUTOCOMMIT = 1 ) 后,此时就不能使用事务回滚了。但是我们还可以手动开启一个事务处理事件,使其可以发生回滚:
仍然使用 COMMIT 提交数据,提交后无法再发生本次事务的回滚。
事务的四大特征:
事务的隔离性可分为四种 ( 性能从低到高 ) :
查看当前数据库的默认隔离级别:
修改隔离级别:
测试 READ UNCOMMITTED ( 读取未提交 ) 的隔离性:
由于小明的转账是在新开启的事务上进行操作的,而该操作的结果是可以被其他事务(另一方的淘宝店)看见的,因此淘宝店的查询结果是正确的,淘宝店确认到账。但就在这时,如果小明在它所处的事务上又执行了 ROLLBACK 命令,会发生什么?
这就是所谓的 脏读 ,一个事务读取到另外一个事务还未提交的数据。这在实际开发中是不允许出现的。
把隔离级别设置为 READ COMMITTED :
这样,再有新的事务连接进来时,它们就只能查询到已经提交过的事务数据了。但是对于当前事务来说,它们看到的还是未提交的数据,例如:
但是这样还有问题,那就是假设一个事务在操作数据时,其他事务干扰了这个事务的数据。例如:
虽然 READ COMMITTED 让我们只能读取到其他事务已经提交的数据,但还是会出现问题,就是 在读取同一个表的数据时,可能会发生前后不一致的情况。* 这被称为* 不可重复读现象 ( READ COMMITTED ) 。
将隔离级别设置为 REPEATABLE READ ( 可被重复读取 ) :
测试 REPEATABLE READ ,假设在两个不同的连接上分别执行 START TRANSACTION :
当前事务开启后,没提交之前,查询不到,提交后可以被查询到。但是,在提交之前其他事务被开启了,那么在这条事务线上,就不会查询到当前有操作事务的连接。相当于开辟出一条单独的线程。
无论小张是否执行过 COMMIT ,在小王这边,都不会查询到小张的事务记录,而是只会查询到自己所处事务的记录:
这是 因为小王在此之前开启了一个新的事务 ( START TRANSACTION ) * ,那么* 在他的这条新事务的线上,跟其他事务是没有联系的 ,也就是说,此时如果其他事务正在操作数据,它是不知道的。
然而事实是,在真实的数据表中,小张已经插入了一条数据。但是小王此时并不知道,也插入了同一条数据,会发生什么呢?
报错了,操作被告知已存在主键为 6 的字段。这种现象也被称为 幻读,一个事务提交的数据,不能被其他事务读取到 。
顾名思义,就是所有事务的 写入操作 全都是串行化的。什么意思?把隔离级别修改成 SERIALIZABLE :
还是拿小张和小王来举例:
此时会发生什么呢?由于现在的隔离级别是 SERIALIZABLE ( 串行化 ) ,串行化的意思就是:假设把所有的事务都放在一个串行的队列中,那么所有的事务都会按照 固定顺序执行 ,执行完一个事务后再继续执行下一个事务的 写入操作 ( 这意味着队列中同时只能执行一个事务的写入操作 ) 。
根据这个解释,小王在插入数据时,会出现等待状态,直到小张执行 COMMIT 结束它所处的事务,或者出现等待超时。
转载:
在第六章介绍的所有 WHERE 子句在过滤时,使用的都是单一的条件。为了进行更强的过滤控制,MySQL允许给出多个WHERE子句,并通过 AND 或 OR 操作符的方式组合使用。
AND 操作符可以使用对多列的条件进行累加过滤。
OR操作符可以使用对多列的条件进行累加过滤。
WHERE 子句可包含任意数目的 AND 和 OR 操作符。但是 AND 的优先级比 OR 高,应使用适当的圆括号分组操作符。
注意: 任何时候都不应该过分依赖默认计算次序,使用圆括号可以增加可读性,消除歧义。
IN 操作符用来指定条件范围,范围中每个条件都可以进行匹配。 IN 的合法值由逗号分隔,并全部括在圆括号中。
使用 IN 的优点为:
WHERE子句中的NOT操作符有且只有一个功能,就是否定它之后的任何条件。
注意: MySQL支持使用NOT对IN、BETWEEN和EXISTS子句取反,这与其他大多数DBMS允许使用NOT对各种条件取反有很大区别。
之前介绍的都是针对已知值的过滤。而对进行未知内容的匹配,可以使用通配符创建比较数据的搜索模式来完成。通配符的匹配,需要通过LIKE操作符指示MySQL完成。
% 表示任何字符出现任意次。例如,为了找到所有以 jet 起头的产品,可使用以下 SELECT 语句
注意:
下划线( _ )通配符只匹配单个字符而不是多个字符。
通配符使用技巧:
正则表达式是用来匹配文本的特殊串(字符集合),所有种类的程序设计语言、文本编辑器、操作系统等都支持正则表达式。
MySQL中的正则表达式仅是正则表达式的一个子集。
为搜索两个串之一,使用( | )。
如果想匹配特定的单一字符,可以使用[]将一组指定的内容括起来。
注意:
集合可用来定义要匹配一个或多个字符,此时可以使用 - 来定义一个范围,例如 [0-9] 表示数字0到9, [a-z] 表示字母a到z。
正则表达式语句由具有特定含义的特殊字符构成。例如 | 或 - 等。如果要表示一些特殊的字母,如含 . 的值,则应该使用 \\\ 为前导。 \\\- 表示查找 - , \\\. 表示查找 . 。
注意:
为方便常见字符集的查找,可以使用预定义的字符集。称为字符类。如下图所示:
目前为止使用的所有正则表达式都试图匹配单词出现。但有时需要对匹配的数目进行更强的控制,此时可以使用下列正则表达式重复元字符来完成。
注意: 使用正则表达式时,编写某个特定的表达式几乎总是有多种方法。
目前为止,所有例子都是匹配一个串中任意位置的文本,为了匹配特定位置的文本,需要使用定位符。
注意: