1. SQL概述
1.1 SQL分类
SQL语言在功能上分为3大类:
- DDL(Data Defination Languages,数据定义语言)
1 | CREATE,DROP,ALTER等 |
- DML(Data Manipulation Language,数据操作语言)
1 | INSERT,DELETE,UPDATE,SELECT等 |
- DCL(Data Control Language,数据控制语言)
1 | GRANT,REVOKE,COMMIT,ROLLBACK,SAVEPOINT等 |
1.2 SQL大小写规范
- MySQL在win环境下大小写不敏感
- MySQL在Linux环境下大小写敏感
- 规范:
- 数据库名,表名,表别名,字段名,字段别名都小写
- SQL关键字,函数名,绑定变量都大写
1.3 注释
1 | 单行注释: #注释文字 |
1.2 基本的SELECT语句
1 | SELECT * |
1.3 列的别名
1 | SELECT last_name AS name,commission_pct comm |
1.4 去除重复行
1 | SELECT DISTINCT department_id |
1.5 空值参与运算
MySQL中,空值不等于空字符串,空串的长度为0,空值的长度为空
所有运算符或列值遇到null值,运算的结果都为null
1 | SELECT employee_id,salary,commission_pct, |
1.6 着重号
通过着重号避免与关键字冲突
1 | SELECT * FROM `ORDER`; |
1.3 显示表结构
1 | DESCRIBE employees; |
1.4 课后练习
1 | # 1.查询员工12个月的工资总和,并起别名为ANNUAL SALARY |
2. 运算符
2.1 算术运算符
2.2 比较运算符
2.3 非符号运算符
2.4 逻辑运算符
2.5 位运算符
2.6 运算符优先级
数字越大优先级越高
2.7 课后练习
1 | # 1.选择工资不在5000到12000的员工的姓名和工资 |
3. 排序与分页
3.1 排序
- 使用ORDER BY排序
- ASC(asend):升序
- DESC(decend):降序
3.2 单列排序
1 | SELECT last_name,job_id,department_id,hire_date |
3.3 多行排序
1 | SELECT last_name,department_id,salary |
3.4 分页
1 | --前10条记录: |
使用LIMIT好处:
减少数据表的网络传输量,提升查询效率
4. 多表查询
4.1 内连接
1 | SELECT 字段列表 |
4.2 左外连接
1 | SELECT 字段列表 |
4.3 右外连接
1 | FROM A表 RIGHT JOIN B表 |
4.4 UNION
合并查询结果
1 | SELECT column,... FROM table1 |
1 | # 查询部门编号>90或邮箱中包换a的员工信息 |
4.5 USING
使用USING指定数据表中的同名字段进行等值连接,USING必须配合JOIN一起使用
1 | SELECT employee_id,last_name,department_name |
4.6 关联条件
1 | #把关联条件写在where后面 |
4.7 课后练习
1 | # 1.显示所有员工的姓名,部门号和部门名称。 |
5. 单行函数
- 操作数据对象
- 接受参数返回一个结果
- 只对一行进行变换
- 每行返回一个结果
- 可以嵌套
- 参数可以是一列或一个值
5.1 基本函数
三角函数
5.2 日期函数
获取月份,星期等函数
时间和秒钟转换函数
计算日期和时间函数
日期格式化与解析
5.3 流程控制函数
流程控制函数用于根据不同条件执行不同的处理流程
5.4 加密与解密函数
5.5 其他函数
5.6 课后练习
1 | # 1.显示系统时间(注:日期+时间) |
6. 聚合函数
聚合函数作于一组数据,并对一组数据返回一个值
常见的聚合函数:AVG(),SUM(),MAX(),MIN(),COUNT()
6.1 常见的聚合函数
可以对数值型数据使用AVG和SUM函数
1 | SELECT AVG(salary), MAX(salary),MIN(salary), SUM(salary) |
可以对任意数据类型使用MIN和MAX函数
1 | SELECT MIN(hire_date),MAX(hire_date) |
COUNT()返回表中的记录总数,适用于*任意数据类型
1 | SELECT COUNT(*) |
COUNT(expr)返回expr不为空的记录总数
1 | SELECT COUNT(commission_pct) |
6.2 GROUP BY
使用GROUP BY将表中数据分为若干组
1 | SELECT column, group_function(column) |
SELECT列表中所有为包含再组函数中的列都应该包含在GROUP BY子句中
包含在GROUP BY中的列不必包含再SELECT列表中
1 | SELECT department_id,AVG(salary) |
多列分组
1 | SELECT department_id dept_id, job_id, SUM(salary) |
使用 WITH ROLLUP 关键字之后,在所有查询出的分组记录之后增加一条记录,该记录计算查询出的所有记录的总和,即统计记录数量。
1 | SELECT department_id,AVG(salary) |
6.3 HAVING
过滤分组: HAVING
- 行已经被分组
- 使用了聚合函数
- 满足HAVING子句中条件的分组将被显示
- HAVING不能单独使用,必须要跟GROUP BY一起使用
1 | SELECT department_id,MAX(salary) |
非法使用聚合函数:
- 不能再WHERE子句中使用聚合函数
WHERE和HAVING对比:
- WHERE 可以直接使用表中的字段作为筛选条件,但不能使用分组中的计算函数作为筛选条件;HAVING 必须要与 GROUP BY 配合使用,可以把分组计算的函数和分组字段作为筛选条件。
- 如果需要通过连接从关联表中获取需要的数据,WHERE 是先筛选后接,而 HAVING 是先连接后筛选。
总结:
- WHERE,先筛选数据再关联效率高,执行效率低,不能使用分组中的计算函数进行筛选
- HAVING,在最后的结果集中进行筛选,执行效率低,可以使用分组中的计算函数
6.4 SELECT的执行过程
查询的结构
1 | #方式1: |
SELECT语句的执行顺序
1 | FROM -> WHERE -> GROUP BY -> HAVING -> SELECT 的字段 -> DISTINCT -> ORDER BY -> LIMIT |
eg
1 | SELECT DISTINCT player_id, player_name, count(*) as num # 顺序 5 |
在 SELECT 语句执行这些步骤的时候,每个步骤都会产生一个 虚拟表 ,然后将这个虚拟表传入下一个步骤中作为输入。需要注意的是,这些步骤隐含在 SQL 的执行过程中,对于我们来说是不可见的。
6.5 SQL运行原理
暂略
7. 子查询
实际问题
1 | #Main Query:谁的工资比Abel高? |
- 子查询在著查询之前一次执行完成
- 子查询的结果为著查询使用
7.1 单行子查询
单行比较操作符
eg
1 | #查询工资大于149号员工工资的员工信息 |
Having中的子查询
- 首先执行子查询
- 再向主查询中的HAVING子句返回结果
1 | #查询最低工资大于50号部门最低工资的部门id和其最低工资 |
CASE中的子查询
1 | #显式员工的employee_id,last_name和location。 |
非法使用子查询
1 | SELECt employee_id,last_name |
7.2多行子查询
- 也被称为集合比较子查询
- 内查询返回多行
- 使用多行比较操作符
多行比较操作符
IN
1 | #查询每个部门中拿最低薪水的员工信息 |
ANY
1 | #返回其它job_id中比job_id为‘IT_PROG’部门任一工资低的员工的员工号、姓名、job_id 以及salary |
ALL
1 | #返回其它job_id中比job_id为‘IT_PROG’部门所有工资低的员工的员工号、姓名、job_id 以及salary |
eg
1 | #查询平均工资最低的部门id |
7.3 相关子查询
子查询依赖于外部查询时,如使用了主查询中的列,则每执行一次外部查询子查询都要重新计算
- 丛主查询中获取候选列
- 子查询使用主查询的数据
- 如果满足子查询的条件则返回该行
相关子查询
1 | #查询员工中工资大于本部门平均公司的员工的last_name,salary和其department_id |
在FROM中使用子查询
1 | #查询员工中工资大于本部门平均公司的员工的last_name,salary和其department_id |
在GROUP BY中使用子查询
1 | #查询员工的id,salary,按照department_name 排序 |
EXISTS与NOT EXISTS关键字
- 如果子查询中不存在满足条件的行:
- 条件返回FALSE
- 继续在子查询中查找
- 如果在子查询中存在满足条件的行:
- 不在子查询中继续查找
- 条件返回TRUE - NOT EXISTS关键字表示如果不存在某种条件,则返回TRUE,否则返回FALSE
1 | #查询公司管理者的employee_id,last_name,job_id,department_id |
1 | #查询departments表中,不存在于employees表中的部门的department_id和department_name |
相关更新
1 | UPDATE table alias1 |
相关删除
1 | DELETE FROM table1 alias1 |
7.4 课后练习
1 |
|
8. 创建和管理表
8.1 MySQL中的数据类型
常用数据类型
8.2 创建和管理数据库
创建数据库
1 | #1.创建数据库 |
查看数据库
1 | #查看当前所有数据库 |
修改数据库
1 | #修改数据库字符集 |
8.3 创建表
直接创建
1 | CREATE TABLE dept( |
使用AS subquery利用查询创建
1 | CREATE TABLE emp1 FROM SELECT * FROM employees; |
查看表结构
1 | SHOW CREATE TABLE 表名\G |
8.4 修改表
1 | #添加一列 |
8.5删除和清空表
1 | #删除 |
DROP TABLE不能回滚
TRUNCATE清空所有数据并释放存储空间
8.6 MySQL8新特性-DDL的原子化
在MySQL 8.0版本中,InnoDB表的DDL支持事务完整性,即 DDL操作要么成功要么回滚 。DDL操作回滚日志写入到data dictionary数据字典表mysql.innodb_ddl_log(该表是隐藏的表,通过show tables无法看到)
中,用于回滚操作。通过设置参数,可将DDL操作日志打印输出到MySQL错误日志中。
1 | CREATE DATABASE mytest; |
9. 增删改
9.1 插入数据
1 | #1.为表的所有字段按默认顺序插入数据 |
9.2 更新数据
1 | UPDATE table_name |
9.3 删除数据
1 | DELETE FROM table_name [WHERE <condition>]; |
9.4 MySQL8新特性-计算列
在MySQL 8.0中,CREATE TABLE 和 ALTER TABLE 中都支持增加计算列。
1 | CREATE TABLE tb1( |
9.5 课后练习
1 | #1. 创建数据库dbtest11 |
10. 约束
数据完整性(Data Integrity)是指数据的精确性(Accuracy)和可靠性(Reliability)。它是防止数据库中
存在不符合语义规定的数据和防止因错误信息的输入输出造成无效操作或错误信息而提出的。
为了保证数据的完整性,SQL规范以约束的方式对表数据进行额外的条件限制。从以下四个方面考虑:
- 实体完整性(Entity Integrity) :例如,同一个表中,不能存在两条完全相同无法区分的记录
- 域完整性(Domain Integrity) :例如:年龄范围0-120,性别范围“男/女”
- 引用完整性(Referential Integrity) :例如:员工所在部门,在部门表中要能找到这个部门
- 用户自定义完整性(User-defined Integrity) :例如:用户名唯一、密码不能为空等,本部门经理的工资不得高于本部门职工的平均工资的5倍。
10.1 非空约束
- 默认,所有的类型的值都可以是NULL,包括INT、FLOAT等数据类型
- 非空约束只能出现在表对象的列上,只能某个列单独限定非空,不能组合非空
- 一个表可以有很多列都分别限定了非空空字符串’’不等于NULL,0也不等于NULL
1 | #添加非空约束 |
10.2 唯一性约束
- 同一个表可以有多个唯一约束。
- 唯一约束可以是某一个列的值唯一,也可以多个列组合的值唯一。
- 唯一性约束允许列值为空。
- 在创建唯一约束的时候,如果不给唯一约束命名,就默认和列名相同。
MySQL会给唯一约束的列上默认创建一个唯一索引
1 | #添加唯一性约束 |
10.3 PRIMARY KEY约束
- 一个表最多只能有一个主键约束,建立主键约束可以在列级别创建,也可以在表级别上创建。
- 主键约束对应着表中的一列或者多列(复合主键)
- 如果是多列组合的复合主键约束,那么这些列都不允许为空值,并且组合的值不允许重复。
- MySQL的主键名总是PRIMARY,就算自己命名了主键约束名也没用。
- 当创建主键约束时,系统默认会在所在的列或列组合上建立对应的主键索引(能够根据主键查询的,就根据主键查询,效率更高)。如果删除主键约束了,主键约束对应的索引就自动删除了。
- 需要注意的一点是,不要修改主键字段的值。因为主键是数据记录的唯一标识,如果修改了主键的值,就有可能会破坏数据的完整性
1 | create table temp( |
复合主键?因为单个主键可能会存在重名等情况,所以采用复合主键保证唯一性。
10.4 自增列:AUTO_INCREMENT
- 一个表最多只能有一个自增长列
- 当需要产生唯一标识符或顺序值时,可设置自增长
- 自增长列约束的列必须是键列(主键列,唯一键列)
- 自增约束的列的数据类型必须是整数类型
- 如果自增列指定了 0 和 null,会在当前最大值的基础上自增;如果自增列手动指定了具体值,直接赋值为具体值。
1 | #创建自增约束 |
10.5 MySQL8新特性-自增变量的持久化
MySQL 8.0将自增主键的计数器持久化到 重做日志 中。每次计数器发生改变,都会将其写入重做日志中。如果数据库重启,InnoDB会根据重做日志中的信息来初始化计数器的内存值。
10.6 FOREIGN KEY约束
主表和从表
主表(父表):被引用的表,被参考的表
从表(子表):引用别人的表,参考别人的表
例如:员工表的员工所在部门这个字段的值要参考部门表:部门表是主表,员工表是从表。
例如:学生表、课程表、选课表:选课表的学生和课程要分别参考学生表和课程表,学生表和课程表是
主表,选课表是从表。
特点
(1)从表的外键列,必须引用/参考主表的主键或唯一约束的列
为什么?因为被依赖/被参考的值必须是唯一的
(2)在创建外键约束时,如果不给外键约束命名,默认名不是列名,而是自动产生一个外键名(例如student_ibfk_1;),也可以指定外键约束名。
(3)创建(CREATE)表时就指定外键约束的话,先创建主表,再创建从表
(4)删表时,先删从表(或先删除外键约束),再删除主表
(5)当主表的记录被从表参照时,主表的记录将不允许删除,如果要删除数据,需要先删除从表中依赖该记录的数据,然后才可以删除主表的数据
(6)在“从表”中指定外键约束,并且一个表可以建立多个外键约束
(7)从表的外键列与主表被参照的列名字可以不相同,但是数据类型必须一样,逻辑意义一致。如果类型不一样,创建子表时,就会出现错误“ERROR 1005 (HY000): Can’t create table’database.tablename’(errno: 150)”。
例如:都是表示部门编号,都是int类型。
(8)当创建外键约束时,系统默认会在所在的列上建立对应的普通索引。但是索引名是外键的约束名。(根据外键查询效率很高)
(9)删除外键约束后,必须 手动 删除对应的索引
1 | #添加外键约束 |
约束等级
- Cascade方式 :在父表上update/delete记录时,同步update/delete掉子表的匹配记录
- Set null方式 :在父表上update/delete记录时,将子表上匹配记录的列设为null,但是要注意子
表的外键列不能为not null - No action方式 :如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作
- Restrict方式 :同no action, 都是立即检查外键约束
- Set default方式 (在可视化工具SQLyog中可能显示空白):父表有变更时,子表将外键列设置成一个默认的值,但Innodb不能识别
1 | create table dept( |
10.7 CHECK约束
检查某个字段的值是否符号xx要求,一般指的是值的范围
5.7不支持check
1 | CREATE TABLE temp( |
10.8 DEFAULT约束
给某个字段/某列指定默认值,一旦设置默认值,在插入数据时,如果此字段没有显式赋值,则赋值为默认值。
1 | create table employee( |
11. 视图
- 视图是一种 虚拟表 ,本身是 不具有数据 的,占用很少的内存空间,它是 SQL 中的一个重要概念。
- 视图建立在已有表的基础上, 视图赖以建立的这些表称为基表。
- 视图的创建和删除只影响视图本身,不影响对应的基表。但是当对视图中的数据进行增加、删除和修改操作时,数据表中的数据会相应地发生变化,反之亦然。
- 视图,是向用户提供基表数据的另一种表现形式。通常情况下,小型项目的数据库可以不使用视图,但是在大型项目中,以及数据表比较复杂的情况下,视图的价值就凸显出来了,它可以帮助我们把经常查询的结果集放到虚拟表中,提升使用效率。理解和使用起来都非常方便。
11.1 创建视图
1 | #创建普通视图 |
11.2 更新视图
MySQL支持使用INSERT、UPDATE和DELETE语句对视图中的数据进行插入、更新和删除操作。当视图中的数据发生变化时,数据表中的数据也会发生变化,反之亦然。
要使视图可更新,视图中的行和底层基本表中的行之间必须存在 一对一 的关系。另外当视图定义出现如
下情况时,视图不支持更新操作:
- 在定义视图的时候指定了“ALGORITHM = TEMPTABLE”,视图将不支持INSERT和DELETE操作;
- 视图中不包含基表中所有被定义为非空又未指定默认值的列,视图将不支持INSERT操作;
- 在定义视图的SELECT语句中使用了 JOIN联合查询 ,视图将不支持INSERT和DELETE操作;
- 在定义视图的SELECT语句后的字段列表中使用了数学表达式或子查询,视图将不支持INSERT,也不支持UPDATE使用了数学表达式、子查询的字段值;
- 在定义视图的SELECT语句后的字段列表中使用 DISTINCT 、 聚合函数 、 GROUP BY 、 HAVING 、UNION 等,视图将不支持INSERT、UPDATE、DELETE;
- 在定义视图的SELECT语句中包含了子查询,而子查询中引用了FROM后面的表,视图将不支持INSERT、UPDATE、DELETE;
- 视图定义基于一个不可更新视图;
- 常量视图。
11.3 修改,删除视图
修改视图
1.使用**CREATE OR REPLACE VIEW **子句修改视图
1 | CREATE OR REPLACE VIEW empvu80 |
2.ALTER VIEW
1 | ALTER VIEW #视图名称 |
删除视图
删除视图只是删除视图的定义,并不会删除基表的数据。
1 | DROP VIEW IF EXISTS 视图名称; |
11.4 视图总结
- 操作简单
- 减少数据冗余
- 数据安全
- 用户不需要查询数据表,可以直接通过视图获取数据表中的信息。
- 适应灵活多变的需求
- 能够分解复杂的查询逻辑
如果实际数据表的结构变更了,我们就需要及时对相关的视图进行相应的维护。特别是嵌套的视图(就是在视图的基础上创建视图),维护会变得比较复杂, 可读性不好 ,容易变成系统的潜在隐患。