前言
在这个数据如同空气般渗透工作与生活的时代,SQL(结构化查询语言) 早已不是程序员的 “专属技能”—— 市场分析师要从报表里挖掘商业规律,运营要统计用户行为的蛛丝马迹,甚至职场新人想快速从海量数据中捞取有效信息,都离不开和 SQL “对话”。
可现实里,太多人卡在了 “入门即放弃” 的尴尬阶段:
想往表里插一条数据,却怕一个手抖把整张表搞 “乱套”;写查询语句时,WHERE子句、排序、分页总是 “配合不默契”,结果要么多到看不过来,要么少到啥也没拿到;面对 “分组聚合”“多表关联”“子查询” 这些 “进阶操作”,更是觉得像在看 “天书”—— 明明每个单词都认识,组合起来却完全不知道该怎么用……
如果这些困惑你也感同身受,那这篇文章就是为你准备的 “SQL 实战通关指南”。
我们会沿着 “插入数据→基础查询→分组聚合→复合查询→表连接→数据更新 / 删除” 的清晰脉络(就像你看到的目录那样),把 SQL 里每个看似复杂的知识点,拆解成 “一看就懂、一学就会、一用就对” 的实操步骤。不管你是 0 基础想系统入门,还是有经验但想查漏补缺、突破瓶颈,都能在这套体系里,找到从 “SQL 小白” 到 “数据操控高手” 的成长路径。
现在,就让我们一起敲开 SQL 的大门,让那些曾经让你头疼的数据,乖乖变成你想要的答案吧~
一. Create插入数据
1.1 插入数据
插入语法:insert (into) 表名(要进行插入的列) values(插入的数据,与前面指定的列要一一对应).
- 当要进行全列插入的时候,也可以不进行指定列,否则必须明确要插入那些列的数据:
在进行插入的时候,不仅仅可以进行单行数据的插入,也支持多行数据插入,即在values后搭配多组数据进行使用,每组数据用()进行标识,使用空格进行分割开。
1.2 替换数据
数据已经插入了,此时我们希望对已经插入的数据进行修改。
数据替换的方式有两种,下面分别进行介绍:
- 语法:
insert (into) 表名(要进行插入的列) values(插入的数据,与前面指定的列要一一对应) on duplicate key update 进行更新的数据。
此方法前面部分与插入操作是一样的,只不过在后面添加了on duplicate 主键/唯一键 update通过指定主键或唯一键,对主键/唯一键重复的数据进行修改。
- 语法:
replace (into) 表名(要进行插入的列) values(插入的数据,与前面指定的列要一一对应),这种方法,有两种情况:(1)没有主键/唯一键冲突,直接进行插入;(2)有主键/唯一键冲突,先将原数据删除再进行插入:
replace into test(id , name , gender) values(10001 , 'Jack' , 'male');
1.3 拷贝其他表
有时我们希望创建一个与一个已经存在的表结构一样的表,此时就可以使用拷贝进行建表。
语法:create table 表名 like 要进行拷贝的表。
二. Retrieve简单查询
关于查询是MySQL中最为常用的一个操作,也是操作最多的一个板块,下面将对各种查询方式进行详细的介绍。
2.1 查询基础语法
- 语法:`select 查询的列名称/表达式 from 表名*。
通过select来对表中的数据进行查询,指定出要进行查询的列名称/表达式,以及哪一个表中进行查询:
如下图中,我们可以对指定的列进行查询,也可以对一些列进行运算后再进行查询。
-
上图中的
*标识全列查询;
对于数据量很大的表,一般是不建议进行全列查询的:因为查询的列越多,意味着需要的传输数据量就越大,数据在网络中存传输的时间久,并且数据量很大我们在进行查看的时候也很不方便。 -
补充:在进行筛选的时候有时候我们希望进行去重操作,可以在select后面加上
distinct来实现。
因此对于大文本数据,我们建议进行筛选查找,下面将详细介绍一些筛选的方法。
2.2 where子句
-
where子句:增加筛选条件,决定从表中拿出那些行。
where子句中有很多种判断条件,这个条件与编程语言中的if语句类似,下面进行一个详细介绍:
比较运算符:
| 运算符 | 说明 |
|---|---|
| >, >=, <, <= | 大于,大于等于,小于,小于等于 |
| = | 等于,NULL 不安全,例如 NULL = NULL 的结果是 NULL
|
| <=> | 等于,NULL 安全,例如 NULL <=> NULL 的结果是 TRUE(1)
|
| !=, <> | 不等于 |
| between a0 and a1 | 范围匹配,[a0, a1],如果 a0 <= value <= a1,返回 TRUE(1)
|
| in (option, …) | 如果是 option 中的任意一个,返回 TRUE(1)
|
| is null | 是 NULL
|
| is not null | 不是 NULL
|
| like | 模糊匹配。% 表示任意多个(包括 0 个)任意字符;_ 表示任意一个字符 |
有几个地方是需要特殊注意的:
- 在
MySQL中等于就是=,而不是==; -
<=>与<>的区别在于:<>能够对空NULL进行比较,而<=>不能;
逻辑运算符:
| 运算符 | 说明 |
|---|---|
| and | 多个条件必须都为 TRUE (1),结果才是 TRUE (1) |
| or | 任意一个条件为 TRUE (1),结果为 TRUE (1) |
| not | 条件为 TRUE (1),结果为 FALSE (0) |
| 关于逻辑运算符,其使用规则与编程语言中的一样,不再过多赘述了。 |
2.3 结果排序
- 在
MySQL中有时候,我们希望对筛选出来的数据进行排序,此时就可以使用order by 列名字/表达式进行排序。
其中asc表示对结果进行升序排列,而desc表示对结果进行降序排列。
例如:以下对60分以上的英语成绩进行升序排列:
- 在使用
select时,我们经常会使用表达式来进行筛选,但是如果直接使用表达式进行输出,其表示的意思并不直观,比如上面的math+chinese+english,我们更希望为其起一个别名。 - 在
MySQL中,我们可以在一个表达式后面使用as 别名来进行起别名操作。
比如,我们想要输出表中总成绩>200分的成员姓名,以及总成绩:
在上述操作中,我们确实将所有成员的总成绩进行了起别名操作,但是在where中我并没有使用别名进行比较,这里能使用别名吗???
答案是不能的,这就与select的执行顺序有关系了,下面介绍以下select的执行顺序,
以select name , math+chinese+english as total from exam_result where math+chinese+english > 200为例:
- 先通过
from exam_result确定要进行筛选的表; - 在
MySQL中每一条数据是按行进行存放的,所以将数据从数据库磁盘加载到内存上时,就应该先进行判断,该行时候满足条件,如果不满足迅速释放,防止其占用内存空间;因此此时要先进行where math+chinese+english行筛选,而此时并没有进行重命名操作,因此不能使用别名; - 最后再将加载到内存中满足条件的行进行列筛选。
如果要再对上面的数据,进行排序,请问:在order by中能否使用别名???
答案是可以的,因为order by是对已经处理好的最终数据进行排序的,在这之前就已经定义了别名。
2.4 筛选分页结果
在进行筛选的时候,有时我们并不需要整张表的数据,而是需要一些特定范围或行数的数据,比如我们希望知道一个成绩单中第一名的信息,此时将整个表打印出来旧完全没有必要了。
语法:
-
limit n: n表示要筛选的行数; -
limit a , b:进行范围筛选,筛选出从a开始步长为b的数据,即[a , a + b]行的数据,注意a的起始下标是0.、
2.5 总结select顺序
在where子句部分我们谈到关于select中进行筛选的顺序问题,此处对select中筛选顺序做一个总结:
- 执行
from 表名,筛选确定使用哪一个表; - 指定
where子句,筛选出满足条件的行; -
select筛选出要进行显示的列; -
order by对准备好的数据进行排序; -
limit对要进行显示的数据进行筛选。
三. 分组聚合
3.1 聚合函数
以下是所有常见的聚合函数:
| 函数 | 说明 |
|---|---|
| count([distinct] expr) | 返回查询到的数据的数量 |
| sum([distinct] expr) | 返回查询到的数据的总和,不是数字没有意义 |
| avg([distinct] expr) | 返回查询到的数据的平均值,不是数字没有意义 |
| max([distinct] expr) | 返回查询到的数据的最大值,不是数字没有意义 |
| min([distinct] expr) | 返回查询到的数据的最小值,不是数字没有意义 |
其使用起来和函数一样,比如求一个数学乘积的平均值:
- 注意:在使用聚合函数的时候,必须保证对应的列是可以被聚合的,比如不能对姓名求平均值等非法操作。
3.2 分组聚合统计
下面进行一些分组聚合的常见操作。
现在有三张表:
下面将将围绕着这三张表进行各种操作。
显示各个部分的平均工资和最高工资:
根据上面的要求我们可以知道我们要对emp职工表进行操作,并且还需要对该表中的成员进行分组,按照部门进行分组。
- 语法:
group by 列名来进行分组。
- 通过指定列名,将列名相同的作为一组,将这张表中的数据分为多组,可以理解为该表被分为了多张小表;
- 再对每一个小表执行聚合函数输出结果。
- 注意:在进行分组后,并不是所有数据都可以进行显示的,即select后面不能随便跟列名,必须是能够进行聚合的列才能进行显示。
显示每个部门的每个岗位的平均工资和最低工资:
要进行分组,并且需要进行两次分组:
显示平均工资低于2000的部门和它的平均工资:
依旧是需要对部门进行分组,此时对于分组后的数据还需要进行筛选,可以使用having来分组后形成的表进行筛选:
在前面我们知道可以使用where子句进行筛选,此处邮件可以使用having进行筛选,那么他们有什么区别呢???
-
where是对于具体的任意列进行条件筛选的;而having是对分组聚合以后形成的结构进行聚合的; - 在
select语句中的执行顺序不一样,having在最后形成表后进行最后一步筛选,而where子句在第一步就进行行筛选。
以上就是分组聚合的所有内容。
四. 复合查询
在上面我们介绍了查询中一些基本的使用方法,下面将介绍一些更为复杂的筛选场景。
此处依旧是使用上面的三张表来进行演示。
3.1 子查询
- 子查询:一条查询内部可以包含其他查询语句。
在一张工资表中查询最高员工的姓名和性质:
两种方法:
- 方法一:对员工工资进行降序,输出第一行的数据:
这种方法有一个问题就是:如果有多个相同的最高工资我们无法进行全部输出,此时更应该使用方法二; - 方法二:使用子查询,先找出最高工资,在拿到表中进行比对,看谁的工资是最高工资:
3.2 多表查询
输出雇员的名字,以及对应的部门名称:
在emp表中只有不能号,而部门名称在dept表中,因此要进行多表查询。
- 关于多表查询就需要使用笛卡尔积来实现。
笛卡尔积组合:就是将两个表中的数据进行穷举组合。
比如下面将员工表与部门表进行笛卡尔积:
此时就会现成一张很大的表,该表通过枚举的方式进行组合,即枚举emp表中的每一行,与dept表中的每一行进行组合。
- 在进行多表查询的时候要进行数据的筛选,每一个成员并不需要与所有部门进行组合,只需要与相关部门进行组合即可,依次要进行
where子查询进行筛选。
注意:在进行使用成员的时候,要指明使用哪一个表的成员,对于重复的列一定要进行指明,对于不重复的列可以不进行指明。
3.3 自连接
能否对同一张表进行笛卡尔积???
可以,但是不能直接进行,因为如果直接进行两个表是一样的,在指定列名称的时候就不知道指定那一张表中的了,所以在进行笛卡尔积的时候要对两张表取别名。
比如下面这个问题:
显示每个员工的上级领导的编号和名称:
此时就可以通过自连接的方式来进行实现:
五. 将子查询结果当作表
3.4 子查询和where子句
在前面我们谈到了可以使用子查询对查询的结果进行处理。
下面详细介绍以下子查询搭配where子句的使用效果,使用方法分为三种:
- 单行子查询;
- 多行子查询;
- 多列子查询。
下面一一进行介绍。
3.4.1 单行子查询
显示SMITH内部门的其他员工:
使用子查询先找到与SMITH所在的部门,再在表中查找看谁的部门与其相同即可。
其中需要进行两次查找,(1)找到SMITH所在的部门;(2)在emp表中查找与其部门一样的成员。
3.4.2 多行子查询
查询和10号部门的工作岗位相同的雇员名称,岗位,工资,部门号,但不包含10号部门自己:
此时同样要进行两次查询:(1)先查找与10号部门的所有岗位;(2)在emp表中查找与所有与10号部门岗位一样的成员。
与上一个单行子查询不一样的是:此次查找出来的第一个表中有多方,而不再是一行;因此此处不能再使用=,而应该使用in表示是否存在表中。
在上面的基础上,增加一个条件:显示每一个员工的部门名称:
此时就需要使用符合查找了,进行多表查询:
将上面的查询结果作为一张表,再进行多表查询。
显示工资比30部门所有员工的工资都高的员工的姓名:
与上面的查询一样,只不过此时的要求是比表中的数据都高,因此此处使用all:
除了all,in还有一个any表示比表中任意一个元素怎么样…
3.4.3 多列子查询
查询和SMITH的部门和岗位完全相同的所有雇员,不包含SMITH本人:
此处要进行多列比较,因此可以进行多次单行子查询或者直接将多行作为一个整体进行比较:
- 目前全部的子查询都是在where子句中的,充当判断条件;任何时刻,查询出来的临时结构,本质在逻辑上也是表的结构。
3.5 子查询与from子句
显示每个高于自己部门平均工资的员工,姓名,部门,工资,平均工资:
进行多次查询:(1)先查询出所有部门的平均工资;(2)将平均工资与所在部门的员工进行结合。
-
此时就需要先形成一张部门平均工资的表,将该表与员工表进行多表查询。
-
多表查询的核心思想:想办法将办法将多表转化为单表,在MySQL中所有的select问题都可以转化为单表问题。
六. 其他查询方法
6.1 合并查询
就是单纯的将两个表进行合并,单纯的叠加,与笛卡尔积完全不一样。
使用union表示将表进行合并,会自动去重;而union all表示将表进行合并,不会去重。
查找工资大于2000,或职位是MANGER的成员信息:
6.2 表的内连和外连
6.2.1 内连接
就是上面我们利用where子句两个表进行笛卡尔积的方式进行标准化,增加可读性了。
语法:select 字段 from 表1 inner join 表2 on 连接条件 and 其他条件:
6.2.2 左外连接
在进行连接查找的是否,如果左侧的表我们先进行完全保留,此时就可以使用左外连接:语法就是将上面的inner换成left:select 字段 from 表1 left join 表2 on 连接条件 and 其他条件:
6.2.3 右外连接
与左外连接相反,保留右图中的所有部分,语法:select 字段 from 表1 right join 表2 on 连接条件 and 其他条件,使用结果与上面类似,此处就不再赘述了。
总结:
- 内连接仅保留两表中匹配的数据行;
- 外连接则会保留某一张表的所有数据行,另一张匹配不到的位置用NULL填充。
七. Delete删除与Update更新
7.1 删除数据
删除表中的指定数据,语法:delete from 表名 where 筛选条件,使用起来很简单,此处就不再进行赘述了。
如果没有where子句筛选条件,默认将整张表进行清空。
还有另一张情况表中数据的方式:truncate 表名;
这两种清空数据的方式有很大的区别:
- 使用delete对数据进行清空,不会对
auto_increment的计数器进行重载; - 使用trancate会对数据进行重置。
7.2 更新数据
语法:update 表名 set 列名=更新后的数据 where 筛选条件。
搭配where子句进行筛选,将筛选出来的列进行更新。