MySQL基础知识

MySQL数据库搭建
  1. 软件的下载
  2. 官网:https://www.mysql.com
  3. SQL背景知识
  4. 1946 年,世界上第一台电脑诞生,如今,借由这台电脑发展起来的互联网已经自成江湖。在这几十 年里,无数的技术、产业在这片江湖里沉浮,有的方兴未艾,有的已经几幕兴衰。但在这片浩荡的 波动里,有一门技术从未消失,甚至“老当益壮”,那就是 SQL。 45 年前,也就是 1974 年,IBM 研究员发布了一篇揭开数据库技术的论文《SEQUEL:一门结构 化的英语查询语言》,直到今天这门结构化的查询语言并没有太大的变化,相比于其他语 言, SQL 的半衰期可以说是非常长 了。 不论是前端工程师,还是后端算法工程师,都一定会和数据打交道,都需要了解如何又快又准确地 提取自己想要的数据。更别提数据分析师了,他们的工作就是和数据打交道,整理不同的报告,以 便指导业务决策。 SQL(Structured Query Language,结构化查询语言)是使用关系模型的数据库应用语言, 与数据直 接打交道 ,由 IBM 上世纪70年代开发出来。后由美国国家标准局(ANSI)开始着手制定SQL标准, 先后有 SQL-86 , SQL-89 , SQL-92 , SQL-99 等标准。 SQL 有两个重要的标准,分别是 SQL92 和 SQL99,它们分别代表了 92 年和 99 年颁布的 SQL 标 准,我们今天使用的 SQL 语言依然遵循这些标准。 不同的数据库生产厂商都支持SQL语句,但都有特有内容。
  5. SQL 分类
    1. DDL(Data Definition Languages、数据定义语言),这些语句定义了不同的数据库、表、视图、索 引等数据库对象,还可以用来创建、删除、修改数据库和数据表的结构。
    1. 主要的语句关键字包括 CREATE 、 DROP 、 ALTER 等。
  2. DML(Data Manipulation Language、数据操作语言),用于添加、删除、更新和查询数据库记 录,并检查数据完整性。 
     1. 主要的语句关键字包括 INSERT 、 DELETE 、 UPDATE 、 SELECT 等。 SELECT是SQL语言的基础,最为重要。 
  3. DCL(Data Control Language、数据控制语言),用于定义数据库、表、字段、用户的访问权限和 安全级别。 
     1. 主要的语句关键字包括 GRANT 、 REVOKE 、 COMMIT 、 ROLLBACK 、 SAVEPOINT 等。  
  1. SQL语言的规则与规范
  2. 基本规则
    1. SQL 可以写在一行或者多行。为了提高可读性,各子句分行写,必要时使用缩进
    2. 每条命令以 ; 或 \g 或 \G 结束
    3. 关键字不能被缩写也不能分行
    4. 关于标点符号
    1. 必须保证所有的()、单引号、双引号是成对结束的
    2. 必须使用英文状态下的半角输入方式
    3. 字符串型和日期时间类型的数据可以使用单引号(’ ')表示
    4. 列的别名,尽量使用双引号(" "),而且不建议省略as
  3. SQL大小写规范
  4. MySQL 在 Windows 环境下是大小写不敏感的
  5. MySQL 在 Linux 环境下是大小写敏感的
    1. 数据库名、表名、表的别名、变量名是严格区分大小写的
    2. 关键字、函数名、列名(或字段名)、列的别名(字段的别名) 是忽略大小写的。
  6. 推荐采用统一的书写规范:
    1. 数据库名、表名、表别名、字段名、字段别名等都小写
    2. SQL 关键字、函数名、绑定变量等都大写
  7. 注 释
  8. 单行注释:#注释文字(MySQL特有的方式)
  9. 单行注释:-- 注释文字(–后面必须包含一个空格。)
  10. 多行注释:/* 注释文字 */
  11. 命名规则
    1. 数据库、表名不得超过30个字符,变量名限制为29个
    2. 必须只能包含 A–Z, a–z, 0–9, _共63个字符
    3. 数据库名、表名、字段名等对象名中间不要包含空格
    4. 同一个MySQL软件中,数据库不能同名;同一个库中,表不能重名;同一个表中,字段不能重名
    5. 必须保证你的字段没有和保留字、数据库系统或常用方法冲突。如果坚持使用,请在SQL语句中使 用`(着重号)引起来
    6. 保持字段名和类型的一致性,在命名字段并为其指定数据类型的时候一定要保证一致性。假如数据 类型在一个表里是整数,那在另一个表里可就别变成字符型了
#以下两句是一样的,不区分大小写
show databases;
SHOW DATABASES;
#创建表格
#create table student info(...); #表名错误,因为表名有空格
create table student_info(...);
#其中order使用``飘号,因为order和系统关键字或系统函数名等预定义标识符重名了
CREATE TABLE `order`(
  id INT,
  lname VARCHAR(20)
);
select id as "编号", `name` as "姓名" from t_stu; #起别名时,as都可以省略
select id as 编号, `name` as 姓名 from t_stu; #如果字段别名中没有空格,那么可以省略""
select id as 编 号, `name` as 姓 名 from t_stu; #错误,如果字段别名中有空格,那么不能省略""

  1. 数据导入指令
  2. 在命令行客户端登录mysql,使用source指令导入 mysql> source d:\mysqldb.sql
mysql> desc employees;
+----------------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+----------------+-------------+------+-----+---------+-------+
| employee_id | int(6) | NO | PRI | 0 | |
| first_name | varchar(20) | YES | | NULL | |
| last_name | varchar(25) | NO | | NULL | |
| email | varchar(25) | NO | UNI | NULL | |
| phone_number | varchar(20) | YES | | NULL | |
| hire_date | date | NO | | NULL | |
| job_id | varchar(10) | NO | MUL | NULL | |
| salary | double(8,2) | YES | | NULL | |
| commission_pct | double(2,2) | YES | | NULL | |
| manager_id | int(6) | YES | MUL | NULL | |
| department_id | int(4) | YES | MUL | NULL | |
+----------------+-------------+------+-----+---------+-------+
11 rows in set (0.00 sec)
基本的SELECT语句
  1. SELECT…
SELECT 1; #没有任何子句
SELECT 9/2; #没有任何子句
  1. SELECT … FROM
  2. 语法:
SELECT 标识选择哪些列
FROM 标识从哪个表中选择

  1. 选择全部列:
SELECT *
FROM departments;
  1. 选择特定的列:
SELECT department_id, location_id
FROM departments;

image.png

  1. 列的别名
  2. 重命名一个列
  3. 便于计算
  4. 紧跟列名,也可以在列名和别名之间加入关键字AS,别名使用双引号,以便在别名中包含空格或特 殊的字符并区分大小写。
  5. AS 可以省略
  6. 建议别名简短,见名知意
SELECT last_name AS name, commission_pct comm
FROM employees;

image.png

  1. 去除重复行
  2. 默认情况下,查询会返回全部行,包括重复行。
##默认情况下,查询会返回全部行,包括重复行。
SELECT department_id
FROM employees;

image.png

  1. 在SELECT语句中使用关键字DISTINCT去除重复行
SELECT DISTINCT department_id
FROM employees;

image.png

  1. 针对于:
SELECT DISTINCT department_id,salary
FROM employees;

  1. 这里有两点需要注意:
    1. DISTINCT 需要放到所有列名的前面,如果写成 SELECT salary, DISTINCT department_id FROM employees 会报错。
    2. DISTINCT 其实是对后面所有列名的组合进行去重,你能看到最后的结果是 74 条,因为这 74 个部 门id不同,都有 salary 这个属性值。如果你想要看都有哪些不同的部门(department_id),只需 要写 DISTINCT department_id 即可,后面不需要再加其他的列名了。

image.png

  1. 空值参与运算
  2. 所有运算符或列值遇到null值,运算的结果都为null
SELECT employee_id,salary,commission_pct,
12 * salary * (1 + commission_pct) "annual_sal"
FROM employees;

这里你一定要注意,在 MySQL 里面, 空值不等于空字符串。一个空字符串的长度是 0,而一个空值的长 度是空。而且,在 MySQL 里面,空值是占用空间的。
image.png

  1. 着重号
  2. 错误的
    1. SELECT * FROM ORDER;

image.png

  1. 正确的
    1. SELECT * FROM ORDER;

image.png

  1. 我们需要保证表中的字段、表名等没有和保留字、数据库系统或常用方法冲突。如果真的相同,请在 SQL语句中使用一对``(着重号)引起来。
  2. 查询常数
    1. SELECT 查询还可以对常数进行查询。对的,就是在 SELECT 查询结果中增加一列固定的常数列。这列的 取值是我们指定的,而不是从数据表中动态取出的。
    2. 你可能会问为什么我们还要对常数进行查询呢?
    3. SQL 中的 SELECT 语法的确提供了这个功能,一般来说我们只从一个表中查询数据,通常不需要增加一个 固定的常数列,但如果我们想整合不同的数据源,用常数列作为这个表的标记,就需要查询常数。
    4. 比如说,我们想对 employees 数据表中的员工姓名进行查询,同时增加一列字段 corporation ,这个 字段固定值为“Google”,可以这样写:
    5. image.png
  3. 显示表结构
    1. 使用DESCRIBE 或 DESC 命令,表示表结构。
DESCRIBE employees;DESC employees;

image.png

  1. 其中,各个字段的含义分别解释如下:
    1. Field:表示字段名称。
    2. Type:表示字段类型,这里 barcode、goodsname 是文本型的,price 是整数类型的。
    3. Null:表示该列是否可以存储NULL值。
    4. Key:表示该列是否已编制索引。PRI表示该列是表主键的一部分;UNI表示该列是UNIQUE索引的一 部分;MUL表示在列中某个给定值允许出现多次。
    5. Default:表示该列是否有默认值,如果有,那么值是多少。
    6. Extra:表示可以获取的与给定列有关的附加信息,例如AUTO_INCREMENT等。
  2. 过滤数据
    1. 语法:
SELECT 字段1,字段2
FROM 表名
WHERE 过滤条件
  1. 使用WHERE 子句,将不满足条件的行过滤掉
  2. WHERE子句紧随 FROM子句
SELECT employee_id, last_name, job_id, department_id
FROM employees
WHERE department_id = 90 ;

image.png

  1. 练习
    1. 查询员工12个月的工资总和,并起别名为ANNUAL SALARY
SELECT employee_id,last_name,salary * 12 "ANNUAL SALARY"
FROM employees;

image.png

  1. 查询employees表中去除重复的job_id以后的数据
# 2.查询employees表中去除重复的job_id以后的数据
SELECT DISTINCT job_id
FROM employees;

image.png

  1. 查询工资大于12000的员工姓名和工资
# 查询工资大于12000的员工姓名和工资
SELECT last_name,salary
FROM employees
WHERE salary > 12000;

image.png

  1. 查询员工号为176的员工的姓名和部门号
# 4.查询员工号为176的员工的姓名和部门号
SELECT last_name,department_id
FROM employees
WHERE employee_id = 176;

image.png

  1. 显示表 departments 的结构,并查询其中的全部数据
# 显示表 departments 的结构,并查询其中的全部数据 
DESCRIBE departments;

SELECT * FROM departments;

image.png

运算符
算术运算符
  1. 算术运算符主要用于数学运算,其可以连接运算符前后的两个数值或表达式,对数值或表达式进行加 (+)、减(-)、乘(*)、除(/)和取模(%)运算 image.png
  2. 加法与减法运算符

image.png

  1. 由运算结果可以得出如下结论:
    1. 一个整数类型的值对整数进行加法和减法操作,结果还是一个整数;
    2. 一个整数类型的值对浮点数进行加法和减法操作,结果是一个浮点数;
    3. 加法和减法的优先级相同,进行先加后减操作与进行先减后加操作的结果是一样的;
    4. 在Java中,+的左右两边如果有字符串,那么表示字符串的拼接。但是在MySQL中+只表示数 值相加。如果遇到非数值类型,先尝试转成数值,如果转失败,就按0计算。(补充:MySQL 中字符串拼接要使用字符串函数CONCAT()实现)
  2. 乘法与除法运算符
    1.
SELECT 100, 100 * 1, 100 * 1.0, 100 / 1.0, 100 / 2,
100 + 2 * 5 / 2,100 / 3, 100 DIV 0  # 分母如果为0,则结果为null
FROM DUAL;

image.png

  1. 求模(求余)运算符
# 取模运算: % mod
SELECT 12 % 3,12 % 5, 12 MOD -5,-12 % 5,-12 % -5
FROM DUAL;

image.png

  1. #练习:查询员工id为偶数的员工信息
SELECT employee_id,last_name,salary
FROM employees
WHERE employee_id % 2 = 0;

image.png

比较运算符
  1. 比较运算符用来对表达式左边的操作数和右边的操作数进行比较,比较的结果为真则返回1,比较的结果 为假则返回0,其他情况则返回NULL。 比较运算符经常被用来作为SELECT查询语句的条件来使用,返回符合条件的结果记录。
  2. image.png
  3. 等号运算符
    1. 等号运算符(=)判断等号两边的值、字符串或表达式是否相等,如果相等则返回1,不相等则返回 0。
    2. 在使用等号运算符时,遵循如下规则:
    1. 如果等号两边的值、字符串或表达式都为字符串,则MySQL会按照字符串进行比较,其比较的 是每个字符串中字符的ANSI编码是否相等。
    2. 如果等号两边的值都是整数,则MySQL会按照整数来比较两个值的大小。
    3. 如果等号两边的值一个是整数,另一个是字符串,则MySQL会将字符串转化为数字进行比较。
    4. 如果等号两边的值、字符串或表达式中有一个为NULL,则比较结果为NULL。
    5. 对比:SQL中赋值符号使用 :=
  3. 
# = 的使用
SELECT 1 = 2,1 != 2,1 = '1',1 = 'a',0 = 'a' #字符串存在隐式转换。如果转换数值不成功,则看做0
FROM DUAL;

image.png


SELECT 'a' = 'a','ab' = 'ab','a' = 'b' #两边都是字符串的话,则按照ANSI的比较规则进行比较。
FROM DUAL;
SELECT 1 = NULL,NULL = NULL # 只要有null参与判断,结果就为null
FROM DUAL;

image.png

  1. 安全等于运算符
    1. 安全等于运算符(<=>)与等于运算符(=)的作用是相似的, 唯一的区别 是‘<=>’可 以用来对NULL进行判断。在两个操作数均为NULL时,其返回值为1,而不为NULL;当一个操作数为NULL 时,其返回值为0,而不为NULL。
# <=> :安全等于。 记忆技巧:为NULL而生。

SELECT 1 <=> 2,1 <=> '1',1 <=> 'a',0 <=> 'a'
FROM DUAL;

SELECT 1 <=> NULL, NULL <=> NULL
FROM DUAL;
  2. ![image.png](https://cdn.nlark.com/yuque/0/2023/png/35659086/1702298121025-a9809c55-b084-4fa2-bba3-cf66330f1b65.png#averageHue=%23373636&clientId=u8f4bcf4f-e7b1-4&from=paste&height=113&id=u3b18ee58&originHeight=170&originWidth=598&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=10145&status=done&style=none&taskId=uc6fcb81e-ea35-4175-ab3e-a610f04ba0e&title=&width=398.6666666666667)
  3. 查询表中commission_pct为null的数据有哪些
  4. ![image.png](https://cdn.nlark.com/yuque/0/2023/png/35659086/1702298176414-031c4d88-e9a0-4295-b18f-dda00457c085.png#averageHue=%23343332&clientId=u8f4bcf4f-e7b1-4&from=paste&height=276&id=uc19cba16&originHeight=414&originWidth=615&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=34554&status=done&style=none&taskId=u8031892b-eb58-4406-a0d8-426b0362ddd&title=&width=410)
SELECT 1 <=> NULL, NULL <=> NULL
FROM DUAL;
  5. ![image.png](https://cdn.nlark.com/yuque/0/2023/png/35659086/1702298235004-74daf7d5-de05-462b-ad55-f5c43e373657.png#averageHue=%23373636&clientId=u8f4bcf4f-e7b1-4&from=paste&height=115&id=u328d3c91&originHeight=173&originWidth=459&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=8918&status=done&style=none&taskId=u4595ee75-93f8-4e14-834f-c3378ea92f1&title=&width=306)
  6.  可以看到,使用安全等于运算符时,两边的操作数的值都为NULL时,返回的结果为1而不是NULL,其他 返回结果与等于运算符相同  
  1. 不等于运算符
    1. 不等于运算符(<>和!=)用于判断两边的数字、字符串或者表达式的值是否不相等, 如果不相等则返回1,相等则返回0。不等于运算符不能判断NULL值。如果两边的值有任意一个为NULL, 或两边都为NULL,则结果为NULL。 SQL语句示例如下:
SELECT 1 <> 1, 1 != 2, 'a' != 'b', (3+4) <> (2+6), 'a' != NULL, NULL <> NULL;

  1. 空运算符
    1. 空运算符(IS NULL或者ISNULL)判断一个值是否为NULL,如果为NULL则返回1,否则返回 0。 SQL语句示例如下:
> SELECT NULL IS NOT NULL, 'a' IS NOT NULL, 1 IS NOT NULL;

image.png

#练习:查询表中commission_pct为null的数据有哪些
SELECT last_name,salary,commission_pct
FROM employees
WHERE commission_pct IS NULL;

SELECT last_name,salary,commission_pct
FROM employees
WHERE ISNULL(commission_pct);
  2. ![image.png](https://cdn.nlark.com/yuque/0/2023/png/35659086/1702298410402-ade7d105-8e4a-4536-b5c7-5411cfb5918e.png#averageHue=%23373534&clientId=u8f4bcf4f-e7b1-4&from=paste&height=221&id=u78369f63&originHeight=332&originWidth=607&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=31687&status=done&style=none&taskId=u94258ad6-0a72-4617-93a3-67a0fee4dff&title=&width=404.6666666666667)
  3. 查询表中commission_pct不为null的数据有哪些
#练习:查询表中commission_pct不为null的数据有哪些
SELECT last_name,salary,commission_pct
FROM employees
WHERE commission_pct IS NOT NULL;
#或
SELECT last_name,salary,commission_pct
FROM employees
WHERE NOT commission_pct <=> NULL;
  1. 非空运算符
    1. 非空运算符(IS NOT NULL)判断一个值是否不为NULL,如果不为NULL则返回1,否则返 回0。 SQL语句示例如下
#① IS NULL \ IS NOT NULL \ ISNULL

SELECT NULL IS NOT NULL, 'a' IS NOT NULL, 1 IS NOT NULL;
  2. ![image.png](https://cdn.nlark.com/yuque/0/2023/png/35659086/1702299008882-b758497c-c4fd-4ce4-869a-1949c5525e75.png#averageHue=%23313030&clientId=u8f4bcf4f-e7b1-4&from=paste&height=167&id=ij0PT&originHeight=251&originWidth=761&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=20590&status=done&style=none&taskId=ubf3b26ad-434c-4482-93e0-565dc1ca4b7&title=&width=507.3333333333333)
#练习:查询表中commission_pct不为null的数据有哪些
SELECT last_name,salary,commission_pct
FROM employees
WHERE commission_pct IS NOT NULL;
  3. ![image.png](https://cdn.nlark.com/yuque/0/2023/png/35659086/1702299110712-380cc806-2248-414c-8b03-2d8fd63299b1.png#averageHue=%23393837&clientId=u8f4bcf4f-e7b1-4&from=paste&height=187&id=ub4a873e5&originHeight=281&originWidth=543&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=25096&status=done&style=none&taskId=u17cd16b0-6a10-478b-bda2-c6e7e2e9362&title=&width=362)
  1. 最小值运算符
    1. 语法格式为:LEAST(值1,值2,…,值n)。其中,“值n”表示参数列表中有n个值。在有 两个或多个参数的情况下,返回最小值。

SELECT LEAST('g','b','t','m'),GREATEST('g','b','t','m')
FROM DUAL;

image.png
由结果可以看到,当参数是整数或者浮点数时,LEAST将返回其中最小的值;当参数为字符串时,返回字 母表中顺序最靠前的字符;当比较值列表中有NULL时,不能判断大小,返回值为NULL。

  1. 最大值运算符
    1. 语法格式为:GREATEST(值1,值2,…,值n)。其中,n表示参数列表中有n个值。当有 两个或多个参数时,返回值为最大值。假如任意一个自变量为NULL,则GREATEST()的返回值为NULL。
SELECT GREATEST(1,0,2), GREATEST('b','a','c'), GREATEST(1,NULL,2);
  1. . BETWEEN AND运算符
    1. BETWEEN运算符使用的格式通常为SELECT D FROM TABLE WHERE C BETWEEN A AND B,此时,当C大于或等于A,并且C小于或等于B时,结果为1,否则结果为0。
SELECT 1 BETWEEN 0 AND 1, 10 BETWEEN 11 AND 12, 'b' BETWEEN 'a' AND 'c';

image.png

     SELECT last_name, salary FROM employees WHERE salary BETWEEN 2500 AND 3500;  
  2. ![image.png](https://cdn.nlark.com/yuque/0/2023/png/35659086/1702299666555-a9a5b726-96f8-4b15-bfab-ff100e2b91aa.png#averageHue=%23353433&clientId=u8f4bcf4f-e7b1-4&from=paste&height=202&id=u53309e9e&originHeight=303&originWidth=527&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=23923&status=done&style=none&taskId=u16d2a809-ac63-4dcd-9597-cadbffce7bf&title=&width=351.3333333333333)
  1. . IN运算符
    1. IN运算符用于判断给定的值是否是IN列表中的一个值,如果是则返回1,否则返回0。如果给 定的值为NULL,或者IN列表中存在NULL,则结果为NULL。
  2. NOT IN运算符 NOT IN运算符用于判断给定的值是否不是IN列表中的一个值,如果不是IN列表中的一 个值,则返回1,否则返回0。
  3. LIKE运算符 LIKE运算符主要用来匹配字符串,通常用于模糊匹配,如果满足条件则返回1,否则返回 0。如果给定的值或者匹配条件为NULL,则返回结果为NULL。 LIKE运算符通常使用如下通配符:
    1. “%”:匹配0个或多个字符。
    2. “_”:只能匹配一个字符。
#练习:查询last_name中包含字符'a'的员工信息
SELECT last_name
FROM employees
WHERE last_name LIKE '%a%';

#练习:查询last_name中以字符'a'开头的员工信息
SELECT last_name
FROM employees
WHERE last_name LIKE 'a%';

  1. ESCAPE
    1. 回避特殊符号的:使用转义符。例如:将[%]转为[ %]、[]转为[ ],然后再加上[ESCAPE‘$’]即可
#或者  (了解)
SELECT last_name
FROM employees
WHERE last_name LIKE '_$_a%' ESCAPE '$';
  1. REGEXP运算符
    1. REGEXP运算符用来匹配字符串,语法格式为: expr REGEXP 匹配条件 。如果expr满足匹配条件,返回 1;如果不满足,则返回0。若expr或匹配条件任意一个为NULL,则结果为NULL。 REGEXP运算符在进行匹配时,常用的有下面几种通配符:
    1. ‘^’匹配以该字符后面的字符开头的字符串。
    2. ‘$’匹配以该字符前面的字符结尾的字符串。
    3. ‘.’匹配任何一个单字符。
    4. “[…]”匹配在方括号内的任何字符。
      1. 例如,“[abc]”匹配“a”或“b”或“c”。为了命名字符的范围,使用一 个‘-’。“[a-z]”匹配任何字母,而“[0-9]”匹配任何数字。
    5. ‘*’匹配零个或多个在它前面的字符。
      1. 例如,“x*”匹配任何数量的‘x’字符,“[0-9]”匹配任何数量的数字, 而“”匹配任何数量的任何字符。
逻辑运算符
  1. 逻辑运算符主要用来判断表达式的真假,在MySQL中,逻辑运算符的返回结果为1、0或者NULL。 MySQL中支持4种逻辑运算符如下:

image.png

  1. 逻辑非运算符
    1. 逻辑非运算符
    1. 逻辑非(NOT或!)运算符表示当给定的值为0时返回1;当给定的值为非0值时返回0; 当给定的值为NULL时,返回NULL。
    2. SELECT NOT 1, NOT 0, NOT(1+1), NOT !1, NOT NULL
SELECT last_name, job_id
FROM employees
WHERE job_id NOT IN ('IT_PROG', 'ST_CLERK', 'SA_REP');
  2.  逻辑与运算符 
     1. 逻辑与(AND或&&)运算符是当给定的所有值均为非0值,并且都不为NULL时,返回 1;当给定的一个值或者多个值为0时则返回0;否则返回NULL。 
     2.   SELECT 1 AND -1, 0 AND 1, 0 AND NULL, 1 AND NULL;  
SELECT employee_id, last_name, job_id, salary
FROM employees
WHERE salary >=10000
AND job_id LIKE '%MAN%';

  3. 逻辑或运算符 
     1. 逻辑或(OR或||)运算符是当给定的值都不为NULL,并且任何一个值为非0值时,则返 回1,否则返回0;当一个值为NULL,并且另一个值为非0值时,返回1,否则返回NULL;当两个值都为 NULL时,返回NULL 
     2.  SELECT 1 OR -1, 1 OR 0, 1 OR NULL, 0 || NULL, NULL || NULL  
#查询基本薪资不在9000-12000之间的员工编号和基本薪资
SELECT employee_id,salary FROM employees
WHERE NOT (salary >= 9000 AND salary <= 12000);
SELECT employee_id,salary FROM employees
WHERE salary <9000 OR salary > 12000;
SELECT employee_id,salary FROM employees
WHERE salary NOT BETWEEN 9000 AND 12000;

SELECT employee_id, last_name, job_id, salary
FROM employees
WHERE salary >= 10000
OR job_id LIKE '%MAN%';

  4.  逻辑异或运算符 
     1. 逻辑异或(XOR)运算符是当给定的值中任意一个值为NULL时,则返回NULL;如果 两个非NULL的值都是0或者都不等于0时,则返回0;如果一个值为0,另一个值不为0时,则返回1。  
     2.  SELECT 1 XOR -1, 1 XOR 0, 0 XOR 0, 1 XOR NULL, 1 XOR 1 XOR 1, 0 XOR 0 XOR 0;  
select last_name,department_id,salary
from employees
where department_id in (10,20) XOR salary > 8000;
位运算符
  1. 位运算符是在二进制数上进行计算的运算符。位运算符会先将操作数变成二进制数,然后进行位运算, 最后将计算结果从二进制变回十进制数。 MySQL支持的位运算符如下:

image.png

  1. 按位与运算符
    1. 按位与(&)运算符将给定值对应的二进制数逐位进行逻辑与运算。当给定值对应的二 进制位的数值都为1时,则该位返回1,否则返回0。
mysql> SELECT 1 & 10, 20 & 30;
+--------+---------+
| 1 & 10 | 20 & 30 |
+--------+---------+
| 0 | 20 |
+--------+---------+
1 row in set (0.00 sec)

  2.  1的二进制数为0001,10的二进制数为1010,所以1 & 10的结果为0000,对应的十进制数为0。20的二进制 数为10100,30的二进制数为11110,所以20 & 30的结果为10100,对应的十进制数为20。  
  1. 按位异或运算符
    1. 按位异或(^)运算符将给定的值对应的二进制数逐位进行逻辑异或运算。当给定值 对应的二进制位的数值不同时,则该位返回1,否则返回0。
mysql> SELECT 1 | 10, 20 | 30;
+--------+---------+
| 1 | 10 | 20 | 30 |
+--------+---------+
| 11 | 30 |
+--------+---------+
1 row in set (0.00 sec)

  2. 1的二进制数为0001,10的二进制数为1010,所以1 ^ 10的结果为1011,对应的十进制数为11。20的二进 制数为10100,30的二进制数为11110,所以20 ^ 30的结果为01010,对应的十进制数为10。 再举例:  
  1. 按位异或运算符
    1. 按位异或(^)运算符将给定的值对应的二进制数逐位进行逻辑异或运算。当给定值 对应的二进制位的数值不同时,则该位返回1,否则返回0。
mysql> SELECT 1 ^ 10, 20 ^ 30;
+--------+---------+
| 1 ^ 10 | 20 ^ 30 |
+--------+---------+
| 11 | 10 |
+--------+---------+
1 row in set (0.00 sec)
  2.  1的二进制数为0001,10的二进制数为1010,所以1 ^ 10的结果为1011,对应的十进制数为11。20的二进 制数为10100,30的二进制数为11110,所以20 ^ 30的结果为01010,对应的十进制数为10。  
  3. ![image.png](https://cdn.nlark.com/yuque/0/2023/png/35659086/1702301040706-b1d2f411-8c7c-4cc4-9b77-9151c9b5e38b.png#averageHue=%23ccd9ec&clientId=u8f4bcf4f-e7b1-4&from=paste&height=359&id=u7da3a9da&originHeight=538&originWidth=727&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=32233&status=done&style=none&taskId=u2758ebd4-8a65-48fe-95ec-2acfbeaa65f&title=&width=484.6666666666667)
  1. 按位取反运算符
    1. 按位取反(~)运算符将给定的值的二进制数逐位进行取反操作,即将1变为0,将0变 为1。
mysql> SELECT 10 & ~1;
+---------+
| 10 & ~1 |
+---------+
| 10 |
+---------+
1 row in set (0.00 sec)
  2.  由于按位取反(~)运算符的优先级高于按位与(&)运算符的优先级,所以10 & ~1,首先,对数字1进 行按位取反操作,结果除了最低位为0,其他位都为1,然后与10进行按位与操作,结果为10。  
  1. 按位右移运算符
    1. 按位右移(>>)运算符将给定的值的二进制数的所有位右移指定的位数。右移指定的 位数后,右边低位的数值被移出并丢弃,左边高位空出的位置用0补齐。
mysql> SELECT 1 >> 2, 4 >> 2;
+--------+--------+
| 1 >> 2 | 4 >> 2 |
+--------+--------+
| 0 | 1 |
+--------+--------+
1 row in set (0.00 sec)

  2.  1的二进制数为0000 0001,右移2位为0000 0000,对应的十进制数为0。4的二进制数为0000 0100,右移2 位为0000 0001,对应的十进制数为1。  
  1. 按位左移运算符
    1. 按位左移(<<)运算符将给定的值的二进制数的所有位左移指定的位数。左移指定的 位数后,左边高位的数值被移出并丢弃,右边低位空出的位置用0补齐。
mysql> SELECT 1 << 2, 4 << 2;
+--------+--------+
| 1 << 2 | 4 << 2 |
+--------+--------+
| 4 | 16 |
+--------+--------+
1 row in set (0.00 sec)
  2. 1的二进制数为0000 0001,左移两位为0000 0100,对应的十进制数为4。4的二进制数为0000 0100,左移 两位为0001 0000,对应的十进制数为16。  
  1. 运算符的优先级
    1. image.png
    2. 数字编号越大,优先级越高,优先级高的运算符先进行计算。可以看到,赋值运算符的优先级最低,使 用“()”括起来的表达式的优先级最高。
拓展:使用正则表达式查询
  1. 正则表达式通常被用来检索或替换那些符合某个模式的文本内容,根据指定的匹配模式匹配文本中符合 要求的特殊字符串。例如,从一个文本文件中提取电话号码,查找一篇文章中重复的单词或者替换用户 输入的某些敏感词语等,这些地方都可以使用正则表达式。正则表达式强大而且灵活,可以应用于非常 复杂的查询。 MySQL中使用REGEXP关键字指定正则表达式的字符匹配模式。下表列出了REGEXP操作符中常用字符匹配 列表。
  2. image.png
  3. 查询以特定字符或字符串开头的记录
    1. 字符‘^’匹配以特定字符或者字符串开头的文本。
    2. 在fruits表中,查询f_name字段以字母‘b’开头的记录,SQL语句如下:
    mysql> SELECT * FROM fruits WHERE f_name REGEXP '^b';  
  1. 查询以特定字符或字符串结尾的记录
    1. 字符‘$’匹配以特定字符或者字符串结尾的文本。
    2. 在fruits表中,查询f_name字段以字母‘y’结尾的记录,SQL语句如下:
       mysql> SELECT * FROM fruits WHERE f_name REGEXP 'y$';  
  1. 用符号"."来替代字符串中的任意一个字符
    1. 字符‘.’匹配任意一个字符。
    2. 在fruits表中,查询f_name字段值 包含字母‘a’与‘g’且两个字母之间只有一个字母的记录,SQL语句如下:
     mysql> SELECT * FROM fruits WHERE f_name REGEXP 'a.g';  

  1. 使用"“和”+"来匹配多个字符
    1. 星号‘
    ’匹配前面的字符任意多次,包括0次。加号‘+’匹配前面的字符至 少一次。
    2. 在fruits表中,查询f_name字段值以字母‘b’开头且‘b’后面出现字母‘a’的记录,SQL语句如下:
    mysql> SELECT * FROM fruits WHERE f_name REGEXP '^ba*';  
  3.  在fruits表中,查询f_name字段值以字母‘b’开头且‘b’后面出现字母‘a’至少一次的记录,SQL语句如下:  
     mysql> SELECT * FROM fruits WHERE f_name REGEXP '^ba+';  
  1. 匹配指定字符串
    1. 正则表达式可以匹配指定字符串,只要这个字符串在查询文本中即可,如要匹配多个 字符串,多个字符串之间使用分隔符‘|’隔开。
    2. 在fruits表中,查询f_name字段值包含字符串“on”的记录,SQL语句如下:
     mysql> SELECT * FROM fruits WHERE f_name REGEXP 'on';  
  3.  在fruits表中,查询f_name字段值包含字符串“on”或者“ap”的记录,SQL语句如下:  
     mysql> SELECT * FROM fruits WHERE f_name REGEXP 'on|ap';  
  4.  之前介绍过,LIKE运算符也可以匹配指定的字符串,但与REGEXP不同,LIKE匹配的字符串如果在文本中 间出现,则找不到它,相应的行也不会返回。REGEXP在文本内进行匹配,如果被匹配的字符串在文本中 出现,REGEXP将会找到它,相应的行也会被返回。对比结果如下所示。 
  5. 在fruits表中,使用LIKE运算符查询f_name字段值为“on”的记录,SQL语句如下:
    mysql> SELECT * FROM fruits WHERE f_name like 'on'; Empty set(0.00 sec)  
  1. 匹配指定字符中的任意一个
    1. 方括号“[]”指定一个字符集合,只匹配其中任何一个字符,即为所查找的 文本。
    2. 在fruits表中,查找f_name字段中包含字母‘o’或者‘t’的记录,SQL语句如下:
     mysql> SELECT * FROM fruits WHERE f_name REGEXP '[ot]';  
  3.  在fruits表中,查询s_id字段中包含4、5或者6的记录,SQL语句如下: 
     mysql> SELECT * FROM fruits WHERE s_id REGEXP '[456]';  
  1. 匹配指定字符以外的字符
    1. [^字符集合]” 匹配不在指定集合中的任何字符。
    2. 在fruits表中,查询f_id字段中包含字母ae和数字12以外字符的记录,SQL语句如下:
     mysql> SELECT * FROM fruits WHERE f_id REGEXP '[^a-e1-2]';  
  1. 使用{n,}或者{n,m}来指定字符串连续出现的次数
    1. “字符串{n,}”表示至少匹配n次前面的字符;“字符串 {n,m}”表示匹配前面的字符串不少于n次,不多于m次。例如,a{2,}表示字母a连续出现至少2次,也可以 大于2次;a{2,4}表示字母a连续出现最少2次,最多不能超过4次。
    2. 在fruits表中,查询f_name字段值出现字母‘x’至少2次的记录,SQL语句如下:
     mysql> SELECT * FROM fruits WHERE f_name REGEXP 'x{2,}'  
  3.  在fruits表中,查询f_name字段值出现字符串“ba”最少1次、最多3次的记录,SQL语句如下:  
     mysql> SELECT * FROM fruits WHERE f_name REGEXP 'ba{1,3}'  
排序与分页
排序数据
  1. 排序数据
  2. 使用 ORDER BY 子句排序
    1. ASC(ascend): 升序
    2. DESC(descend):降序
  3. ORDER BY 子句在SELECT语句的结尾。
  4. 单列排序
# 练习:按照salary从高到低的顺序显示员工信息
SELECT employee_id,last_name,salary
FROM employees
ORDER BY salary;

image.png


# 练习:按照salary从低到高的顺序显示员工信息
SELECT employee_id,last_name,salary
FROM employees
ORDER BY salary ASC;

image.png

SELECT employee_id,last_name,salary
FROM employees
ORDER BY salary DESC;

image.png

#2. 我们可以使用列的别名,进行排序
SELECT employee_id,salary,salary * 12 annual_sal
FROM employees
ORDER BY annual_sal;

image.png

  1. 多列排序
  2. 可以使用不在SELECT列表中的列排序。
  3. 在对多列进行排序的时候,首先排序的第一列必须有相同的列值,才会对第二列进行排序。如果第 一列数据中所有值都是唯一的,将不再对第二列进行排序。
#练习:显示员工信息,按照department_id的降序排列,salary的升序排列
SELECT employee_id,salary,department_id
FROM employees
ORDER BY department_id DESC,salary ASC;

image.png

分页
  1. 背景
  2. 背景1:查询返回的记录太多了,查看起来很不方便,怎么样能够实现分页查询呢?
  3. 背景2:表里有 4 条数据,我们只想要显示第 2、3 条数据怎么办呢?
  4. 实现规则
  5. 分页原理
    1. 所谓分页显示,就是将数据库中的结果集,一段一段显示出来需要的条件。
  6. MySQL中使用 LIMIT 实现分页
    1. 格式:
      1. LIMIT [位置偏移量,] 行数
      2. 第一个“位置偏移量”参数指示MySQL从哪一行开始显示,是一个可选参数,如果不指定“位置偏移 量”,将会从表中的第一条记录开始(第一条记录的位置偏移量是0,第二条记录的位置偏移量是 1,以此类推);第二个参数“行数”指示返回的记录条数。
--前10条记录:
SELECT * FROM 表名 LIMIT 0,10;
或者
SELECT * FROM 表名 LIMIT 10;
--第11至20条记录:
SELECT * FROM 表名 LIMIT 10,10;
--第21至30条记录:
SELECT * FROM 表名 LIMIT 20,10;

MySQL 8.0中可以使用“LIMIT 3 OFFSET 4”,意思是获取从第5条记录开始后面的3条记录,和“LIMIT 4,3;”返回的结果相同。

        1.  分页显式公式:(当前页数-1)*每页条数,每页条数  
           1.  SELECT * FROM table LIMIT(PageNo - 1)*PageSize,PageSize;  
           2.  注意:LIMIT 子句必须放在整个SELECT语句的最后! 使用 LIMIT 的好处  
        2.  约束返回结果的数量可以 减少数据表的网络传输量 ,也可以 提升查询效率 。如果我们知道返回结果只有 1 条,就可以使用 LIMIT 1 ,告诉 SELECT 语句只需要返回一条记录即可。这样的好处就是 SELECT 不需 要扫描完整的表,只需要检索到一条符合条件的记录即可返回。  
  1. 需求1:每页显示10条记录,此时显示第1页
SELECT employee_id,last_name
FROM employees
LIMIT 0,10;

image.png

  1. 需求2:每页显示10条记录,此时显示第2页
# 需求2:每页显示10条记录,此时显示第2页
SELECT employee_id,last_name
FROM employees
LIMIT 10,10;

image.png

  1. 需求3:每页显示10条记录,此时显示第3页
# 需求3:每页显示10条记录,此时显示第3页
SELECT employee_id,last_name
FROM employees
LIMIT 20,10;

image.png

  1. 拓展
  2. WHERE … ORDER BY …LIMIT 声明顺序如下:
# LIMIT的格式: 严格来说:LIMIT 位置偏移量,条目数
# 结构"LIMIT 0,条目数" 等价于 "LIMIT 条目数"

SELECT employee_id,last_name,salary
FROM employees
WHERE salary > 6000
ORDER BY salary DESC
Limit 0,10;

image.png

  1. 在不同的 DBMS 中使用的关键字可能不同。在 MySQL、PostgreSQL、MariaDB 和 SQLite 中使用 LIMIT 关 键字,而且需要放到 SELECT 语句的最后面。
    1. 如果是 SQL Server 和 Access,需要使用 TOP 关键字,比如:
    1. SELECT TOP 5 name, hp_max FROM heros ORDER BY hp_max DESC
  2.  如果是 DB2,使用 FETCH FIRST 5 ROWS ONLY 这样的关键字:  
     1.  SELECT name, hp_max FROM heros ORDER BY hp_max DESC FETCH FIRST 5 ROWS ONLY  
  3.  如果是 Oracle,你需要基于 ROWNUM 来统计行数:  
     1.  SELECT rownum,last_name,salary FROM employees WHERE rownum < 5 ORDER BY salary DESC;  
  4.  需要说明的是,这条语句是先取出来前 5 条数据行,然后再按照 hp_max 从高到低的顺序进行排序。但 这样产生的结果和上述方法的并不一样。我会在后面讲到子查询,你可以使用  
SELECT rownum, last_name,salary
FROM (
SELECT last_name,salary
FROM employees
ORDER BY salary DESC)
WHERE rownum < 10;
练习
  1. 练习:表里有107条数据,我们只想要显示第 32、33 条数据怎么办呢?
SELECT employee_id,last_name
FROM employees
LIMIT 31,2;

SELECT employee_id,last_name
FROM employees
LIMIT 2 OFFSET 31;

image.png

  1. 查询员工表中工资最高的员工信息
#练习:查询员工表中工资最高的员工信息
SELECT employee_id,last_name,salary
FROM employees
ORDER BY salary DESC
#limit 0,1
LIMIT 1;

image.png

  1. 查询员工的姓名和部门号和年薪,按年薪降序,按姓名升序显示
#1. 查询员工的姓名和部门号和年薪,按年薪降序,按姓名升序显示 

SELECT last_name,department_id,salary * 12 annual_salary
FROM employees
ORDER BY annual_salary DESC,last_name ASC;

image.png

  1. 选择工资不在 8000 到 17000 的员工的姓名和工资,按工资降序,显示第21到40位置的数据
SELECT last_name,salary
FROM employees
WHERE salary NOT BETWEEN 8000 AND 17000
ORDER BY salary DESC
LIMIT 20,20;

image.png

  1. 查询邮箱中包含 e 的员工信息,并先按邮箱的字节数降序,再按部门号升序
SELECT employee_id,last_name,email,department_id
FROM employees
#where email like '%e%'
WHERE email REGEXP '[e]'
ORDER BY LENGTH(email) DESC,department_id;

image.png

多表查询

多表查询,也称为关联查询,指两个或更多个表一起完成查询操作。 前提条件:这些一起查询的表之间是有关系的(一对一、一对多),它们之间一定是有关联字段,这个 关联字段可能建立了外键,也可能没有建立外键。比如:员工表和部门表,这两个表依靠“部门编号”进 行关联。

一个案例引发的多表连接
  1. 案例说明

image.png
image.png

#案例:查询员工的姓名及其部门名称
SELECT last_name, department_name
FROM employees, departments;

image.png
image.png
分析错误情况:

SELECT COUNT(employee_id) FROM employees;
#输出107行
SELECT COUNT(department_id)FROM departments;
#输出27行
SELECT 107*27 FROM dual;
我们把上述多表查询中出现的问题称为:笛卡尔积的错误。  
  1. 笛卡尔积(或交叉连接)的理解
  2. 笛卡尔乘积是一个数学运算。假设我有两个集合 X 和 Y,那么 X 和 Y 的笛卡尔积就是 X 和 Y 的所有可能 组合,也就是第一个对象来自于 X,第二个对象来自于 Y 的所有可能。组合的个数即为两个集合中元素 个数的乘积数

image.png

  1.  SQL92中,笛卡尔积也称为 交叉连接 ,英文是 CROSS JOIN 。在 SQL99 中也是使用 CROSS JOIN表示交 叉连接。它的作用就是可以把任意表进行连接,即使这两张表不相关。在MySQL中如下情况会出现笛卡 尔积:  
#查询员工姓名和所在部门名称
SELECT last_name,department_name FROM employees,departments;
SELECT last_name,department_name FROM employees CROSS JOIN departments;
SELECT last_name,department_name FROM employees INNER JOIN departments;
SELECT last_name,department_name FROM employees JOIN departments;

  1. 案例分析与问题解决
  2. 笛卡尔积的错误会在下面条件下产生:
    1. 省略多个表的连接条件(或关联条件)
    2. 连接条件(或关联条件)无效
    3. 所有表中的所有行互相连接
  3. 为了避免笛卡尔积, 可以在 WHERE 加入有效的连接条件。
  4. 加入连接条件后,查询语法:
  5. 在 WHERE子句中写入连接条件。
SELECT table1.column, table2.column
FROM table1, table2
WHERE table1.column1 = table2.column2; #连接条件

image.png

  1. 多表查询分类讲解 分类1:等值连接 vs 非等值连
    1. 等值连接 vs 非等值连接

image.png

#4. 如果查询语句中出现了多个表中都存在的字段,则必须指明此字段所在的表。
SELECT employees.employee_id,departments.department_name,employees.department_id
FROM employees,departments
WHERE employees.`department_id` = departments.department_id;

image.png

  1. :多个连接条件与 AND 操作符

image.png
查询员工的employee_id,last_name,department_name,city

#6. 结论:如果有n个表实现多表的查询,则需要至少n-1个连接条件
#练习:查询员工的employee_id,last_name,department_name,city
SELECT e.employee_id,e.last_name,d.department_name,l.city,e.department_id,l.location_id
FROM employees e,departments d,locations l
WHERE e.`department_id` = d.`department_id`
AND d.`location_id` = l.`location_id`;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 表的别名
    1. 使用别名可以简化查询。
    2. 列名前使用表名前缀可以提高查询效率
SELECT e.employee_id, e.last_name, e.department_id,
d.department_id, d.location_id
FROM employees e , departments d
WHERE e.department_id = d.department_id;

image.png

  1. 连接多个表

image.png
连接 n个表,至少需要n-1个连接条件。比如,连接三个表,至少需要两个连接条件。

  1. 查询出公司员工的 last_name,department_name, city

image.png

多表查询分类讲解
  1. 非等值连接
    1. image.png
SELECT e.last_name,e.salary,j.grade_level
FROM employees e,job_grades j
#where e.`salary` between j.`lowest_sal` and j.`highest_sal`;
WHERE e.`salary` >= j.`lowest_sal` AND e.`salary` <= j.`highest_sal`;
  1. image.png
  2. 自连接 vs 非自连接
    1. image.png
SELECT CONCAT(worker.last_name ,' works for '
, manager.last_name)
FROM employees worker, employees manager
WHERE worker.manager_id = manager.employee_id ;

image.png
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  2.  内连接 vs 外连接  
     1. ![image.png](https://img-blog.csdnimg.cn/img_convert/0b0d845cbee1b597f8ba88b7f08fe2e2.png)
  3.  内连接: 合并具有同一列的两个以上的表的行, 结果集中不包含一个表与另一个表不匹配的行 
  4. 外连接: 两个表在连接过程中除了返回满足连接条件的行以外还返回左(或右)表中不满足条件的 行 ,这种连接称为左(或右) 外连接。没有匹配的行时, 结果表中相应的列为空(NULL)。 
  5. 如果是左外连接,则连接条件中左边的表也称为 主表 ,右边的表称为 从表 。 如果是右外连接,则连接条件中右边的表也称为 主表 ,左边的表称为 从表 。  
SQL99语法实现多表查询
  1. 基本语法
  2. 使用JOIN…ON子句创建连接的语法结构:
SELECT table1.column, table2.column,table3.column
FROM table1
JOIN table2 ON table1 和 table2 的连接条件
JOIN table3 ON table2 和 table3 的连接条件
  1. 它的嵌套逻辑类似我们使用的 FOR 循环:
for t1 in table1:
for t2 in table2:
if condition1:
for t3 in table3:
if condition2:
output t1 + t2 + t3
  1. SQL99 采用的这种嵌套结构非常清爽、层次性更强、可读性更强,即使再多的表进行连接也都清晰 可见。如果你采用 SQL92,可读性就会大打折扣。
  2. 语法说明:
    1. 可以使用 ON 子句指定额外的连接条件。
    2. 这个连接条件是与其它条件分开的。
    3. ON 子句使语句具有更高的易读性。
    4. 关键字 JOIN、INNER JOIN、CROSS JOIN 的含义是一样的,都表示内连接
  3. 内连接(INNER JOIN)的实现
  4. 语法
SELECT 字段列表
FROM AINNER JOIN BON 关联条件
WHERE 等其他子句;

SELECT e.employee_id, e.last_name, e.department_id,
d.department_id, d.location_id
FROM employees e JOIN departments d
ON (e.department_id = d.department_id);

image.png

SELECT last_name,department_name,city
FROM employees e JOIN departments d
ON e.`department_id` = d.`department_id`
JOIN locations l
ON d.`location_id` = l.`location_id`;

image.png

  1. 外连接(OUTER JOIN)的实现
  2. 左外连接
  3. 语法:
#实现查询结果是A
SELECT 字段列表
FROM ALEFT JOIN BON 关联条件
WHERE 等其他子句;

  1. 举例:
SELECT e.last_name, e.department_id, d.department_name
FROM employees e
LEFT OUTER JOIN departments d
ON (e.department_id = d.department_id) ;

image.png

  1. 右外连接
#实现查询结果是B
SELECT 字段列表
FROM ARIGHT JOIN BON 关联条件
WHERE 等其他子句;
SELECT e.last_name, e.department_id, d.department_name
FROM employees e
RIGHT OUTER JOIN departments d
ON (e.department_id = d.department_id) ;

image.png

  1. 满外连接
    1. 满外连接的结果 = 左右表匹配的数据 + 左表没有匹配到的数据 + 右表没有匹配到的数据。
    2. SQL99是支持满外连接的。使用FULL JOIN 或 FULL OUTER JOIN来实现。
    3. 需要注意的是,MySQL不支持FULL JOIN,但是可以用 LEFT JOIN UNION RIGHT join代替。
    4. #满外连接:mysql不支持FULL OUTER JOIN
UNION的使用
  1. 合并查询结果
  2. 利用UNION关键字,可以给出多条SELECT语句,并将它们的结果组合成单个结果集。合并 时,两个表对应的列数和数据类型必须相同,并且相互对应。各个SELECT语句之间使用UNION或UNION ALL关键字分隔。
SELECT column,... FROM table1
UNION [ALL]
SELECT column,... FROM table2

  1. UNION操作符

image.png
UNION 操作符返回两个查询的结果集的并集,去除重复记录。

  1. UNION ALL操作符

image.png
UNION ALL操作符返回两个查询的结果集的并集。对于两个结果集的重复部分,不去重。
注意:执行UNION ALL语句时所需要的资源比UNION语句少。如果明确知道合并数据后的结果数据 不存在重复数据,或者不需要去除重复的数据,则尽量使用UNION ALL语句,以提高数据查询的效 率。
查询部门编号>90或邮箱包含a的员工信息

#方式1
SELECT * FROM employees WHERE email LIKE '%a%' OR department_id>90;

#方式2
SELECT * FROM employees WHERE email LIKE '%a%'
UNION
SELECT * FROM employees WHERE department_id>90;

查询中国用户中男性的信息以及美国用户中年男性的用户信息

SELECT id,cname FROM t_chinamale WHERE csex='男'
UNION ALL
SELECT id,tname FROM t_usmale WHERE tGender='male';
7种SQL JOINS的实现

image.png

  1. 代码实现
  2. image.png
#中图:内连接 AB
SELECT employee_id,last_name,department_name
FROM employees e JOIN departments d
ON e.`department_id` = d.`department_id`;

image.png

  1. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
#左上图:左外连接
SELECT employee_id,last_name,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`;

image.png

  1. image.png
#右上图:右外连接
SELECT employee_id,last_name,department_name
FROM employees e RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. image.png
#左中图:A - AB
SELECT employee_id,last_name,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE d.`department_id` IS NULL

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
#右中图:B-AB
SELECT employee_id,last_name,department_name
FROM employees e RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE e.`department_id` IS NULL

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. image.png
#左下图:满外连接
# 左中图 + 右上图 AB
SELECT employee_id,last_name,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE d.`department_id` IS NULL
UNION ALL #没有去重操作,效率高
SELECT employee_id,last_name,department_name
FROM employees e RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`;

# 方式2:左中图 UNION ALL 右上图

SELECT employee_id,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE d.`department_id` IS NULL
UNION ALL
SELECT employee_id,department_name
FROM employees e RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
image.png

  1. image.png
#右下图
#左中图 + 右中图 AB- AB 或者 (A - AB) ∪ (B - ABSELECT employee_id,last_name,department_name
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE d.`department_id` IS NULL
UNION ALL
SELECT employee_id,last_name,department_name
FROM employees e RIGHT JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE e.`department_id` IS NULL

image.png

  1. 语法格式小结
#实现A - AB
select 字段列表
from A表 left join B表
on 关联条件
where 从表关联字段 is null and 等其他子句;
#实现B - AB
select 字段列表
from A表 right join B表
on 关联条件
where 从表关联字段 is null and 等其他子句;

#实现查询结果是AB
#用左外的A,union 右外的B
select 字段列表
from A表 left join B表
on 关联条件
where 等其他子句
union
select 字段列表
from A表 right join B表
on 关联条件
where 等其他子句;
#实现AB - AB(A - AB) ∪ (B - AB)
#使用左外的 (A - AB) union 右外的(B - AB)
select 字段列表
from A表 left join B表
on 关联条件
where 从表关联字段 is null and 等其他子句
union
select 字段列表
from A表 right join B表
on 关联条件
where 从表关联字段 is null and 等其他子句
SQL99语法新特性
  1. 自然连接
  2. SQL99 在 SQL92 的基础上提供了一些特殊语法,比如 NATURAL JOIN 用来表示自然连接。我们可以把 自然连接理解为 SQL92 中的等值连接。它会帮你自动查询两张连接表中 所有相同的字段 ,然后进行 等值 连接 。
  3. 在SQL92标准中:
SELECT employee_id,last_name,department_name
FROM employees e JOIN departments d
ON e.`department_id` = d.`department_id`
AND e.`manager_id` = d.`manager_id`;

  1. 在 SQL99 中你可以写成:
SELECT employee_id,last_name,department_name
FROM employees e NATURAL JOIN departments d;
  1. USING连接
    1. 当我们进行连接的时候,SQL99还支持使用 USING 指定数据表里的 同名字段 进行等值连接。但是只能配 合JOIN一起使用。比如:
SELECT employee_id,last_name,department_name
FROM employees e JOIN departments d
USING (department_id);
  1.  你能看出与自然连接 NATURAL JOIN 不同的是,USING 指定了具体的相同的字段名称,你需要在 USING 的括号 () 中填入要指定的同名字段。同时使用 JOIN...USING 可以简化 JOIN ON 的等值连接。它与下 面的 SQL 查询结果是相同的:  
SELECT employee_id,last_name,department_name
FROM employees e ,departments d
WHERE e.department_id = d.department_id;
章节小结
  1. 表连接的约束条件可以有三种方式:WHERE, ON, USING
  2. WHERE:适用于所有关联查询
  3. ON :只能和JOIN一起使用,只能写关联条件。虽然关联条件可以并到WHERE中和其他条件一起 写,但分开写可读性更好。
  4. USING:只能和JOIN一起使用,而且要求两个关联字段在关联表中名称一致,而且只能表示关联字 段值相等
#关联条件
#把关联条件写在where后面
SELECT last_name,department_name
FROM employees,departments
WHERE employees.department_id = departments.department_id;
#把关联条件写在on后面,只能和JOIN一起使用
SELECT last_name,department_name
FROM employees INNER JOIN departments
ON employees.department_id = departments.department_id;
SELECT last_name,department_name
FROM employees CROSS JOIN departments
ON employees.department_id = departments.department_id;
SELECT last_name,department_name
FROM employees JOIN departments
ON employees.department_id = departments.department_id;
#把关联字段写在using()中,只能和JOIN一起使用
#而且两个表中的关联字段必须名称相同,而且只能表示=
#查询员工姓名与基本工资
SELECT last_name,job_title
FROM employees INNER JOIN jobs USING(job_id);
#n张表关联,需要n-1个关联条件
#查询员工姓名,基本工资,部门名称
SELECT last_name,job_title,department_name FROM employees,departments,jobs
WHERE employees.department_id = departments.department_id
AND employees.job_id = jobs.job_id;
SELECT last_name,job_title,department_name
FROM employees INNER JOIN departments INNER JOIN jobs
ON employees.department_id = departments.department_id
AND employees.job_id = jobs.job_id;

我们要 控制连接表的数量 。多表连接就相当于嵌套 for 循环一样,非常消耗资源,会让 SQL 查询性能下 降得很严重,因此不要连接不必要的表。在许多 DBMS 中,也都会有最大连接表的限制。
在正式开始讲连接表的种类时,我们首先需要知道 SQL 存在不同版本的标准规范,因为不同规范下的表 连接操作是有区别的。 SQL 有两个主要的标准,分别是 SQL92 和 SQL99 。92 和 99 代表了标准提出的时间,SQL92 就是 92 年 提出的标准规范。当然除了 SQL92 和 SQL99 以外,还存在 SQL-86、SQL-89、SQL:2003、SQL:2008、 SQL:2011 和 SQL:2016 等其他的标准。 这么多标准,到底该学习哪个呢?实际上最重要的 SQL 标准就是 SQL92 和 SQL99。一般来说 SQL92 的 形式更简单,但是写的 SQL 语句会比较长,可读性较差。而 SQL99 相比于 SQL92 来说,语法更加复杂, 但可读性更强。我们从这两个标准发布的页数也能看出,SQL92 的标准有 500 页,而 SQL99 标准超过了 1000 页。实际上从 SQL99 之后,很少有人能掌握所有内容,因为确实太多了。就好比我们使用 Windows、Linux 和 Office 的时候,很少有人能掌握全部内容一样。我们只需要掌握一些核心的功能,满 足日常工作的需求即可。 SQL92 和 SQL99 是经典的 SQL 标准,也分别叫做 SQL-2 和 SQL-3 标准。也正是在这两个标准发布之 后,SQL 影响力越来越大,甚至超越了数据库领域。现如今 SQL 已经不仅仅是数据库领域的主流语言, 还是信息领域中信息处理的主流语言。在图形检索、图像检索以及语音检索中都能看到 SQL 语言的使 用 .

练习
  1. 显示所有员工的姓名,部门号和部门名称。
# 1.显示所有员工的姓名,部门号和部门名称。
SELECT e.last_name,e.department_id,d.department_name
FROM employees e LEFT OUTER JOIN departments d
ON e.`department_id` = d.`department_id

image.png

  1. 查询90号部门员工的job_id和90号部门的location_id
# 2.查询90号部门员工的job_id和90号部门的location_id

SELECT e.job_id,d.location_id
FROM employees e JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE d.`department_id` = 90;

image.png

  1. 选择所有有奖金的员工的 last_name , department_name , location_id , city
# 3.选择所有有奖金的员工的 last_name , department_name , location_id , city
SELECT e.last_name ,e.`commission_pct`, d.department_name , d.location_id , l.city
FROM employees e LEFT JOIN departments d
ON e.`department_id` = d.`department_id`
LEFT JOIN locations l
ON d.`location_id` = l.`location_id`
WHERE e.`commission_pct` IS NOT NULL; #也应该是35条记录

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. .选择city在Toronto工作的员工的 last_name , job_id , department_id , department_name
# 4.选择city在Toronto工作的员工的 last_name , job_id , department_id , department_name 

SELECT e.last_name , e.job_id , e.department_id , d.department_name
FROM employees e JOIN departments d
ON e.`department_id` = d.`department_id`
JOIN locations l
ON d.`location_id` = l.`location_id`
WHERE l.`city` = 'Toronto';

image.png

  1. 查询员工所在的部门名称、部门地址、姓名、工作、工资,其中员工所在部门的部门名称为’Executive’

# 5.查询员工所在的部门名称、部门地址、姓名、工作、工资,其中员工所在部门的部门名称为’ExecutiveSELECT d.department_name,l.street_address,e.last_name,e.job_id,e.salary
FROM departments d LEFT JOIN employees e
ON e.`department_id` = d.`department_id`
LEFT JOIN locations l
ON d.`location_id` = l.`location_id`
WHERE d.`department_name` = 'Executive';

image.png


SELECT emp.last_name "employees",emp.employee_id "Emp#",mgr.last_name "manager", mgr.employee_id "Mgr#"
FROM employees emp LEFT JOIN employees mgr
ON emp.manager_id = mgr.employee_id;
  1. 查询哪些部门没有员工
# 7.查询哪些部门没有员工

SELECT d.department_id
FROM departments d LEFT JOIN employees e
ON d.`department_id` = e.`department_id`
WHERE e.`department_id` IS NULL;

image.png

  1. 查询哪个城市没有部
# 8. 查询哪个城市没有部门 

SELECT l.location_id,l.city
FROM locations l LEFT JOIN departments d
ON l.`location_id` = d.`location_id`
WHERE d.`location_id` IS NULL;

image.png

  1. 查询部门名为 Sales 或 IT 的员工信息
# 9. 查询部门名为 SalesIT 的员工信息

SELECT e.employee_id,e.last_name,e.department_id
FROM employees e JOIN departments d
ON e.`department_id` = d.`department_id`
WHERE d.`department_name` IN ('Sales','IT');

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

单行函数
函数的理解
  1. 什么是函数
  2. 函数在计算机语言的使用中贯穿始终,函数的作用是什么呢?它可以把我们经常使用的代码封装起来, 需要的时候直接调用即可。这样既 提高了代码效率 ,又 提高了可维护性 。在 SQL 中我们也可以使用函数 对检索出来的数据进行函数操作。使用这些函数,可以极大地 提高用户对数据库的管理效率 。
  3. image.png
  4. 从函数定义的角度出发,我们可以将函数分成 内置函数 和 自定义函数 。在 SQL 语言中,同样也包括了 内置函数和自定义函数。内置函数是系统内置的通用函数,而自定义函数是我们根据自己的需要编写 的,本章及下一章讲解的是 SQL 的内置函数。
  5. 不同DBMS函数的差异
    1. 我们在使用 SQL 语言的时候,不是直接和这门语言打交道,而是通过它使用不同的数据库软件,即 DBMS。DBMS 之间的差异性很大,远大于同一个语言不同版本之间的差异。实际上,只有很少的函数是 被 DBMS 同时支持的。比如,大多数 DBMS 使用(||)或者(+)来做拼接符,而在 MySQL 中的字符串拼 接函数为concat()。大部分 DBMS 会有自己特定的函数,这就意味着采用 SQL 函数的代码可移植性是很 差的,因此在使用函数的时候需要特别注意
  6. MySQL的内置函数及分类
    1. MySQL提供了丰富的内置函数,这些函数使得数据的维护与管理更加方便,能够更好地提供数据的分析 与统计功能,在一定程度上提高了开发人员进行数据分析与统计的效率。 MySQL提供的内置函数从 实现的功能角度 可以分为数值函数、字符串函数、日期和时间函数、流程控制 函数、加密与解密函数、获取MySQL信息函数、聚合函数等。这里,我将这些丰富的内置函数再分为两 类: 单行函数 、 聚合函数(或分组函数) 。
    2. 两种SQL函数
    1. image.png
  3.  单行函数  
     1.  操作数据对象 
     2. 接受参数返回一个结果 
     3. 只对一行进行变换 
     4. 每行返回一个结果 
     5. 可以嵌套 
     6. 参数可以是一列或一个值  
数值函数
  1. 基本函数

image.png

SELECT
ABS(-123),ABS(32),SIGN(-23),SIGN(43),PI(),CEIL(32.32),CEILING(-43.23),FLOOR(32.32),
FLOOR(-43.23),MOD(12,5)
FROM DUAL;

image.png

#取随机数
SELECT RAND(),RAND(),RAND(10),RAND(10),RAND(-1),RAND(-1)
FROM DUAL;

image.png

#四舍五入,截断操作
SELECT ROUND(123.556),ROUND(123.456,0),ROUND(123.456,1),ROUND(123.456,2),
ROUND(123.456,-1),ROUND(153.456,-2)
FROM DUAL;

image.png

#单行函数可以嵌套
SELECT TRUNCATE(ROUND(123.456,2),0)
FROM DUAL;

image.png

  1. 角度与弧度互换函数

image.png

SELECT RADIANS(30),RADIANS(60),RADIANS(90),DEGREES(2*PI()),DEGREES(RADIANS(90))
FROM DUAL;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 三角函数

image.png
ATAN2(M,N)函数返回两个参数的反正切值。 与ATAN(X)函数相比,ATAN2(M,N)需要两个参数,例如有两个 点point(x1,y1)和point(x2,y2),使用ATAN(X)函数计算反正切值为ATAN((y2-y1)/(x2-x1)),使用ATAN2(M,N)计 算反正切值则为ATAN2(y2-y1,x2-x1)。由使用方式可以看出,当x2-x1等于0时,ATAN(X)函数会报错,而 ATAN2(M,N)函数则仍然可以计算。

#三角函数
SELECT SIN(RADIANS(30)),DEGREES(ASIN(1)),TAN(RADIANS(45)),DEGREES(ATAN(1))
FROM DUAL;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 指数与对数

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#指数和对数
SELECT POW(2,5),POWER(2,4),EXP(2)
FROM DUAL;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 进制间的转换

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#进制间的转换
SELECT BIN(10),HEX(10),OCT(10),CONV(10,10,8)
FROM DUAL;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

字符串函数

image.png
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#2. 字符串函数

SELECT ASCII('Abcdfsf'),CHAR_LENGTH('hello'),CHAR_LENGTH('我们'),
LENGTH('hello'),LENGTH('我们')
FROM DUAL;

image.png

# xxx worked for yyy
SELECT CONCAT(emp.last_name,' worked for ',mgr.last_name) "details"
FROM employees emp JOIN employees mgr
WHERE emp.`manager_id` = mgr.employee_id;

image.png

SELECT CONCAT_WS('-','hello','world','hello','beijing')
FROM DUAL;
#字符串的索引是从1开始的!
SELECT INSERT('helloworld',2,3,'aaaaa'),REPLACE('hello','lol','mmm')
FROM DUAL;

SELECT UPPER('HelLo'),LOWER('HelLo')
FROM DUAL;

SELECT last_name,salary
FROM employees
WHERE LOWER(last_name) = 'King';

SELECT LEFT('hello',2),RIGHT('hello',3),RIGHT('hello',13)
FROM DUAL;

# LPAD:实现右对齐效果
# RPAD:实现左对齐效果
SELECT employee_id,last_name,LPAD(salary,10,' ')
FROM employees;

SELECT CONCAT('---',LTRIM('    h  el  lo   '),'***'),
TRIM('oo' FROM 'ooheollo')
FROM DUAL;

SELECT REPEAT('hello',4),LENGTH(SPACE(5)),STRCMP('abc','abe')
FROM DUAL;


SELECT SUBSTR('hello',2,2),LOCATE('lll','hello')
FROM DUAL;

SELECT ELT(2,'a','b','c','d'),FIELD('mm','gg','jj','mm','dd','mm'),
FIND_IN_SET('mm','gg,mm,jj,dd,mm,gg')
FROM DUAL;

SELECT employee_id,NULLIF(LENGTH(first_name),LENGTH(last_name)) "compare"
FROM employees;

日期和时间函数

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#3.1  获取日期、时间
SELECT CURDATE(),CURRENT_DATE(),CURTIME(),NOW(),SYSDATE(),
UTC_DATE(),UTC_TIME()
FROM DUAL;

image.png

SELECT CURDATE(),CURDATE() + 0,CURTIME() + 0,NOW() + 0
FROM DUAL;

image.png

#3.2 日期与时间戳的转换
SELECT UNIX_TIMESTAMP(),UNIX_TIMESTAMP('2021-10-01 12:12:32'),
FROM_UNIXTIME(1635173853),FROM_UNIXTIME(1633061552)
FROM DUAL;

image.png

#3.3 获取月份、星期、星期数、天数等函数
SELECT YEAR(CURDATE()),MONTH(CURDATE()),DAY(CURDATE()),
HOUR(CURTIME()),MINUTE(NOW()),SECOND(SYSDATE())
FROM DUAL;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
SELECT MONTHNAME('2021-10-26'),DAYNAME('2021-10-26'),WEEKDAY('2021-10-26'),
QUARTER(CURDATE()),WEEK(CURDATE()),DAYOFYEAR(NOW()),
DAYOFMONTH(NOW()),DAYOFWEEK(NOW())
FROM DUAL;

image.png

  1. 日期与时间戳的转换

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. image.png
#3.4 日期的操作函数

SELECT EXTRACT(SECOND FROM NOW()),EXTRACT(DAY FROM NOW()),
EXTRACT(HOUR_MINUTE FROM NOW()),EXTRACT(QUARTER FROM '2021-05-12')
FROM DUAL;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. image.png
#3.5 时间和秒钟转换的函数
SELECT TIME_TO_SEC(CURTIME()),
SEC_TO_TIME(83355)
FROM DUAL;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
#3.6 计算日期和时间的函数

SELECT NOW(),DATE_ADD(NOW(),INTERVAL 1 YEAR),
DATE_ADD(NOW(),INTERVAL -1 YEAR),
DATE_SUB(NOW(),INTERVAL 1 YEAR)
FROM DUAL;

image.png


SELECT DATE_ADD(NOW(), INTERVAL 1 DAY) AS col1,DATE_ADD('2021-10-21 23:32:12',INTERVAL 1 SECOND) AS col2,
ADDDATE('2021-10-21 23:32:12',INTERVAL 1 SECOND) AS col3,
DATE_ADD('2021-10-21 23:32:12',INTERVAL '1_1' MINUTE_SECOND) AS col4,
DATE_ADD(NOW(), INTERVAL -1 YEAR) AS col5, #可以是负数
DATE_ADD(NOW(), INTERVAL '1_1' YEAR_MONTH) AS col6 #需要单引号
FROM DUAL;


SELECT ADDTIME(NOW(),20),SUBTIME(NOW(),30),SUBTIME(NOW(),'1:1:3'),DATEDIFF(NOW(),'2021-10-01'),
TIMEDIFF(NOW(),'2021-10-25 22:10:10'),FROM_DAYS(366),TO_DAYS('0000-12-25'),
LAST_DAY(NOW()),MAKEDATE(YEAR(NOW()),32),MAKETIME(10,21,23),PERIOD_ADD(20200101010101,10)
FROM DUAL;
  1. 日期的格式化与解析
    1. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
    2. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
    3. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
    4. 格式化
#格式化:
SELECT DATE_FORMAT(CURDATE(),'%Y-%M-%D'),
DATE_FORMAT(NOW(),'%Y-%m-%d'),TIME_FORMAT(CURTIME(),'%h:%i:%S'),
DATE_FORMAT(NOW(),'%Y-%M-%D %h:%i:%S %W %w %T %r')
FROM DUAL;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 格式化的逆过程
#解析:格式化的逆过程
SELECT STR_TO_DATE('2021-October-25th 11:37:30 Monday 1','%Y-%M-%D %h:%i:%S %W %w')
FROM DUAL;
流程控制函数
  1. 流程处理函数可以根据不同的条件,执行不同的处理流程,可以在SQL语句中实现不同的条件选择。 MySQL中的流程处理函数主要包括IF()、IFNULL()和CASE()函数。
  2. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
    1. IF(VALUE,VALUE1,VALUE2)
#4.流程控制函数
#4.1 IF(VALUE,VALUE1,VALUE2)

SELECT last_name,salary,IF(salary >= 6000,'高工资','低工资') "details"
FROM employees;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

SELECT last_name,commission_pct,IF(commission_pct IS NOT NULL,commission_pct,0) "details",
salary * 12 * (1 + IF(commission_pct IS NOT NULL,commission_pct,0)) "annual_sal"
FROM employees;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. IFNULL(VALUE1,VALUE2):看做是IF(VALUE,VALUE1,VALUE2)的特殊情况
#4.2 IFNULL(VALUE1,VALUE2):看做是IF(VALUE,VALUE1,VALUE2)的特殊情况
SELECT last_name,commission_pct,IFNULL(commission_pct,0) "details"
FROM employees;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. CASE WHEN … THEN …WHEN … THEN … ELSE … END
#4.3 CASE WHEN ... THEN ...WHEN ... THEN ... ELSE ... END
# 类似于java的if ... else if ... else if ... else
SELECT last_name,salary,CASE WHEN salary >= 15000 THEN '白骨精' 
			     WHEN salary >= 10000 THEN '潜力股'
			     WHEN salary >= 8000 THEN '小屌丝'
			     ELSE '草根' END "details",department_id
FROM employees;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询部门号为 10,20, 30 的员工信息,

若部门号为 10, 则打印其工资的 1.1 倍,
20 号部门, 则打印其工资的 1.2 倍,
30 号部门,打印其工资的 1.3 倍数,
其他部门,打印其工资的 1.4 倍数

SELECT employee_id,last_name,department_id,salary,CASE department_id WHEN 10 THEN salary * 1.1
								     WHEN 20 THEN salary * 1.2
								     WHEN 30 THEN salary * 1.3
								     ELSE salary * 1.4 END "details"
FROM employees;
![image.png](https://cdn.nlark.com/yuque/0/2023/png/35659086/1702366301571-cb721a47-ae3a-4b94-8553-26f319933eb7.png#averageHue=%23333231&clientId=u3c0857c8-d8ba-4&from=paste&height=233&id=ucb21230a&originHeight=406&originWidth=1129&originalType=binary&ratio=1.5&rotation=0&showTitle=false&size=38233&status=done&style=none&taskId=u8f00b0d8-085a-4f3b-8074-888a984c640&title=&width=647.6666870117188)
练习2
查询部门号为 10,20, 30 的员工信息, 
若部门号为 10, 则打印其工资的 1.1, 
20 号部门, 则打印其工资的 1.2, 
30 号部门打印其工资的 1.3 倍数

*/
SELECT employee_id,last_name,department_id,salary,CASE department_id WHEN 10 THEN salary * 1.1
								     WHEN 20 THEN salary * 1.2
								     WHEN 30 THEN salary * 1.3
								     END "details"
FROM employees
WHERE department_id IN (10,20,30);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

加密与解密函数
  1. 加密与解密函数主要用于对数据库中的数据进行加密和解密处理,以防止数据被他人窃取。这些函数在 保证数据库安全时非常有用。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#5. 加密与解密的函数
# PASSWORD()在mysql8.0中弃用。
SELECT MD5('mysql'),SHA('mysql'),MD5(MD5('mysql'))
FROM DUAL;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

MySQL信息函数

MySQL中内置了一些可以查询MySQL信息的函数,这些函数主要用于帮助数据库开发或运维人员更好地 对数据库进行维护工作。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#6. MySQL信息函数

SELECT VERSION(),CONNECTION_ID(),DATABASE(),SCHEMA(),
USER(),CURRENT_USER(),CHARSET('Google'),COLLATION('Google')
FROM DUAL;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

其他函数
  1. MySQL中有些函数无法对其进行具体的分类,但是这些函数在MySQL的开发和运维过程中也是不容忽视 的。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#7. 其他函数
#如果n的值小于或者等于0,则只保留整数部分
SELECT FORMAT(123.125,2),FORMAT(123.125,0),FORMAT(123.125,-2)
FROM DUAL;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#以“192.168.1.100”为例,计算方式为192乘以2563次方,加上168乘以2562次方,加上1乘以256,再加上100SELECT INET_ATON('192.168.1.100'),INET_NTOA(3232235876)
FROM DUAL;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


#BENCHMARK()用于测试表达式的执行效率
SELECT BENCHMARK(100000,MD5('mysql'))

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

# CONVERT():可以实现字符集的转换
# CONVERT():可以实现字符集的转换
SELECT CHARSET('Google'),CHARSET(CONVERT('Google' USING 'gbk'))
FROM DUAL;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

练习
  1. 显示系统时间(注:日期+时间)
# 1.显示系统时间(注:日期+时间)
SELECT NOW(),SYSDATE(),CURRENT_TIMESTAMP(),LOCALTIME(),LOCALTIMESTAMP() #大家只需要掌握一个函数就可以了
FROM DUAL;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询员工号,姓名,工资,以及工资提高百分之20%后的结果(new salary)
# 2.查询员工号,姓名,工资,以及工资提高百分之20%后的结果(new salary)
SELECT employee_id,last_name,salary,salary * 1.2 "new salary"
FROM employees;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. .将员工的姓名按首字母排序,并写出姓名的长度(length)
# 3.将员工的姓名按首字母排序,并写出姓名的长度(length)
SELECT last_name,LENGTH(last_name) "name_length"
FROM employees
#order by last_name asc;
ORDER BY name_length ASC;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询员工id,last_name,salary,并作为一个列输出,别名为OUT_PUT
# 4.查询员工id,last_name,salary,并作为一个列输出,别名为OUT_PUT

SELECT CONCAT(employee_id,',',last_name,',',salary) "OUT_PUT"
FROM employees;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询公司各员工工作的年数、工作的天数,并按工作年数的降序排序

# 5.查询公司各员工工作的年数、工作的天数,并按工作年数的降序排序

SELECT employee_id,DATEDIFF(CURDATE(),hire_date)/365 "worked_years",DATEDIFF(CURDATE(),hire_date) "worked_days",
TO_DAYS(CURDATE()) - TO_DAYS(hire_date) "worked_days1"
FROM employees
ORDER BY worked_years DESC;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询员工姓名,hire_date , department_id,满足以下条件:
# 6.查询员工姓名,hire_date , department_id,满足以下条件:
#雇用时间在1997年之后,department_id 为80 或 90 或110, commission_pct不为空
SELECT last_name,hire_date,department_id
FROM employees
WHERE department_id IN (80,90,110)
AND commission_pct IS NOT NULL
#and hire_date >= '1997-01-01';  #存在着隐式转换
#and  date_format(hire_date,'%Y-%m-%d') >= '1997-01-01';  # 显式转换操作,格式化:日期---> 字符串
#and  date_format(hire_date,'%Y') >= '1997';   # 显式转换操作,格式化
AND hire_date >= STR_TO_DATE('1997-01-01','%Y-%m-%d');# 显式转换操作,解析:字符串 ----> 日期

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询公司中入职超过10000天的员工姓名、入职时间

# 7.查询公司中入职超过10000天的员工姓名、入职时间
SELECT last_name,hire_date
FROM employees
WHERE DATEDIFF(CURDATE(),hire_date) >= 10000;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

# 8.做一个查询,产生下面的结果
#<last_name> earns <salary> monthly but wants <salary*3> 

SELECT CONCAT(last_name,' earns ',TRUNCATE(salary,0), ' monthly but wants ',TRUNCATE(salary * 3,0)) "Dream Salary"
FROM employees;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 使用case-when,按照下面的条件:
SELECT last_name "Last_name",job_id "Job_id",CASE job_id WHEN 'AD_PRES' THEN 'A'
							 WHEN 'ST_MAN' THEN 'B'
							 WHEN 'IT_PROG' THEN 'C'
							 WHEN 'SA_REP' THEN 'D'
							 WHEN 'ST_CLERK' THEN 'E'
							 END "Grade"
FROM employees;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

聚合函数
聚合函数介绍

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. AVG和SUM函数
  2. AVG / SUM :只适用于数值类型的字段(或变量)
#1. 常见的几个聚合函数
#1.1 AVG / SUM :只适用于数值类型的字段(或变量)

SELECT AVG(salary),SUM(salary),AVG(salary) * 107
FROM employees;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. MIN和MAX函数
    1. MAX / MIN :适用于数值类型、字符串类型、日期时间类型的字段(或变量)
#1.2 MAX / MIN :适用于数值类型、字符串类型、日期时间类型的字段(或变量)

SELECT MAX(salary),MIN(salary)
FROM employees;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. COUNT函数
  2. COUNT(*)返回表中记录总数,适用于任意数据类型。
  3. COUNT(expr) 返回expr不为空的记录总数。
  4. COUNT:
    1. 作用:计算指定字段在查询结构中出现的个数(不包含NULL值的)
#1.3 COUNT:
# ① 作用:计算指定字段在查询结构中出现的个数(不包含NULL值的)

SELECT COUNT(employee_id),COUNT(salary),COUNT(2 * salary),COUNT(1),COUNT(2),COUNT(*)
FROM employees ;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 问题:用count(),count(1),count(列名)谁好呢?
    1. 其实,对于MyISAM引擎的表是没有区别的。这种引擎内部有一计数器在维护着行数。 Innodb引擎的表用count(
    ),count(1)直接读行数,复杂度是O(n),因为innodb真的要去数一遍。但好 于具体的count(列名)。
  2. 问题:能不能使用count(列名)替换count(*)?
    1. 不要使用 count(列名)来替代 count() , count() 是 SQL92 定义的标准统计行数的语法,跟数 据库无关,跟 NULL 和非 NULL 无关。 说明:count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。
GROUP BY
  1. 基本使用

SELECT column, group_function(column)
FROM table
[WHERE condition]
[GROUP BY group_by_expression]
[ORDER BY column];

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 在SELECT列表中所有未包含在组函数中的列都应该包含在 GROUP BY子句中
SELECT department_id, AVG(salary)
FROM employees
GROUP BY department_id ;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
包含在 GROUP BY 子句中的列不必包含在SELECT 列表中

SELECT AVG(salary)
FROM employees
GROUP BY department_id ;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 使用多个列分组

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 3 GROUP BY中使用WITH ROLLUP
  2. 使用 WITH ROLLUP 关键字之后,在所有查询出的分组记录之后增加一条记录,该记录计算查询出的所 有记录的总和,即统计记录数量。
SELECT department_id,AVG(salary)
FROM employees
WHERE department_id > 80
GROUP BY department_id WITH ROLLUP;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#需求:查询各个department_id,job_id的平均工资
#方式1:
SELECT department_id,job_id,AVG(salary)
FROM employees
GROUP BY  department_id,job_id;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 需求:查询各个部门的平均工资,按照平均工资升序排列
#需求:查询各个部门的平均工资,按照平均工资升序排列
SELECT department_id,AVG(salary) avg_sal
FROM employees
GROUP BY department_id
ORDER BY avg_sal ASC;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

HAVING
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  1. 行已经被分组。
  2. 使用了聚合函数。
  3. 满足HAVING 子句中条件的分组将被显示。
  4. HAVING 不能单独使用,必须要跟 GROUP BY 一起使用。
#3. HAVING的使用 (作用:用来过滤数据的)
#练习:查询各个部门中最高工资比10000高的部门信息
#错误的写法:
SELECT department_id,MAX(salary)
FROM employees
WHERE MAX(salary) > 10000
GROUP BY department_id;
#正确的写法:
SELECT department_id,MAX(salary)
FROM employees
GROUP BY department_id
HAVING MAX(salary) > 10000;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询部门id为10,20,30,40这4个部门中最高工资比10000高的部门信息

#练习:查询部门id为10,20,30,40这4个部门中最高工资比10000高的部门信息
#方式1:推荐,执行效率高于方式2.
SELECT department_id,MAX(salary)
FROM employees
WHERE department_id IN (10,20,30,40)
GROUP BY department_id
HAVING MAX(salary) > 10000;

#方式2:
SELECT department_id,MAX(salary)
FROM employees
GROUP BY department_id

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 非法使用聚合函数 : 不能在 WHERE 子句中使用聚合函数。如下:
SELECT department_id, AVG(salary)
FROM employees
WHERE AVG(salary) > 8000
GROUP BY department_id;
  1. WHERE和HAVING的对比
  2. 区别1:WHERE 可以直接使用表中的字段作为筛选条件,但不能使用分组中的计算函数作为筛选条件; HAVING 必须要与 GROUP BY 配合使用,可以把分组计算的函数和分组字段作为筛选条件。
    1. 这决定了,在需要对数据进行分组统计的时候,HAVING 可以完成 WHERE 不能完成的任务。这是因为, 在查询语法结构中,WHERE 在 GROUP BY 之前,所以无法对分组结果进行筛选。HAVING 在 GROUP BY 之 后,可以使用分组字段和分组中的计算函数,对分组的结果集进行筛选,这个功能是 WHERE 无法完成 的。另外,WHERE排除的记录不再包括在分组中。
  3. 区别2:如果需要通过连接从关联表中获取需要的数据,WHERE 是先筛选后连接,而 HAVING 是先连接 后筛选。
    1. 这一点,就决定了在关联查询中,WHERE 比 HAVING 更高效。因为 WHERE 可以先筛选,用一 个筛选后的较小数据集和关联表进行连接,这样占用的资源比较少,执行效率也比较高。HAVING 则需要 先把结果集准备好,也就是用未被筛选的数据集进行关联,然后对这个大的数据集进行筛选,这样占用 的资源就比较多,执行效率也较低。
  4. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  5. 开发中的选择:
    1. WHERE 和 HAVING 也不是互相排斥的,我们可以在一个查询里面同时使用 WHERE 和 HAVING。包含分组 统计函数的条件用 HAVING,普通条件用 WHERE。这样,我们就既利用了 WHERE 条件的高效快速,又发 挥了 HAVING 可以使用包含分组统计函数的查询条件的优点。当数据量特别大的时候,运行效率会有很 大的差别。
SELECT的执行过程
  1. 查询的结构
#方式1:
SELECT ...,....,...
FROM ...,...,....
WHERE 多表的连接条件
AND 不包含组函数的过滤条件
GROUP BY ...,...
HAVING 包含组函数的过滤条件
ORDER BY ... ASC/DESC
LIMIT ...,...
#方式2:
SELECT ...,....,...
FROM ... JOIN ...
ON 多表的连接条件
JOIN ...
ON ...
WHERE 不包含组函数的过滤条件
AND/OR 不包含组函数的过滤条件
GROUP BY ...,...
HAVING 包含组函数的过滤条件
ORDER BY ... ASC/DESC
LIMIT ...,...
#其中:
#(1)from:从哪些表中筛选
#(2)on:关联多表查询时,去除笛卡尔积
#(3)where:从表中筛选的条件
#(4)group by:分组依据
#(5)having:在统计结果中再次筛选
#(6)order by:排序
#(7)limit:分页

  1. SELECT执行顺序
  2. 关键字的顺序是不能颠倒的:
    1. SELECT … FROM … WHERE … GROUP BY … HAVING … ORDER BY … LIMIT…
  3. SELECT 语句的执行顺序(在 MySQL 和 Oracle 中,SELECT 执行顺序基本相同):
    1. FROM -> WHERE -> GROUP BY -> HAVING -> SELECT 的字段 -> DISTINCT -> ORDER BY -> LIMIT
  4. 比如你写了一个 SQL 语句,那么它的关键字顺序和执行顺序是下面这样的:
SELECT DISTINCT player_id, player_name, count(*) as num # 顺序 5
FROM player JOIN team ON player.team_id = team.team_id # 顺序 1
WHERE height > 1.80 # 顺序 2
GROUP BY player.team_id # 顺序 3
HAVING num > 2 # 顺序 4
ORDER BY num DESC # 顺序 6
LIMIT 2 # 顺序 7

在 SELECT 语句执行这些步骤的时候,每个步骤都会产生一个 虚拟表 ,然后将这个虚拟表传入下一个步 骤中作为输入。需要注意的是,这些步骤隐含在 SQL 的执行过程中,对于我们来说是不可见的。

  1. SQL 的执行原理
  2. SELECT 是先执行 FROM 这一步的。在这个阶段,如果是多张表联查,还会经历下面的几个步骤:
    1. 首先先通过 CROSS JOIN 求笛卡尔积,相当于得到虚拟表 vt(virtual table)1-1;
    2. 通过 ON 进行筛选,在虚拟表 vt1-1 的基础上进行筛选,得到虚拟表 vt1-2;
    3. 添加外部行。如果我们使用的是左连接、右链接或者全连接,就会涉及到外部行,也就是在虚拟 表 vt1-2 的基础上增加外部行,得到虚拟表 vt1-3
  3. 当然如果我们操作的是两张以上的表,还会重复上面的步骤,直到所有表都被处理完为止。这个过程得 到是我们的原始数据。
  4. 当我们拿到了查询数据表的原始数据,也就是最终的虚拟表 vt1 ,就可以在此基础上再进行 WHERE 阶 段 。在这个阶段中,会根据 vt1 表的结果进行筛选过滤,得到虚拟表 vt2 。
  5. 然后进入第三步和第四步,也就是 GROUP 和 HAVING 阶段 。在这个阶段中,实际上是在虚拟表 vt2 的 基础上进行分组和分组过滤,得到中间的虚拟表 vt3 和 vt4 。
  6. 当我们完成了条件筛选部分之后,就可以筛选表中提取的字段,也就是进入到 SELECT 和 DISTINCT 阶段 。
  7. 首先在 SELECT 阶段会提取想要的字段,然后在 DISTINCT 阶段过滤掉重复的行,分别得到中间的虚拟表 vt5-1 和 vt5-2 。
  8. 当我们提取了想要的字段数据之后,就可以按照指定的字段进行排序,也就是 ORDER BY 阶段 ,得到 虚拟表 vt6 。
  9. 最后在 vt6 的基础上,取出指定行的记录,也就是 LIMIT 阶段 ,得到最终的结果,对应的是虚拟表 vt7 。
  10. 当然我们在写 SELECT 语句的时候,不一定存在所有的关键字,相应的阶段就会省略。
  11. 同时因为 SQL 是一门类似英语的结构化查询语言,所以我们在写 SELECT 语句的时候,还要注意相应的 关键字顺序,所谓底层运行的原理,就是我们刚才讲到的执行顺序。
练习
  1. #2.查询公司员工工资的最大值,最小值,平均值,总和
#2.查询公司员工工资的最大值,最小值,平均值,总和
SELECT MAX(salary) max_sal ,MIN(salary) mim_sal,AVG(salary) avg_sal,SUM(salary) sum_sal
FROM employees;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询各job_id的员工工资的最大值,最小值,平均值,总和
#3.查询各job_id的员工工资的最大值,最小值,平均值,总和

SELECT job_id,MAX(salary),MIN(salary),AVG(salary),SUM(salary)
FROM employees
GROUP BY job_id;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 选择具有各个job_id的员工人数
#4.选择具有各个job_id的员工人数
SELECT job_id,COUNT(*)
FROM employees
GROUP BY job_id;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询员工最高工资和最低工资的差距(DIFFERENCE)
# 5.查询员工最高工资和最低工资的差距(DIFFERENCE)  #DATEDIFF
SELECT MAX(salary) - MIN(salary) "DIFFERENCE"
FROM employees;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询各个管理者手下员工的最低工资,其中最低工资不能低于6000,没有管理者的员工不计算在内
# 查询各个管理者手下员工的最低工资,其中最低工资不能低于6000,没有管理者的员工不计算在内

SELECT manager_id,MIN(salary)
FROM employees
WHERE manager_id IS NOT NULL
GROUP BY manager_id
HAVING MIN(salary) >= 6000;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询所有部门的名字,location_id,员工数量和平均工资,并按平均工资降序
# 7.查询所有部门的名字,location_id,员工数量和平均工资,并按平均工资降序 

SELECT d.department_name,d.location_id,COUNT(employee_id),AVG(salary)
FROM departments d LEFT JOIN employees e
ON d.`department_id` = e.`department_id`
GROUP BY department_name,location_id


外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询每个工种、每个部门的部门名、工种名和最低工资
# 8.查询每个工种、每个部门的部门名、工种名和最低工资 

SELECT d.department_name,e.job_id,MIN(salary)
FROM departments d LEFT JOIN employees e
ON d.`department_id` = e.`department_id`
GROUP BY department_name,job_id

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

子查询
需求分析与问题解决

子查询指一个查询语句嵌套在另一个查询语句内部的查询,这个特性从MySQL 4.1开始引入。 SQL 中子查询的使用大大增强了 SELECT 查询的能力,因为很多时候查询需要从结果集中获取数据,或者 需要从同一个表中先计算得出一个数据结果,然后与这个数据结果(可能是某个标量,也可能是某个集 合)进行比较。

  1. 实际问题

   1.  现有解决方式:  
```sql
#需求:谁的工资比Abel的高?
#方式1:
SELECT salary
FROM employees
WHERE last_name = 'Abel';

SELECT last_name,salary
FROM employees
WHERE salary > 11000;

#方式2:自连接
SELECT e2.last_name,e2.salary
FROM employees e1,employees e2
WHERE e2.`salary` > e1.`salary` #多表的连接条件
AND e1.last_name = 'Abel';

#方式3:子查询
SELECT last_name,salary
FROM employees
WHERE salary > (
		SELECT salary
		FROM employees
		WHERE last_name = 'Abel'
		);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 子查询的基本使用
  2. 子查询的基本语法结构:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 子查询(内查询)在主查询之前一次执行完成。
  2. 子查询的结果被主查询(外查询)使用 。
  3. 注意事项
    1. 子查询要包含在括号内
    2. 将子查询放在比较条件的右侧
    3. 单行操作符对应单行子查询,多行操作符对应多行子查询
  4. 子查询的分类
  5. 我们按内查询的结果返回一条还是多条记录,将子查询分为 单行子查询 、 多行子查询 。
    1. 单行子查询
    1. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  2.  多行子查询  
     1. ![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fcdn.nlark.com%2Fyuque%2F0%2F2023%2Fpng%2F35659086%2F1702378996296-95a173bc-b559-4de1-ab04-ad72c36462b6.png%23averageHue%3D%2523f1e3d8%26clientId%3Ducec1da5e-aaf3-4%26from%3Dpaste%26height%3D121%26id%3Duc3f050e5%26originHeight%3D182%26originWidth%3D767%26originalType%3Dbinary%26ratio%3D1.5%26rotation%3D0%26showTitle%3Dfalse%26size%3D24778%26status%3Ddone%26style%3Dnone%26taskId%3Duda4829b0-a197-4eb7-ba6f-6f3b7e69ef0%26title%3D%26width%3D511.3333333333333&pos_id=img-14Sql1gC-1702637081186)
  1. 分类方式2:
    1. 我们按内查询是否被执行多次,将子查询划分为 相关(或关联)子查询 和 不相关(或非关联)子查询 。
    2. 子查询从数据表中查询了数据结果,如果这个数据结果只执行一次,然后这个数据结果作为主查询的条 件进行执行,那么这样的子查询叫做不相关子查询。
    3. 同样,如果子查询需要执行多次,即采用循环的方式,先从外部查询开始,每次都传入子查询进行查 询,然后再将结果反馈给外部,这种嵌套的执行方式就称为相关子查询。
单行子查询
  1. 单行比较操作符
  2. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  3. 代码示例
  4. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  5. 返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资
SELECT last_name, job_id, salary
FROM employees
WHERE job_id =
(SELECT job_id
FROM employees
WHERE employee_id = 141)
AND salary >
(SELECT salary
FROM employees
WHERE employee_id = 143);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 返回公司工资最少的员工的last_name,job_id和salary
#题目:返回公司工资最少的员工的last_name,job_id和salary

SELECT last_name,job_id,salary
FROM employees
WHERE salary = (
		SELECT MIN(salary)
		FROM employees
		);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询与141号或174号员工的manager_id和department_id相同的其他员工的employee_id, manager_id,department_id
SELECT employee_id, manager_id, department_id
FROM employees
WHERE manager_id IN
(SELECT manager_id
FROM employees
WHERE employee_id IN (174,141))
AND department_id IN
(SELECT department_id
FROM employees
WHERE employee_id IN (174,141))
AND employee_id NOT IN(174,141);

SELECT employee_id, manager_id, department_id
FROM employees
WHERE (manager_id, department_id) IN
(SELECT manager_id, department_id
FROM employees
WHERE employee_id IN (141,174))
AND employee_id NOT IN (141,174);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. HAVING 中的子查询
  2. 首先执行子查询。
  3. 向主查询中的HAVING 子句返回结果。
  4. 查询最低工资大于110号部门最低工资的部门id和其最低工资
SELECT department_id, MIN(salary)
FROM employees
GROUP BY department_id
HAVING MIN(salary) >
(SELECT MIN(salary)
FROM employees
WHERE department_id = 110);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. CASE中的子查询
  2. 显式员工的employee_id,last_name和location。其中,若员工department_id与location_id为1800 的department_id相同,则location为’Canada’,其余则为’USA’。
SELECT employee_id, last_name,
(CASE department_id
WHEN
(SELECT department_id FROM departments
WHERE location_id = 1800)
THEN 'Canada' ELSE 'USA' END) location
FROM employees;
  1. 显式员工的employee_id,last_name和location。其中,若员工department_id与location_id为1800的department_id相同,则location为’Canada’,其余则为’USA’。

SELECT employee_id,last_name,CASE department_id WHEN (SELECT department_id FROM departments WHERE location_id = 1800) THEN 'Canada'
						ELSE 'USA' END "location"
FROM employees;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 子查询中的空值问题
SELECT last_name, job_id
FROM   employees
WHERE  job_id =
                (SELECT job_id
                 FROM   employees
                 WHERE  last_name = 'Haas');

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 非法使用子查询
#4.3 非法使用子查询
#错误:Subquery returns more than 1 row
SELECT employee_id, last_name
FROM   employees
WHERE  salary =
                (SELECT   MIN(salary)
                 FROM     employees
                 GROUP BY department_id);   

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

多行子查询

也称为集合比较子查询
内查询返回多行
使用多行比较操作符

  1. 多行比较操作符

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    1. 返回其它job_id中比job_id为‘IT_PROG’部门任一工资低的员工的员工号、姓名、job_id 以及salary
#5.2举例:
# IN:
SELECT employee_id, last_name
FROM   employees
WHERE  salary IN
                (SELECT   MIN(salary)
                 FROM     employees
                 GROUP BY department_id); 

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 返回其它job_id中比job_id为‘IT_PROG’部门所有工资都低的员工的员工号、姓名、job_id以及
SELECT employee_id,last_name,job_id,salary
FROM employees
WHERE job_id <> 'IT_PROG'
AND salary < ANY (
		SELECT salary
		FROM employees
		WHERE job_id = 'IT_PROG'
		);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询平均工资最低的部门id
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary) = (
			SELECT MIN(avg_sal)
			FROM(
				SELECT AVG(salary) avg_sal
				FROM employees
				GROUP BY department_id
				) t_dept_avg_sal
			);
#方式2:
SELECT department_id
FROM employees
GROUP BY department_id
HAVING AVG(salary) <= ALL (
SELECT AVG(salary) avg_sal
FROM employees
GROUP BY department_id
)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 空值问题
SELECT last_name
FROM employees
WHERE employee_id NOT IN (
			SELECT manager_id
			FROM employees
			);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

相关子查询
  1. 相关子查询执行流程
  2. 如果子查询的执行依赖于外部查询,通常情况下都是因为子查询中的表用到了外部的表,并进行了条件 关联,因此每执行一次外部查询,子查询都要重新计算一次,这样的子查询就称之为 关联子查询 。 相关子查询按照一行接一行的顺序执行,主查询的每一行都执行一次子查询。
  3. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  4. 代码示例
  5. 查询员工中工资大于本部门平均工资的员工的last_name,salary和其department_id
    1. 相关子查询
SELECT last_name,salary,department_id
FROM employees e1
WHERE salary > (
		SELECT AVG(salary)
		FROM employees e2
		WHERE department_id = e1.`department_id`
		);
  2.  在 FROM 中使用子查询  

#方式2:在FROM中声明子查询
SELECT e.last_name,e.salary,e.department_id
FROM employees e,(
		SELECT department_id,AVG(salary) avg_sal
		FROM employees
		GROUP BY department_id) t_dept_avg_sal
WHERE e.department_id = t_dept_avg_sal.department_id
AND e.salary > t_dept_avg_sal.avg_sa

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
from型的子查询:子查询是作为from的一部分,子查询要用()引起来,并且要给这个子查询取别 名, 把它当成一张“临时的虚拟的表”来使用。

  1. 查询员工的id,salary,按照department_name 排序
SELECT employee_id,salary
FROM employees e
ORDER BY (
SELECT department_name
FROM departments d
WHERE e.`department_id` = d.`department_id`
);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 若employees表中employee_id与job_history表中employee_id相同的数目不小于2,输出这些相同 id的员工的employee_id,last_name和其job_id
SELECT e.employee_id, last_name,e.job_id
FROM employees e
WHERE 2 <= (SELECT COUNT(*)
FROM job_history
WHERE employee_id = e.employee_id);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. EXISTS 与 NOT EXISTS关键字
  2. 关联子查询通常也会和 EXISTS操作符一起来使用,用来检查在子查询中是否存在满足条件的行。
  3. 如果在子查询中不存在满足条件的行:
    1. 条件返回 FALSE
    2. 继续在子查询中查找
  4. 如果在子查询中存在满足条件的行:
    1. 不在子查询中继续查找
    2. 条件返回 TRUE
  5. NOT EXISTS关键字表示如果不存在某种条件,则返回TRUE,否则返回FALSE。
  6. 查询公司管理者的employee_id,last_name,job_id,department_id信息
    1. 方式一:
SELECT employee_id, last_name, job_id, department_id
FROM employees e1
WHERE EXISTS ( SELECT *
FROM employees e2
WHERE e2.manager_id =
e1.employee_id);
  2.  自连接  
SELECT DISTINCT e1.employee_id, e1.last_name, e1.job_id, e1.department_id
FROM employees e1 JOIN employees e2
WHERE e1.employee_id = e2.manager_id;
  3.  方式三:  
SELECT employee_id,last_name,job_id,department_id
FROM employees
WHERE employee_id IN (
SELECT DISTINCT manager_id
FROM employees
);

  1. 查询departments表中,不存在于employees表中的部门的department_id和department_name
SELECT department_id, department_name
FROM departments d
WHERE NOT EXISTS (SELECT 'X'
FROM employees
WHERE department_id = d.department_id);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 相关更新
UPDATE table1 alias1
SET column = (SELECT expression
FROM table2 alias2
WHERE alias1.column = alias2.column);

d);

使用相关子查询依据一个表中的数据更新另一个表的数据。

  1. 在employees中增加一个department_name字段,数据为员工对应的部门名称
# 2)
UPDATE employees e
SET department_name = (SELECT department_name
FROM departments d
WHERE e.department_id = d.department_i
  1. 相关删除
  2. 删除表employees中,其与emp_history表皆有的数据
DELETE FROM employees e
WHERE employee_id in
(SELECT employee_id
FROM emp_history
WHERE employee_id = e.employee_id);
. 抛一个思考题
  1. 谁的工资比Abel的高?
#方式1:自连接
SELECT e2.last_name,e2.salary
FROM employees e1,employees e2
WHERE e1.last_name = 'Abel'
AND e1.`salary` < e2.`salary`
#方式2:子查询
SELECT last_name,salary
FROM employees
WHERE salary > (
SELECT salary
FROM employees
WHERE last_name = 'Abel'
)

问题:以上两种方式有好坏之分吗?
解答:
自连接方式好! 题目中可以使用子查询,也可以使用自连接。一般情况建议你使用自连接,因为在许多 DBMS 的处理过 程中,对于自连接的处理速度要比子查询快得多。 可以这样理解:子查询实际上是通过未知表进行查询后的条件判断,而自连接是通过已知的自身数据表 进行条件判断,因此在大部分 DBMS 中都对自连接处理进行了优化。

练习
  1. 查询和Zlotkey相同部门的员工姓名和工资
#1.查询和Zlotkey相同部门的员工姓名和工资

SELECT last_name,salary
FROM employees
WHERE department_id IN (
			SELECT department_id
			FROM employees
			WHERE last_name = 'Zlotkey'
			);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询工资比公司平均工资高的员工的员工号,姓名和工资。
#2.查询工资比公司平均工资高的员工的员工号,姓名和工资。

SELECT employee_id,last_name,salary
FROM employees
WHERE salary > (
		SELECT AVG(salary)
		FROM employees
		);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 择工资大于所有JOB_ID = 'SA_MAN’的员工的工资的员工的last_name, job_id, salary
#3.选择工资大于所有JOB_ID = 'SA_MAN'的员工的工资的员工的last_name, job_id, salary

SELECT last_name,job_id,salary
FROM employees
WHERE salary > ALL(
		SELECT salary
		FROM employees
		WHERE job_id = 'SA_MAN'
		);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询和姓名中包含字母u的员工在相同部门的员工的员工号和姓名
#4.查询和姓名中包含字母u的员工在相同部门的员工的员工号和姓名

SELECT employee_id,last_name
FROM employees 
WHERE department_id IN (
			SELECT DISTINCT department_id
			FROM employees
			WHERE last_name LIKE '%u%'
			);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询在部门的location_id为1700的部门工作的员工的员工号
#5.查询在部门的location_id为1700的部门工作的员工的员工号

SELECT employee_id
FROM employees
WHERE department_id IN (
			SELECT department_id
			FROM departments
			WHERE location_id = 1700
			);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询管理者是King的员工姓名和工资
SELECT last_name,salary,manager_id
FROM employees
WHERE manager_id IN (
			SELECT employee_id
			FROM employees
			WHERE last_name = 'King'
			);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询工资最低的员工信息: last_name, salary

SELECT last_name,salary
FROM employees
WHERE salary = (
		SELECT MIN(salary)
		FROM employees
		)

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询平均工资最低的部门信息
#方式1:
SELECT *
FROM departments
WHERE department_id = (
			SELECT department_id
			FROM employees
			GROUP BY department_id
			HAVING AVG(salary ) = (
						SELECT MIN(avg_sal)
						FROM (
							SELECT AVG(salary) avg_sal
							FROM employees
							GROUP BY department_id
							) t_dept_avg_sal

						)
			);

#方式2:

SELECT *
FROM departments
WHERE department_id = (
			SELECT department_id
			FROM employees
			GROUP BY department_id
			HAVING AVG(salary ) <= ALL(
						SELECT AVG(salary)
						FROM employees
						GROUP BY department_id
						)
			);

#方式3: LIMIT

SELECT *
FROM departments
WHERE department_id = (
			SELECT department_id
			FROM employees
			GROUP BY department_id
			HAVING AVG(salary ) =(
						SELECT AVG(salary) avg_sal
						FROM employees
						GROUP BY department_id
						ORDER BY avg_sal ASC
						LIMIT 1		
						)
			);

#方式4:

SELECT d.*
FROM departments d,(
		SELECT department_id,AVG(salary) avg_sal
		FROM employees
		GROUP BY department_id
		ORDER BY avg_sal ASC
		LIMIT 0,1
		) t_dept_avg_sal
WHERE d.`department_id` = t_dept_avg_sal.department_id

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询平均工资最高的 job 信息
#方式1:
SELECT *
FROM jobs
WHERE job_id = (
		SELECT job_id
		FROM employees
		GROUP BY job_id
		HAVING AVG(salary) = (
					SELECT MAX(avg_sal)
					FROM (
						SELECT AVG(salary) avg_sal
						FROM employees
						GROUP BY job_id
						) t_job_avg_sal
					)
		);

#方式2:
SELECT *
FROM jobs
WHERE job_id = (
		SELECT job_id
		FROM employees
		GROUP BY job_id
		HAVING AVG(salary) >= ALL(
				     SELECT AVG(salary) 
				     FROM employees
				     GROUP BY job_id
				     )
		);

#方式3:
SELECT *
FROM jobs
WHERE job_id = (
		SELECT job_id
		FROM employees
		GROUP BY job_id
		HAVING AVG(salary) =(
				     SELECT AVG(salary) avg_sal
				     FROM employees
				     GROUP BY job_id
				     ORDER BY avg_sal DESC
				     LIMIT 0,1
				     )
		);

#方式4:
SELECT j.*
FROM jobs j,(
		SELECT job_id,AVG(salary) avg_sal
		FROM employees
		GROUP BY job_id
		ORDER BY avg_sal DESC
		LIMIT 0,1		
		) t_job_avg_sal
WHERE j.job_id = t_job_avg_sal.job_id

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询平均工资高于公司平均工资的部门有哪些?
#11.查询平均工资高于公司平均工资的部门有哪些?

SELECT department_id
FROM employees
WHERE department_id IS NOT NULL
GROUP BY department_id
HAVING AVG(salary) > (
			SELECT AVG(salary)
			FROM employees
			);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询出公司中所有 manager 的详细信息
#12.查询出公司中所有 manager 的详细信息

#方式1:自连接  xxx worked for yyy
SELECT DISTINCT mgr.employee_id,mgr.last_name,mgr.job_id,mgr.department_id
FROM employees emp JOIN employees mgr
ON emp.manager_id = mgr.employee_id;

#方式2:子查询

SELECT employee_id,last_name,job_id,department_id
FROM employees
WHERE employee_id IN (
			SELECT DISTINCT manager_id
			FROM employees
			);

#方式3:使用EXISTS
SELECT employee_id,last_name,job_id,department_id
FROM employees e1
WHERE EXISTS (
	       SELECT *
	       FROM employees e2
	       WHERE e1.`employee_id` = e2.`manager_id`
	     );

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#13.各个部门中 最高工资中最低的那个部门的 最低工资是多少?

#方式1:
SELECT MIN(salary)
FROM employees
WHERE department_id = (
			SELECT department_id
			FROM employees
			GROUP BY department_id
			HAVING MAX(salary) = (
						SELECT MIN(max_sal)
						FROM (
							SELECT MAX(salary) max_sal
							FROM employees
							GROUP BY department_id
							) t_dept_max_sal
						)
			);
#方式2:
SELECT MIN(salary)
FROM employees
WHERE department_id = (
			SELECT department_id
			FROM employees
			GROUP BY department_id
			HAVING MAX(salary) <= ALL (
						SELECT MAX(salary)
						FROM employees
						GROUP BY department_id
						)
			);

#方式3:
SELECT MIN(salary)
FROM employees
WHERE department_id = (
			SELECT department_id
			FROM employees
			GROUP BY department_id
			HAVING MAX(salary) = (
						SELECT MAX(salary) max_sal
						FROM employees
						GROUP BY department_id
						ORDER BY max_sal ASC
						LIMIT 0,1
						)
			);
			
#方式4:
SELECT MIN(salary)
FROM employees e,(
		SELECT department_id,MAX(salary) max_sal
		FROM employees
		GROUP BY department_id
		ORDER BY max_sal ASC
		LIMIT 0,1
		) t_dept_max_sal
WHERE e.department_id = t_dept_max_sal.department_id

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询平均工资最高的部门的 manager 的详细信息: last_name, department_id, email, salary
#14.查询平均工资最高的部门的 manager 的详细信息: last_name, department_id, email, salary
#方式1:
SELECT last_name, department_id, email, salary
FROM employees
WHERE employee_id = ANY (
			SELECT DISTINCT manager_id
			FROM employees
			WHERE department_id = (
						SELECT department_id
						FROM employees
						GROUP BY department_id
						HAVING AVG(salary) = (
									SELECT MAX(avg_sal)
									FROM (
										SELECT AVG(salary) avg_sal
										FROM employees
										GROUP BY department_id
										) t_dept_avg_sal
									)
						)
			);

#方式2:
SELECT last_name, department_id, email, salary
FROM employees
WHERE employee_id = ANY (
			SELECT DISTINCT manager_id
			FROM employees
			WHERE department_id = (
						SELECT department_id
						FROM employees
						GROUP BY department_id
						HAVING AVG(salary) >= ALL (
								SELECT AVG(salary) avg_sal
								FROM employees
								GROUP BY department_id
								)
						)
			);

#方式3:
SELECT last_name, department_id, email, salary
FROM employees
WHERE employee_id IN (
			SELECT DISTINCT manager_id
			FROM employees e,(
					SELECT department_id,AVG(salary) avg_sal
					FROM employees
					GROUP BY department_id
					ORDER BY avg_sal DESC
					LIMIT 0,1
					) t_dept_avg_sal
			WHERE e.`department_id` = t_dept_avg_sal.department_id
			);




外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询部门的部门号,其中不包括job_id是"ST_CLERK"的部门号
#15. 查询部门的部门号,其中不包括job_id是"ST_CLERK"的部门号
#方式1:
SELECT department_id
FROM departments
WHERE department_id NOT IN (
			SELECT DISTINCT department_id
			FROM employees
			WHERE job_id = 'ST_CLERK'
			);

#方式2:
SELECT department_id
FROM departments d
WHERE NOT EXISTS (
		SELECT *
		FROM employees e
		WHERE d.`department_id` = e.`department_id`
		AND e.`job_id` = 'ST_CLERK'
		);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 选择所有没有管理者的员工的last_name
选择所有没有管理者的员工的last_name

SELECT last_name
FROM employees emp
WHERE NOT EXISTS (
		SELECT *
		FROM employees mgr
		WHERE emp.`manager_id` = mgr.`employee_id`
		);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询员工号、姓名、雇用时间、工资,其中员工的管理者为 ‘De Haan’
#17.查询员工号、姓名、雇用时间、工资,其中员工的管理者为 'De Haan'
#方式1:
SELECT employee_id,last_name,hire_date,salary
FROM employees
WHERE manager_id IN (
		SELECT employee_id
		FROM employees
		WHERE last_name = 'De Haan'
		);
    #方式2:
SELECT employee_id,last_name,hire_date,salary
FROM employees e1
WHERE EXISTS (
		SELECT *
		FROM employees e2
		WHERE e1.`manager_id` = e2.`employee_id`
		AND e2.last_name = 'De Haan'
		); 

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#18.查询各部门中工资比本部门平均工资高的员工的员工号, 姓名和工资(相关子查询)

#方式1:使用相关子查询
SELECT last_name,salary,department_id
FROM employees e1
WHERE salary > (
		SELECT AVG(salary)
		FROM employees e2
		WHERE department_id = e1.`department_id`
		);
    #方式2:在FROM中声明子查询
SELECT e.last_name,e.salary,e.department_id
FROM employees e,(
		SELECT department_id,AVG(salary) avg_sal
		FROM employees
		GROUP BY department_id) t_dept_avg_sal
WHERE e.department_id = t_dept_avg_sal.department_id
AND e.salary > t_dept_avg_sal.avg_sal

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询每个部门下的部门人数大于 5 的部门名称(相关子查询)

SELECT department_name
FROM departments d
WHERE 5 < (
	   SELECT COUNT(*)
	   FROM employees e
	   WHERE d.department_id = e.`department_id`
	  );

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

#20.查询每个国家下的部门个数大于 2 的国家编号(相关子查询)

SELECT * FROM locations;

SELECT country_id
FROM locations l
WHERE 2 < (
	   SELECT COUNT(*)
	   FROM departments d
	   WHERE l.`location_id` = d.`location_id`
	 );

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建和管理表
基础知识
  1. 一条数据存储的过程
  2. 存储数据是处理数据的第一步 。只有正确地把数据存储起来,我们才能进行有效的处理和分析。否则,只 能是一团乱麻,无从下手。
  3. 那么,怎样才能把用户各种经营相关的、纷繁复杂的数据,有序、高效地存储起来呢? 在 MySQL 中, 一个完整的数据存储过程总共有 4 步,分别是创建数据库、确认字段、创建数据表、插入数据。
  4. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  5. 我们要先创建一个数据库,而不是直接创建数据表呢?
  6. 因为从系统架构的层次上看,MySQL 数据库系统从大到小依次是 数据库服务器 、 数据库 、 数据表 、数 据表的 行与列 。
  7. MySQL 数据库服务器之前已经安装。所以,我们就从创建数据库开始。
  8. 标识符命名规则
  9. 数据库名、表名不得超过30个字符,变量名限制为29个
  10. 必须只能包含 A–Z, a–z, 0–9, _共63个字符
  11. 数据库名、表名、字段名等对象名中间不要包含空格
  12. 同一个MySQL软件中,数据库不能同名;同一个库中,表不能重名;同一个表中,字段不能重名 必须保证你的字段没有和保留字、数据库系统或常用方法冲突。如果坚持使用,请在SQL语句中使 用`(着重号)引起来
  13. 保持字段名和类型的一致性:在命名字段并为其指定数据类型的时候一定要保证一致性,假如数据 类型在一个表里是整数,那在另一个表里可就别变成字符型了
  14. MySQL中的数据类型

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

创建和管理数据库
  1. 创建数据库
  2. 方式1:创建数据库
    1. CREATE DATABASE 数据库名;
  3. 方式2:创建数据库并指定字符集
    1. CREATE DATABASE 数据库名 CHARACTER SET 字符集;
  4. 方式3:判断数据库是否已经存在,不存在则创建数据库( 推荐 )
    1. CREATE DATABASE IF NOT EXISTS 数据库名;
  5. 如果MySQL中已经存在相关的数据库,则忽略创建语句,不再创建数据库。
  6. 注意:DATABASE 不能改名。一些可视化工具可以改名,它是建新库,把所有表复制到新库,再删 旧库完成的。
  7. 使用数据库
  8. 查看当前所有的数据库
    1. SHOW DATABASES; #有一个S,代表多个数据库
  9. 查看当前正在使用的数据库
    1. SELECT DATABASE(); #使用的一个 mysql 中的全局函数
  10. 查看指定库下所有的表
    1. SHOW TABLES FROM 数据库名;
  11. 查看数据库的创建信息
    1. SHOW CREATE DATABASE 数据库名; 或者: SHOW CREATE DATABASE 数据库名\G
  12. 使用/切换数据库
    1. USE 数据库名;
  13. 注意:要操作表格和数据之前必须先说明是对哪个数据库进行操作,否则就要对所有对象加上“数 据库名.”。
  14. 修改数据
  15. 更改数据库字符集
    1. ALTER DATABASE 数据库名 CHARACTER SET 字符集; #比如:gbk、utf8等
  16. 删除数据库
  17. 方式1:删除指定的数据库
    1. DROP DATABASE 数据库名;
  18. 方式2:删除指定的数据库( 推荐 )
    1. DROP DATABASE IF EXISTS 数据库名;
创建表
  1. 创建方式1
  2. 必须具备:
    1. CREATE TABLE权限
    2. 存储空间
CREATE TABLE [IF NOT EXISTS] 表名(
字段1, 数据类型 [约束条件] [默认值],
字段2, 数据类型 [约束条件] [默认值],
字段3, 数据类型 [约束条件] [默认值],
……
[表约束条件]
);
加上了IF NOT EXISTS关键字,则表示:如果当前数据库中不存在要创建的数据表,则创建数据表;
如果当前数据库中已经存在要创建的数据表,则忽略建表语句,不再创建数据表。

必须指定:
表名
列名(或字段名),数据类型,长度
可选指定:
约束条件
默认值
创建表举例1:

-- 创建表
CREATE TABLE emp (
-- int类型
emp_id INT,
-- 最多保存20个中英文字符
emp_name VARCHAR(20),
-- 总位数不超过15位
salary DOUBLE,
-- 日期类型
birthday DATE
);
DESC emp;

MySQL在执行建表语句时,将id字段的类型设置为int(11),这里的11实际上是int类型指定的显示宽度,默 认的显示宽度为11。也可以在创建数据表的时候指定数据的显示宽度。

  1. 创建方式2
  2. 使用 AS subquery 选项,将创建表和插入数据结合起来
  3. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  4. 指定的列和子查询中的列要一一对应
  5. 通过列名和默认值定义
CREATE TABLE emp1 AS SELECT * FROM employees;
CREATE TABLE emp2 AS SELECT * FROM employees WHERE 1=2; -- 创建的emp2是空表

CREATE TABLE dept80
AS
SELECT employee_id, last_name, salary*12 ANNSAL, hire_date
FROM employees
WHERE department_id = 80;

DESCRIBE dept80;
  1. 查看数据表结构
  2. 在MySQL中创建好数据表之后,可以查看数据表的结构。MySQL支持使用 DESCRIBE/DESC 语句查看数据 表结构,也支持使用 SHOW CREATE TABLE 语句查看数据表结构。 语法格式如下:
  3. SHOW CREATE TABLE 表名\G
  4. 使用SHOW CREATE TABLE语句不仅可以查看表创建时的详细语句,还可以查看存储引擎和字符编码。
修改表

修改表指的是修改数据库中已经存在的数据表的结构。
使用 ALTER TABLE 语句可以实现:
向已有的表中添加列
修改现有表中的列
删除现有表中的列
重命名现有表中的列

  1. 追加一个列
  2. ALTER TABLE 表名 ADD 【COLUMN】 字段名 字段类型 【FIRST|AFTER 字段名】;
ALTER TABLE 表名 ADDCOLUMN】 字段名 字段类型 【FIRST|AFTER 字段名】;
  1. 修改一个列
  2. 可以修改列的数据类型,长度、默认值和位置
  3. 修改字段数据类型、长度、默认值、位置的语法格式如下:
  4. ALTER TABLE 表名 MODIFY 【COLUMN】 字段名1 字段类型 【DEFAULT 默认值】【FIRST|AFTER 字段名 2】;
ALTER TABLE dept80
MODIFY last_name VARCHAR(30);

ALTER TABLE dept80
MODIFY salary double(9,2) default 1000;
  1. 重命名一个列
  2. 使用 CHANGE old_column new_column dataType子句重命名列。语法格式如下
  3. ALTER TABLE 表名 CHANGE 【column】 列名 新列名 新数据类型
ALTER TABLE dept80
CHANGE department_name dept_name varchar(15);

  1. 删除一个列
  2. 删除表中某个字段的语法格式如下:
  3. ALTER TABLE 表名 DROP 【COLUMN】字段名
ALTER TABLE dept80
DROP COLUMN job_id;
重命名表
  1. 方式一:使用RENAME
  2. RENAME TABLE emp TO myemp;
  3. 方式二:
  4. ALTER table dept RENAME [TO] detail_dept; – [TO]可以省略
  5. 必须是对象的拥有者
删除表
  1. 在MySQL中,当一张数据表 没有与其他任何数据表形成关联关系 时,可以将当前数据表直接删除。
  2. 数据和结构都被删除
  3. 所有正在运行的相关事务被提交
  4. 所有相关索引被删除
  5. 语法格式:
  6. DROP TABLE [IF EXISTS] 数据表1 [, 数据表2, …, 数据表n];
  7. IF EXISTS 的含义为:如果当前数据库中存在相应的数据表,则删除数据表;如果当前数据库中不存 在相应的数据表,则忽略删除语句,不再执行删除数据表的操作。
DROP TABLE dept80;
DROP TABLE 语句不能回滚
清空表
  1. TRUNCATE TABLE语句:
  2. 删除表中所有的数据
  3. 释放表的存储空间
TRUNCATE TABLE detail_dept;
TRUNCATE语句不能回滚,而使用 DELETE 语句删除数据,可以回滚
对比:

SET autocommit = FALSE;
DELETE FROM emp2;
#TRUNCATE TABLE emp2;
SELECT * FROM emp2;
ROLLBACK;
SELECT * FROM emp2;

阿里开发规范: 【参考】TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但 TRUNCATE 无 事务且不触发 TRIGGER,有可能造成事故,故不建议在开发代码中使用此语句。 说明:TRUNCATE TABLE 在功能上与不带 WHERE 子句的 DELETE 语句相同。

内容拓展
  1. 阿里巴巴《Java开发手册》之MySQL字段命名
  2. 【 强制 】表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只出 现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
  3. 正例:aliyun_admin,rdc_config,level3_name
  4. 反例:AliyunAdmin,rdcConfig,level_3_name
  5. 【 强制 】禁用保留字,如 desc、range、match、delayed 等,请参考 MySQL 官方保留字。
  6. 【 强制 】表必备三字段:id, gmt_create, gmt_modified。
    1. 说明:其中 id 必为主键,类型为BIGINT UNSIGNED、单表时自增、步长为 1。gmt_create, gmt_modified 的类型均为 DATETIME 类型,前者现在时表示主动式创建,后者过去分词表示被 动式更新
  7. 【 推荐 】表的命名最好是遵循 “业务名称_表的作用”。
    1. 正例:alipay_task 、 force_project、 trade_config 【 推荐 】库名与应用名称尽量一致。
  8. 【参考】合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速 度。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
** 拓展2:如何理解清空表、删除表等操作需谨慎?! **

  1. ** 表删除 操作将把表的定义和表中的数据一起删除,并且MySQL在执行删除操作时,不会有任何的确认信 息提示,因此执行删除操时应当慎重。在删除表前,最好对表中的数据进行 备份 ,这样当操作失误时可 以对数据进行恢复,以免造成无法挽回的后果。 同样的,在使用 ALTER TABLE 进行表的基本修改操作时,在执行操作过程之前,也应该确保对数据进 行完整的 备份 ,因为数据库的改变是 无法撤销 的,如果添加了一个不需要的字段,可以将其删除;相 同的,如果删除了一个需要的列,该列下面的所有数据都将会丢失。 **

拓展3:MySQL8新特性—DDL的原子化

  1. 在MySQL 8.0版本中,InnoDB表的DDL支持事务完整性,即 DDL操作要么成功要么回滚 。DDL操作回滚日志 写入到data dictionary数据字典表mysql.innodb_ddl_log(该表是隐藏的表,通过show tables无法看到) 中,用于回滚操作。通过设置参数,可将DDL操作日志打印输出到MySQL错误日志中。 分别在MySQL 5.7版本和MySQL 8.0版本中创建数据库和数据表,结果如下:
CREATE DATABASE mytest;
USE mytest;
CREATE TABLE book1(
book_id INT ,
book_name VARCHAR(255)
);
SHOW TABLES;

在MySQL 5.7版本中,测试步骤如下: 删除数据表book1和数据表book2,结果如下:

mysql> DROP TABLE book1,book2;
ERROR 1051 (42S02): Unknown table 'mytest.book2'

再次查询数据库中的数据表名称,结果如下:

mysql> SHOW TABLES;
Empty set (0.00 sec

从结果可以看出,虽然删除操作时报错了,但是仍然删除了数据表book1。
在MySQL 8.0版本中,测试步骤如下: 删除数据表book1和数据表book2,结果如下:

mysql> DROP TABLE book1,book2;
ERROR 1051 (42S02): Unknown table 'mytest.book2'

再次查询数据库中的数据表名称,结果如下:

数据处理之增删改查
插入数据
  1. 实际问题

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 方式1:VALUES的方式添加
    1. 使用这种语法一次只能向表中插入一条数据。
INSERT INTO 表名
VALUES (value1,value2,....);
	值列表中需要为表的每一个字段指定值,并且值的顺序必须和数据表中字段定义时的顺序相同。  
INSERT INTO emp1
VALUES (1,'Tom','2000-12-21',3400);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 方式2:将查询结果插入到表中
    1. 使用这种语法一次只能向表中插入一条数据。
INSERT INTO 表名(column1 [, column2,, columnn])
VALUES (value1 [,value2,, valuen]);

为表的指定字段插入数据,就是在INSERT语句中只向部分字段中插入值,而其他字段的值为表定义时的 默认值。 在 INSERT 子句中随意列出列名,但是一旦列出,VALUES中要插入的value1,…valuen需要与 column1,…columnn列一一对应。如果类型不同,将无法插入,并且MySQL会产生错误。

  1. 同时插入多条记录
    1. INSERT语句可以同时向数据表中插入多条记录,插入时指定多个值列表,每个值列表之间用逗号分隔 开,基本语法格式如下:
INSERT INTO table_name
VALUES
(value1 [,value2,, valuen]),
(value1 [,value2,, valuen]),
……
(value1 [,value2,, valuen]);

INSERT INTO table_name(column1 [, column2,, columnn])
VALUES
(value1 [,value2,, valuen]),
(value1 [,value2,, valuen]),
……
(value1 [,value2,, valuen]);

  1.  举例:  
mysql> INSERT INTO emp(emp_id,emp_name)
-> VALUES (1001,'shkstart'),
-> (1002,'atguigu'),
-> (1003,'Tom');

使用INSERT同时插入多条记录时,MySQL会返回一些在执行单行插入时没有的额外信息,这些信息的含 义如下: ● Records:表明插入的记录条数。 ● Duplicates:表明插入时被忽略的记录,原因可能是这 些记录包含了重复的主键值。 ● Warnings:表明有问题的数据值,例如发生数据类型转换。
VALUES 也可以写成 VALUE ,但是VALUES是标准写法。 字符和日期型数据应包含在单引号中。

  1. 将查询结果插入到表中
  2. INSERT还可以将SELECT语句查询的结果插入到表中,此时不需要把每一条记录的值一个一个输入,只需 要使用一条INSERT语句和一条SELECT语句组成的组合语句即可快速地从一个或多个表中向一个表中插入 多行。 基本语法格式如下:
INSERT INTO 目标表名
(tar_column1 [, tar_column2,, tar_columnn])
SELECT
(src_column1 [, src_column2,, src_columnn])
FROM 源表名
[WHERE condition]

在 INSERT 语句中加入子查询。 不必书写 VALUES 子句。 子查询中的值列表应与 INSERT 子句中的列名对应。
举例

INSERT INTO emp1(id,NAME,salary,hire_date)
#查询语句
SELECT employee_id,last_name,salary,hire_date  # 查询的字段一定要与添加到的表的字段一一对应
FROM employees
WHERE department_id IN (70,60);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

更新数据

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 使用 UPDATE 语句更新数据。语法如下:
UPDATE table_name
SET column1=value1, column2=value2,, column=valuen
[WHERE condition]

  1. 可以一次更新多条数据。
  2. 如果需要回滚数据,需要保证在DML前,进行设置:SET AUTOCOMMIT = FALSE;
  3. 使用 WHERE 子句指定需要更新的数据。
#同时修改一条数据的多个字段
UPDATE emp1
SET hire_date = CURDATE(),salary = 6000
WHERE id = 4;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
如果省略 WHERE 子句,则表中的所有数据都将被更新。

  1. 更新中的数据完整性错误
UPDATE employees
SET department_id = 55
WHERE department_id = 110;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
说明:不存在 55 号部门
将表中姓名中包含字符a的提薪20%

#题目:将表中姓名中包含字符a的提薪20%
UPDATE emp1
SET salary = salary * 1.2
WHERE NAME LIKE '%a%';

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

删除数据

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 使用 DELETE 语句从表中删除数据
  2. DELETE FROM table_name [WHERE ];
  3. table_name指定要执行删除操作的表;“[WHERE ]”为可选参数,指定删除条件,如果没有WHERE子句, DELETE语句将删除表中的所有记录。
DELETE FROM departments
WHERE department_name = 'Finance';

  1. 如果省略 WHERE 子句,则表中的全部数据将被删除
  2. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
MySQL8新特性:计算列
  1. 什么叫计算列呢?简单来说就是某一列的值是通过别的列计算得来的。例如,a列值为1、b列值为2,c列 不需要手动插入,定义a+b的结果为c的值,那么c就是计算列,是通过别的列计算得来的。 在MySQL 8.0中,CREATE TABLE 和 ALTER TABLE 中都支持增加计算列。下面以CREATE TABLE为例进行讲 解。
  2. 举例:定义数据表tb1,然后定义字段id、字段a、字段b和字段c,其中字段c为计算列,用于计算a+b的 值。 首先创建测试表tb1,语句如下:
CREATE TABLE test1(
a INT,
b INT,
c INT GENERATED ALWAYS AS (a + b) VIRTUAL  #字段c即为计算列
);
  1. 插入演示数据,语句如下:
INSERT INTO test1(a,b)
VALUES(10,20);

  1. 查询数据表tb1中的数据,结果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 更新数据中的数据,语句如下:
UPDATE test1
SET a = 100;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

综合案例
  1. 创建数据库test01_library
  2. 创建表 books,表结构如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 向books表中插入记录
  2. 不指定字段名称,插入第一条记录
# 1)不指定字段名称,插入第一条记录
INSERT INTO books
VALUES(1,'Tal of AAA','Dickes',23,'1995','novel',11);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 指定所有字段名称,插入第二记录
# 2)指定所有字段名称,插入第二记录
INSERT INTO books (id,name,`authors`,price,pubdate,note,num)
VALUES(2,'EmmaT','Jane lura',35,1993,'Joke',22);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 同时插入多条记录(剩下的所有记录)
# 3)同时插入多条记录(剩下的所有记录)
INSERT INTO books (id,name,`authors`,price,pubdate,note,num) VALUES
(3,'Story of Jane','Jane Tim',40,2001,'novel',0),
(4,'Lovey Day','George Byron',20,2005,'novel',30),
(5,'Old land','Honore Blade',30,2010,'Law',0),
(6,'The Battle','Upton Sara',30,1999,'medicine',40),
(7,'Rose Hood','Richard haggard',28,2008,'cartoon',28);

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 将小说类型(novel)的书的价格都增加5。
# 4、将小说类型(novel)的书的价格都增加5。
UPDATE books SET price=price+5 WHERE note = 'novel';

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 将名称为EmmaT的书的价格改为40,并将说明改为drama。
# 5、将名称为EmmaT的书的价格改为40,并将说明改为drama。
UPDATE books SET price=40,note='drama' WHERE name='EmmaT';

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 删除库存为0的记录。
# 6、删除库存为0的记录。
DELETE FROM books WHERE num=0;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 统计书名中包含a字母的书
# 7、统计书名中包含a字母的书
SELECT * FROM books WHERE name LIKE '%a%';

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 统计书名中包含a字母的书的数量和库存总量
# 8、统计书名中包含a字母的书的数量和库存总量
SELECT COUNT(*),SUM(num) FROM books WHERE name LIKE '%a%';

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 找出“novel”类型的书,按照价格降序排列
# 9、找出“novel”类型的书,按照价格降序排列
SELECT * FROM books WHERE note = 'novel' ORDER BY price DESC;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询图书信息,按照库存量降序排列,如果库存量相同的按照note升序排列
# 10、查询图书信息,按照库存量降序排列,如果库存量相同的按照note升序排列
SELECT * FROM books ORDER BY num DESC,note ASC;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 按照note分类统计书的数量
# 11、按照note分类统计书的数量
SELECT note,COUNT(*) FROM books GROUP BY note;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 按照note分类统计书的库存量,显示库存量超过30本的
# 12、按照note分类统计书的库存量,显示库存量超过30本的
SELECT note,SUM(num) FROM books GROUP BY note HAVING SUM(num)>30;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询所有图书,每页显示5本,显示第二页
# 13、查询所有图书,每页显示5本,显示第二页
SELECT * FROM books LIMIT 5,5;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 按照note分类统计书的库存量,显示库存量最多的
# 14、按照note分类统计书的库存量,显示库存量最多的
SELECT note,SUM(num) sum_num FROM books GROUP BY note ORDER BY sum_num DESC LIMIT 0,1;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询书名达到10个字符的书,不包括里面的空格
# 15、查询书名达到10个字符的书,不包括里面的空格
SELECT * FROM books WHERE CHAR_LENGTH(REPLACE(name,' ',''))>=10;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询书名和类型,其中note值为novel显示小说,law显示法律,medicine显示医药,cartoon显示卡通, joke显示笑话
/*
16、查询书名和类型,
其中note值为 novel显示小说,law显示法律,medicine显示医药,cartoon显示卡通,joke显示笑话
*/
SELECT name AS "书名" ,note, CASE note
WHEN 'novel' THEN '小说'
WHEN 'law' THEN '法律'
WHEN 'medicine' THEN '医药'
WHEN 'cartoon' THEN '卡通'
WHEN 'joke' THEN '笑话'
END AS "类型"
FROM books;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 查询书名、库存,其中num值超过30本的,显示滞销,大于0并低于10的,显示畅销,为0的显示需要无货
# 17、查询书名、库存,其中num值超过30本的,显示滞销,大于0并低于10的,显示畅销,为0的显示需要无货
SELECT name,num,CASE
WHEN num>30 THEN '滞销'
WHEN num>0 AND num<10 THEN '畅销'
WHEN num=0 THEN '无货'
ELSE '正常'
END AS "库存状态"
FROM books;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 统计每一种note的库存量,并合计总量
# 18、统计每一种note的库存量,并合计总量
SELECT IFNULL(note,'合计总库存量') AS note,SUM(num) FROM books GROUP BY note WITH
ROLLUP;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 统计每一种note的数量,并合计总量
# 19、统计每一种note的数量,并合计总量
SELECT IFNULL(note,'合计总数') AS note,COUNT(*) FROM books GROUP BY note WITH ROLLUP;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 统计库存量前三名的图书
# 20、统计库存量前三名的图书
SELECT * FROM books ORDER BY num DESC LIMIT 0,3;

![外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传](https://img-home.csdnimg.cn/images/20230724024159.png?origin_url=https%3A%2F%2Fcdn.nlark.com%2Fyuque%2F0%2F2023%2Fpng%2F35659086%2F1702476975236-eb739aa0-3c6f-45bf-8fa9-5239f1f4305d.png%23averageHue%3D%2523343332%26clientId%3Du0a7d5de6-b0ae-4%26from%3Dpaste%26height%3D238%26id%3Du3f94dc5b%26originHeight%3D321%26originWidth%3D876%26originalType%3Dbinary%26ratio%3D1.5%26rotation%3D0%26showTitle%3Dfalse%26size%3D27799%26status%3Ddone%26style%3Dnone%26taskId%3Duc46258ff-e9e4-461b-b678-8b1d3bf62d1%26title%3D%26width%3D648.8889347280527&pos_id=img-cZDTem1w-1702637081200)
  1. 找出最早出版的一本书
# 21、找出最早出版的一本书
SELECT * FROM books ORDER BY pubdate ASC LIMIT 0,1;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 找出novel中价格最高的一本书
# 22、找出novel中价格最高的一本书
SELECT * FROM books WHERE note = 'novel' ORDER BY price DESC LIMIT 0,1;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 找出书名中字数最多的一本书,不含空格
# 23、找出书名中字数最多的一本书,不含空格
SELECT * FROM books ORDER BY CHAR_LENGTH(REPLACE(name,' ','')) DESC LIMIT 0,1;

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据类型
MySQL中的数据类型

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

整数类型
  1. 类型介绍
  2. 整数类型一共有 5 种,包括 TINYINT、SMALLINT、MEDIUMINT、INT(INTEGER)和 BIGINT。 它们的区别如下表所示:
  3. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  4. 可选属性
  5. 整数类型的可选属性有三个:
    1. M
    1. M : 表示显示宽度,M的取值范围是(0, 255)。例如,int(5):当数据宽度小于5位的时候在数字前面需要用 字符填满宽度。该项功能需要配合“ ZEROFILL ”使用,表示用“0”填满宽度,否则指定显示宽度无效。 如果设置了显示宽度,那么插入的数据宽度超过显示宽度限制,会不会截断或插入失败? 答案:不会对插入的数据有任何影响,还是按照类型的实际宽度进行保存,即 显示宽度与类型可以存储的 值范围无关 。从MySQL 8.0.17开始,整数数据类型不推荐使用显示宽度属性。 整型数据类型可以在定义表结构时指定所需要的显示宽度,如果不指定,则系统为每一种类型指定默认 的宽度值。
CREATE TABLE test_int1 ( x TINYINT, y SMALLINT, z MEDIUMINT, m INT, n BIGINT );

TINYINT有符号数和无符号数的取值范围分别为-128127和0255,由于负号占了一个数字位,因此 TINYINT默认的显示宽度为4。同理,其他整数类型的默认显示宽度与其有符号数的最小值的宽度相同。

CREATE TABLE test_int2(
f1 INT,
f2 INT(5),
f3 INT(5) ZEROFILL
)
DESC test_int2;
INSERT INTO test_int2(f1,f2,f3)
VALUES(1,123,123);
INSERT INTO test_int2(f1,f2)
VALUES(123456,123456);
INSERT INTO test_int2(f1,f2,f3)
VALUES(123456,123456,123456);

  2.  UNSIGNED  
     1.  UNSIGNED : 无符号类型(非负),所有的整数类型都有一个可选的属性UNSIGNED(无符号属性),无 符号整数类型的最小取值为0。所以,如果需要在MySQL数据库中保存非负整数值时,可以将整数类型设 置为无符号类型。 
     2. int类型默认显示宽度为int(11),无符号int类型默认显示宽度为int(10)。  
CREATE TABLE test_int3(
f1 INT UNSIGNED
);
  3.  ZEROFILL  
     1.  ZEROFILL : 0填充,(如果某列是ZEROFILL,那么MySQL会自动为当前列添加UNSIGNED属性),如果指 定了ZEROFILL只是表示不够M位时,用0在左边填充,如果超过M位,只要不超过数据存储范围即可。 
     2. 原来,在 int(M) 中,M 的值跟 int(M) 所占多少存储空间并无任何关系。 int(3)、int(4)、int(8) 在磁盘上都 是占用 4 bytes 的存储空间。也就是说,int(M),必须和UNSIGNED ZEROFILL一起使用才有意义。如果整 数值超过M位,就按照实际位数存储。只是无须再用字符 0 进行填充。  
  1. 适用场景
  2. TINYINT :一般用于枚举数据,比如系统设定取值范围很小且固定的场景。
  3. SMALLINT :可以用于较小范围的统计数据,比如统计工厂的固定资产库存数量等。
  4. MEDIUMINT :用于较大整数的计算,比如车站每日的客流量等。
  5. INT、INTEGER :取值范围足够大,一般情况下不用考虑超限问题,用得最多。比如商品编号。
  6. BIGINT :只有当你处理特别巨大的整数时才会用到。比如双十一的交易量、大型门户网站点击量、证 券公司衍生产品持仓等。
  7. 如何选择?
  8. ** 在评估用哪种整数类型的时候,你需要考虑 存储空间 和 可靠性 的平衡问题:一方 面,用占用字节数少 的整数类型可以节省存储空间;另一方面,要是为了节省存储空间, 使用的整数类型取值范围太小,一 旦遇到超出取值范围的情况,就可能引起 系统错误 ,影响可靠性。 **
  9. 举个例子,商品编号采用的数据类型是 INT。原因就在于,客户门店中流通的商品种类较多,而且,每 天都有旧商品下架,新商品上架,这样不断迭代,日积月累。
  10. **如果使用 SMALLINT 类型,虽然占用字节数比 INT 类型的整数少,但是却不能保证数据不会超出范围 65535。相反,使用 INT,就能确保有足够大的取值范围,不用担心数据超出范围影响可靠性的问题。 **
  11. **你要注意的是,在实际工作中,系统故障产生的成本远远超过增加几个字段存储空间所产生的成本。因 此,我建议你首先确保数据不会超过取值范围,在这个前提之下,再去考虑如何节省存储空间。 **
浮点类型
  1. 类型介绍
  2. 浮点数和定点数类型的特点是可以 处理小数 ,你可以把整数看成小数的一个特例。因此,浮点数和定点 数的使用场景,比整数大多了。 MySQL支持的浮点数类型,分别是 FLOAT、DOUBLE、REAL。
    1. FLOAT 表示单精度浮点数;
    2. DOUBLE 表示双精度浮点数;
  3. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  4. REAL默认就是 DOUBLE。如果你把 SQL 模式设定为启用“ REAL_AS_FLOAT ”,那 么,MySQL 就认为 REAL 是 FLOAT。如果要启用“REAL_AS_FLOAT”,可以通过以下 SQL 语句实现:
    1. SET sql_mode = “REAL_AS_FLOAT”
  5. 问题1:FLOAT 和 DOUBLE 这两种数据类型的区别是啥呢?
    1. FLOAT 占用字节数少,取值范围小;DOUBLE 占用字节数多,取值范围也大。
  6. 问题2:为什么浮点数类型的无符号数取值范围,只相当于有符号数取值范围的一半,也就是只相当于 有符号数取值范围大于等于零的部分呢?
    1. MySQL 存储浮点数的格式为: 符号(S) 、 尾数(M) 和 阶码(E) 。因此,无论有没有符号,MySQL 的浮 点数都会存储表示符号的部分。因此, 所谓的无符号数取值范围,其实就是有符号数取值范围大于等于 零的部分。
  7. 数据精度说明
  8. 对于浮点类型,在MySQL中单精度值使用 4 个字节,双精度值使用 8 个字节。
    1. MySQL允许使用 非标准语法 (其他数据库未必支持,因此如果涉及到数据迁移,则最好不要这么 用):
    2. FLOAT(M,D) 或 DOUBLE(M,D) 。这里,M称为 精度 ,D称为 标度 。(M,D)中 M=整数位+小数 位,D=小数位。 D<=M<=255,0<=D<=30。 例如,定义为FLOAT(5,2)的一个列可以显示为-999.99-999.99。如果超过这个范围会报错。 FLOAT和DOUBLE类型在不指定(M,D)时,默认会按照实际的精度(由实际的硬件和操作系统决定) 来显示。
    3. 说明:浮点类型,也可以加 UNSIGNED ,但是不会改变数据范围,例如:FLOAT(3,2) UNSIGNED仍然 只能表示0-9.99的范围。
  9. 不管是否显式设置了精度(M,D),这里MySQL的处理方案如下:
    1. 如果存储时,整数部分超出了范围,MySQL就会报错,不允许存这样的值
    2. 如果存储时,小数点部分若超出范围,就分以下情况:
    1. 若四舍五入后,整数部分没有超出范围,则只警告,但能成功操作并四舍五入删除多余 的小数位后保存。例如在FLOAT(5,2)列内插入999.009,近似结果是999.01。
    2. 若四舍五入后,整数部分超出范围,则MySQL报错,并拒绝处理。如FLOAT(5,2)列内插入 999.995和-999.995都会报错。
  10. 从MySQL 8.0.17开始,FLOAT(M,D) 和DOUBLE(M,D)用法在官方文档中已经明确不推荐使用,将来可 能被移除。另外,关于浮点型FLOAT和DOUBLE的UNSIGNED也不推荐使用了,将来也可能被移除。
CREATE TABLE test_double1(
f1 FLOAT,
f2 FLOAT(5,2),
f3 DOUBLE,
f4 DOUBLE(5,2)
);
DESC test_double1;
INSERT INTO test_double1
VALUES(123.456,123.456,123.4567,123.45);
#Out of range value for column 'f2' at row 1
INSERT INTO test_double1
VALUES(123.456,1234.456,123.4567,123.45);
SELECT * FROM test_double1;
  1. 精度误差说明
  2. ** 浮点数类型有个缺陷,就是不精准。下面我来重点解释一下为什么 MySQL 的浮点数不够精准。比如,我 们设计一个表,有f1这个字段,插入值分别为0.47,0.44,0.19,我们期待的运行结果是:0.47 + 0.44 + 0.19 = 1.1。而使用sum之后查询: **
CREATE TABLE test_double2(
f1 DOUBLE
);
INSERT INTO test_double2
VALUES(0.47),(0.44),(0.19);

mysql> SELECT SUM(f1)
-> FROM test_double2;
+--------------------+
| SUM(f1) |
+--------------------+
| 1.0999999999999999 |
+--------------------+
1 row in set (0.00 sec)



mysql> SELECT SUM(f1) = 1.1,1.1 = 1.1
-> FROM test_double2;
+---------------+-----------+
| SUM(f1) = 1.1 | 1.1 = 1.1 |
+---------------+-----------+
| 0 | 1 |
+---------------+-----------+
1 row in set (0.00 sec)

查询结果是 1.0999999999999999。看到了吗?虽然误差很小,但确实有误差。 你也可以尝试把数据类型 改成 FLOAT,然后运行求和查询,得到的是, 1.0999999940395355。显然,误差更大了。
那么,为什么会存在这样的误差呢?问题还是出在 MySQL 对浮点类型数据的存储方式上。
MySQL 用 4 个字节存储 FLOAT 类型数据,用 8 个字节来存储 DOUBLE 类型数据。无论哪个,都是采用二 进制的方式来进行存储的。比如 9.625,用二进制来表达,就是 1001.101,或者表达成 1.001101×2^3。如 果尾数不是 0 或 5(比如 9.624),你就无法用一个二进制数来精确表达。进而,就只好在取值允许的范 围内进行四舍五入。

在编程中,如果用到浮点数,要特别注意误差问题,因为浮点数是不准确的,所以我们要避免使用“=”来 判断两个数是否相等。同时,在一些对精确度要求较高的项目中,千万不要使用浮点数,不然会导致结 果错误,甚至是造成不可挽回的损失。那么,MySQL 有没有精准的数据类型呢?当然有,这就是定点数 类型: DECIMAL 。

定点数类型
  1. 类型介绍
  2. MySQL中的定点数类型只有 DECIMAL 一种类型。
  3. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  4. 使用 DECIMAL(M,D) 的方式表示高精度小数。其中,M被称为精度,D被称为标度。0<=M<=65, 0<=D<=30,D
  5. DECIMAL(M,D)的最大取值范围与DOUBLE类型一样,但是有效的数据范围是由M和D决定的。 DECIMAL 的存储空间并不是固定的,由精度值M决定,总共占用的存储空间为M+2个字节。也就是 说,在一些对精度要求不高的场景下,比起占用同样字节长度的定点数,浮点数表达的数值范围可 以更大一些。
  6. 定点数在MySQL内部是以 字符串 的形式进行存储,这就决定了它一定是精准的。
  7. 当DECIMAL类型不指定精度和标度时,其默认为DECIMAL(10,0)。当数据的精度超出了定点数类型的 精度范围时,则MySQL同样会进行四舍五入处理。
  8. 浮点数 vs 定点数
    1. 浮点数相对于定点数的优点是在长度一定的情况下,浮点类型取值范围大,但是不精准,适用 于需要取值范围大,又可以容忍微小误差的科学计算场景(比如计算化学、分子建模、流体动 力学等)
    2. 定点数类型取值范围相对小,但是精准,没有误差,适合于对精度要求极高的场景 (比如涉 及金额计算的场景)
CREATE TABLE test_decimal1(
f1 DECIMAL,
f2 DECIMAL(5,2)
);
DESC test_decimal1;
INSERT INTO test_decimal1(f1,f2)
VALUES(123.123,123.456);
#Out of range value for column 'f2' at row 1
INSERT INTO test_decimal1(f2)
VALUES(1234.34);

mysql> SELECT * FROM test_decimal1;
+------+--------+
| f1 | f2 |
+------+--------+
| 123 | 123.46 |
+------+--------+
1 row in set (0.00 sec)

举例
我们运行下面的语句,把test_double2表中字段“f1”的数据类型修改为 DECIMAL(5,2):

ALTER TABLE test_double2
MODIFY f1 DECIMAL(5,2);

然后,我们再一次运行求和语句:

mysql> SELECT SUM(f1)
-> FROM test_double2;
+---------+
| SUM(f1) |
+---------+
| 1.10 |
+---------+
1 row in set (0.00 sec)


mysql> SELECT SUM(f1) = 1.1
-> FROM test_double2;
+---------------+
| SUM(f1) = 1.1 |
+---------------+
| 1 |
+---------------+
1 row in set (0.00 sec
  1. 开发中经验
  2. “由于 DECIMAL 数据类型的精准性,在我们的项目中,除了极少数(比如商品编号)用到整数类型 外,其他的数值都用的是 DECIMAL,原因就是这个项目所处的零售行业,要求精准,一分钱也不能 差。 ” ——来自某项目经理
位类型:BIT
  1. BIT类型中存储的是二进制值,类似010110。
  2. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  3. BIT类型,如果没有指定(M),默认是1位。这个1位,表示只能存1位的二进制值。这里(M)是表示二进制的 位数,位数最小值为1,最大值为64。
CREATE TABLE test_bit1(
f1 BIT,
f2 BIT(5),
f3 BIT(64)
);
INSERT INTO test_bit1(f1)
VALUES(1);
#Data too long for column 'f1' at row 1
INSERT INTO test_bit1(f1)
VALUES(2);
INSERT INTO test_bit1(f2)
VALUES(23);

注意:在向BIT类型的字段中插入数据时,一定要确保插入的数据在BIT类型支持的范围内。
使用SELECT命令查询位字段时,可以用 BIN() 或 HEX() 函数进行读取。

mysql> SELECT * FROM test_bit1;
+------------+------------+------------+
| f1 | f2 | f3 |
+------------+------------+------------+
| 0x01 | NULL | NULL |
| NULL | 0x17 | NULL |
+------------+------------+------------+
2 rows in set (0.00 sec)


mysql> SELECT BIN(f2),HEX(f2)
-> FROM test_bit1;
+---------+---------+
| BIN(f2) | HEX(f2) |
+---------+---------+
| NULL | NULL |
| 10111 | 17 |
+---------+---------+
2 rows in set (0.00 sec)

mysql> SELECT f2 + 0
-> FROM test_bit1;
+--------+
| f2 + 0 |
+--------+
| NULL |
| 23 |
+--------+
2 rows in set (0.00 sec)

可以看到,使用b+0查询数据时,可以直接查询出存储的十进制数据的值。

日期与时间类型

日期与时间是重要的信息,在我们的系统中,几乎所有的数据表都用得到。原因是客户需要知道数据的 时间标签,从而进行数据查询、统计和处理。
MySQL有多种表示日期和时间的数据类型,不同的版本可能有所差异,MySQL8.0版本支持的日期和时间 类型主要有:YEAR类型、TIME类型、DATE类型、DATETIME类型和TIMESTAMP类型。
YEAR 类型通常用来表示年
DATE 类型通常用来表示年、月、日
TIME 类型通常用来表示时、分、秒
DATETIME 类型通常用来表示年、月、日、时、分、秒
TIMESTAMP 类型通常用来表示带时区的年、月、日、时、分、秒
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
可以看到,不同数据类型表示的时间内容不同、取值范围不同,而且占用的字节数也不一样,你要根据 实际需要灵活选取。
为什么时间类型 TIME 的取值范围不是 -23:59:59~23:59:59 呢?原因是 MySQL 设计的 TIME 类型,不光表 示一天之内的时间,而且可以用来表示一个时间间隔,这个时间间隔可以超过 24 小时。

  1. YEAR类型
  2. ** YEAR类型用来表示年份,在所有的日期时间类型中所占用的存储空间最小,只需要 1个字节 的存储空 间。 **
  3. **在MySQL中,YEAR有以下几种存储格式: **
    1. ** 以4位字符串或数字格式表示YEAR类型,其格式为YYYY,最小值为1901,最大值为2155。 **
    2. **以2位字符串格式表示YEAR类型,最小值为00,最大值为99。 **
      1. ** 当取值为01到69时,表示2001到2069; **
      2. **当取值为70到99时,表示1970到1999; **
      3. **当取值整数的0或00添加的话,那么是0000年; **
      4. **当取值是日期/字符串的’0’添加的话,是2000年。 **
  4. 从MySQL5.5.27开始,2位格式的YEAR已经不推荐使用。YEAR默认格式就是“YYYY”,没必要写成YEAR(4), 从MySQL 8.0.19开始,不推荐使用指定显示宽度的YEAR(4)数据类型。
CREATE TABLE test_year(
f1 YEAR,
f2 YEAR(4)
);

mysql> DESC test_year;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| f1 | year(4) | YES | | NULL | |
| f2 | year(4) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
2 rows in set (0.00 sec)

INSERT INTO test_year
VALUES('2020','2021');
mysql> SELECT * FROM test_year;
+------+------+
| f1 | f2 |
+------+------+
| 2020 | 2021 |
+------+------+
1 rows in set (0.00 sec)

INSERT INTO test_year
VALUES('45','71');
INSERT INTO test_year
VALUES(0,'0');
mysql> SELECT * FROM test_year;
+------+------+
| f1 | f2 |
+------+------+
| 2020 | 2021 |
| 2045 | 1971 |
| 0000 | 2000 |
+------+------+
3 rows in set (0.00 sec)
  1. DATE类型
  2. DATE类型表示日期,没有时间部分,格式为 YYYY-MM-DD ,其中,YYYY表示年份,MM表示月份,DD表示 日期。需要 3个字节 的存储空间。在向DATE类型的字段插入数据时,同样需要满足一定的格式条件。
    1. 以 YYYY-MM-DD 格式或者 YYYYMMDD 格式表示的字符串日期,其最小取值为1000-01-01,最大取值为 9999-12-03。YYYYMMDD格式会被转化为YYYY-MM-DD格式。
    2. 以 YY-MM-DD 格式或者 YYMMDD 格式表示的字符串日期,此格式中,年份为两位数值或字符串满足 YEAR类型的格式条件为:当年份取值为00到69时,会被转化为2000到2069;当年份取值为70到99 时,会被转化为1970到1999。
    3. 使用 CURRENT_DATE() 或者 NOW() 函数,会插入当前系统的日期。
    4. 创建数据表,表中只包含一个DATE类型的字段f1。
CREATE TABLE test_date1(
f1 DATE
);
Query OK, 0 rows affected (0.13 sec)
  5.  插入数据:  
INSERT INTO test_date1
VALUES ('2020-10-01'), ('20201001'),(20201001);
INSERT INTO test_date1
VALUES ('00-01-01'), ('000101'), ('69-10-01'), ('691001'), ('70-01-01'), ('700101'),
('99-01-01'), ('990101');

INSERT INTO test_date1
VALUES (000301), (690301), (700301), (990301);
INSERT INTO test_date1
VALUES (CURRENT_DATE()), (NOW());
SELECT *
FROM test_date1;

  1. TIME类型
  2. TIME类型用来表示时间,不包含日期部分。在MySQL中,需要 3个字节 的存储空间来存储TIME类型的数 据,可以使用“HH:MM:SS”格式来表示TIME类型,其中,HH表示小时,MM表示分钟,SS表示秒。 在MySQL中,向TIME类型的字段插入数据时,也可以使用几种不同的格式。 (1)可以使用带有冒号的 字符串,比如’ D HH:MM:SS’ 、’ HH:MM:SS ‘、’ HH:MM ‘、’ D HH:MM ‘、’ D HH ‘或’ SS ‘格式,都能被正 确地插入TIME类型的字段中。其中D表示天,其最小值为0,最大值为34。如果使用带有D格式的字符串 插入TIME类型的字段时,D会被转化为小时,计算格式为D*24+HH。当使用带有冒号并且不带D的字符串 表示时间时,表示当天的时间,比如12:10表示12:10:00,而不是00:12:10。 (2)可以使用不带有冒号的 字符串或者数字,格式为’ HHMMSS '或者 HHMMSS 。如果插入一个不合法的字符串或者数字,MySQL在存 储数据时,会将其自动转化为00:00:00进行存储。比如1210,MySQL会将最右边的两位解析成秒,表示 00:12:10,而不是12:10:00。 (3)使用 CURRENT_TIME() 或者 NOW() ,会插入当前系统的时间。
  3. 创建数据表,表中包含一个TIME类型的字段f1。
CREATE TABLE test_time1(
f1 TIME
);
Query OK, 0 rows affected (0.02 sec)

INSERT INTO test_time1
VALUES('2 12:30:29'), ('12:35:29'), ('12:40'), ('2 12:40'),('1 05'), ('45');
INSERT INTO test_time1
VALUES ('123520'), (124011),(1210);
INSERT INTO test_time1
VALUES (NOW()), (CURRENT_TIME());
SELECT * FROM test_time1;
  1. DATETIME类型
  2. DATETIME类型在所有的日期时间类型中占用的存储空间最大,总共需要 8 个字节的存储空间。在格式上 为DATE类型和TIME类型的组合,可以表示为 YYYY-MM-DD HH:MM:SS ,其中YYYY表示年份,MM表示月 份,DD表示日期,HH表示小时,MM表示分钟,SS表示秒。 在向DATETIME类型的字段插入数据时,同样需要满足一定的格式条件。
    1. 以 YYYY-MM-DD HH:MM:SS 格式或者 YYYYMMDDHHMMSS 格式的字符串插入DATETIME类型的字段时, 最小值为1000-01-01 00:00:00,最大值为9999-12-03 23:59:59。
    1. 以YYYYMMDDHHMMSS格式的数字插入DATETIME类型的字段时,会被转化为YYYY-MM-DD HH:MM:SS格式。
  2.  以 YY-MM-DD HH:MM:SS 格式或者 YYMMDDHHMMSS 格式的字符串插入DATETIME类型的字段时,两位 数的年份规则符合YEAR类型的规则,00到69表示2000到2069;70到99表示1970到1999。 
  3. 使用函数 CURRENT_TIMESTAMP() 和 NOW() ,可以向DATETIME类型的字段插入系统的当前日期和 时间。  
  1. 创建数据表,表中包含一个DATETIME类型的字段dt。
CREATE TABLE test_datetime1(
dt DATETIME
);
Query OK, 0 rows affected (0.02 sec)

插入数据

INSERT INTO test_datetime1
VALUES ('2021-01-01 06:50:30'), ('20210101065030');
INSERT INTO test_datetime1
VALUES ('99-01-01 00:00:00'), ('990101000000'), ('20-01-01 00:00:00'),
('200101000000');
INSERT INTO test_datetime1
VALUES (20200101000000), (200101000000), (19990101000000), (990101000000);
INSERT INTO test_datetime1
VALUES (CURRENT_TIMESTAMP()), (NOW());

  1. TIMESTAMP类型

TIMESTAMP类型也可以表示日期时间,其显示格式与DATETIME类型相同,都是 YYYY-MM-DD HH:MM:SS ,需要4个字节的存储空间。但是TIMESTAMP存储的时间范围比DATETIME要小很多,只能存储 “1970-01-01 00:00:01 UTC”到“2038-01-19 03:14:07 UTC”之间的时间。其中,UTC表示世界统一时间,也叫 作世界标准时间。

  1. 存储数据的时候需要对当前时间所在的时区进行转换,查询数据的时候再将时间转换回当前的时 区。因此,使用TIMESTAMP存储的同一个时间值,在不同的时区查询时会显示不同的时间。

向TIMESTAMP类型的字段插入数据时,当插入的数据格式满足YY-MM-DD HH:MM:SS和YYMMDDHHMMSS 时,两位数值的年份同样符合YEAR类型的规则条件,只不过表示的时间范围要小很多。 如果向TIMESTAMP类型的字段插入的时间超出了TIMESTAMP类型的范围,则MySQL会抛出错误信息。
创建数据表,表中包含一个TIMESTAMP类型的字段ts。

CREATE TABLE test_timestamp1(
ts TIMESTAMP
);

插入数据:
INSERT INTO test_timestamp1
VALUES ('1999-01-01 03:04:50'), ('19990101030405'), ('99-01-01 03:04:05'),
('990101030405');
INSERT INTO test_timestamp1
VALUES ('2020@01@01@00@00@00'), ('20@01@01@00@00@00');
INSERT INTO test_timestamp1
VALUES (CURRENT_TIMESTAMP()), (NOW());
#Incorrect datetime value
INSERT INTO test_timestamp1
VALUES ('2038-01-20 03:14:07');

TIMESTAMP和DATETIME的区别:
TIMESTAMP存储空间比较小,表示的日期时间范围也比较小
底层存储方式不同,TIMESTAMP底层存储的是毫秒值,距离1970-1-1 0:0:0 0毫秒的毫秒值。
两个日期比较大小或日期计算时,TIMESTAMP更方便、更快。
TIMESTAMP和时区有关。TIMESTAMP会根据用户的时区不同,显示不同的结果。而DATETIME则只能 反映出插入时当地的时区,其他时区的人查看数据必然会有误差的。

CREATE TABLE temp_time(
d1 DATETIME,
d2 TIMESTAMP
);

INSERT INTO temp_time VALUES('2021-9-2 14:45:52','2021-9-2 14:45:52');
INSERT INTO temp_time VALUES(NOW(),NOW());


mysql> SELECT * FROM temp_time;
+---------------------+---------------------+
| d1 | d2 |
+---------------------+---------------------+
| 2021-09-02 14:45:52 | 2021-09-02 14:45:52 |
| 2021-11-03 17:38:17 | 2021-11-03 17:38:17 |
+---------------------+---------------------+
2 rows in set (0.00 sec)

#修改当前的时区
SET time_zone = '+9:00';


mysql> SELECT * FROM temp_time;
+---------------------+---------------------+
| d1 | d2 |
+---------------------+---------------------+
| 2021-09-02 14:45:52 | 2021-09-02 15:45:52 |
| 2021-11-03 17:38:17 | 2021-11-03 18:38:17 |
+---------------------+---------------------+
2 rows in set (0.00 sec)
  1. 开发中经验
  2. 用得最多的日期时间类型,就是 DATETIME 。虽然 MySQL 也支持 YEAR(年)、 TIME(时间)、 DATE(日期),以及 TIMESTAMP 类型,但是在实际项目中,尽量用 DATETIME 类型。因为这个数据类型 包括了完整的日期和时间信息,取值范围也最大,使用起来比较方便。毕竟,如果日期时间信息分散在 好几个字段,很不容易记,而且查询的时候,SQL 语句也会更加复杂。 此外,一般存注册时间、商品发布时间等,不建议使用DATETIME存储,而是使用 时间戳 ,因为 DATETIME虽然直观,但不便于计算
mysql> SELECT UNIX_TIMESTAMP();
+------------------+
| UNIX_TIMESTAMP() |
+------------------+
| 1635932762 |
+------------------+
1 row in set (0.00 sec)
文本字符串类型

在实际的项目中,我们还经常遇到一种数据,就是字符串数据。 MySQL中,文本字符串总体上分为 CHAR 、 VARCHAR 、 TINYTEXT 、 TEXT 、 MEDIUMTEXT 、 LONGTEXT 、 ENUM 、 SET 等类型。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. CHAR与VARCHAR类型

CHAR和VARCHAR类型都可以存储比较短的字符串。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
CHAR类型:
CHAR(M) 类型一般需要预先定义字符串长度。如果不指定(M),则表示长度默认是1个字符。
如果保存时,数据的实际长度比CHAR类型声明的长度小,则会在 右侧填充 空格以达到指定的长 度。当MySQL检索CHAR类型的数据时,CHAR类型的字段会去除尾部的空格。
定义CHAR类型字段时,声明的字段长度即为CHAR类型字段所占的存储空间的字节数。

CREATE TABLE test_char1(
c1 CHAR,
c2 CHAR(5)
);
DESC test_char1;

INSERT INTO test_char1
VALUES('a','Tom');
SELECT c1,CONCAT(c2,'***') FROM test_char1;
INSERT INTO test_char1(c2)
VALUES('a ');
SELECT CHAR_LENGTH(c2)
FROM test_char1;


VARCHAR类型:
VARCHAR(M) 定义时, 必须指定 长度M,否则报错。
MySQL4.0版本以下,varchar(20):指的是20字节,如果存放UTF8汉字时,只能存6个(每个汉字3字 节) ;MySQL5.0版本以上,varchar(20):指的是20字符。
检索VARCHAR类型的字段数据时,会保留数据尾部的空格。VARCHAR类型的字段所占用的存储空间 为字符串实际长度加1个字节。

CREATE TABLE test_varchar1(
NAME VARCHAR #错误
);


#Column length too big for column 'NAME' (max = 21845);
CREATE TABLE test_varchar2(
NAME VARCHAR(65535) #错误
);

CREATE TABLE test_varchar3(
NAME VARCHAR(5)
);
INSERT INTO test_varchar3
VALUES('尚硅谷'),('尚硅谷教育');
#Data too long for column 'NAME' at row 1
INSERT INTO test_varchar3
VALUES('尚硅谷IT教育');

哪些情况使用 CHAR 或 VARCHAR 更好
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
情况1:存储很短的信息。比如门牌号码101,201……这样很短的信息应该用char,因为varchar还要占个 byte用于存储信息长度,本来打算节约存储的,结果得不偿失。
情况2:固定长度的。比如使用uuid作为主键,那用char应该更合适。因为他固定长度,varchar动态根据 长度的特性就消失了,而且还要占个长度信息。 情况3:十分频繁改变的column。因为varchar每次存储都要有额外的计算,得到长度等工作,如果一个 非常频繁改变的,那就要有很多的精力用于计算,而这些对于char来说是不需要的。 情况4:具体存储引擎中的情况:
MyISAM 数据存储引擎和数据列:MyISAM数据表,最好使用固定长度(CHAR)的数据列代替可变长 度(VARCHAR)的数据列。这样使得整个表静态化,从而使 数据检索更快 ,用空间换时间。
MEMORY 存储引擎和数据列:MEMORY数据表目前都使用固定长度的数据行存储,因此无论使用 CHAR或VARCHAR列都没有关系,两者都是作为CHAR类型处理的。
InnoDB 存储引擎,建议使用VARCHAR类型。因为对于InnoDB数据表,内部的行存储格式并没有区 分固定长度和可变长度列(所有数据行都使用指向数据列值的头指针),而且主要影响性能的因素 是数据行使用的存储总量,由于char平均占用的空间多于varchar,所以除了简短并且固定长度的, 其他考虑varchar。这样节省空间,对磁盘I/O和数据存储总量比较好。

  1. TEXT类型
  2. 在MySQL中,TEXT用来保存文本类型的字符串,总共包含4种类型,分别为TINYTEXT、TEXT、 MEDIUMTEXT 和 LONGTEXT 类型。
  3. 在向TEXT类型的字段保存和查询数据时,系统自动按照实际长度存储,不需要预先定义长度。这一点和 VARCHAR类型相同。
  4. 每种TEXT类型保存的数据长度和所占用的存储空间不同,如下:
  5. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  6. 由于实际存储的长度不确定,MySQL 不允许 TEXT 类型的字段做主键。遇到这种情况,你只能采用 CHAR(M),或者 VARCHAR(M)。
CREATE TABLE test_text(
tx TEXT
);

INSERT INTO test_text
VALUES('atguigu ');
SELECT CHAR_LENGTH(tx)
FROM test_text; #10

  1. 开发中经验:
  2. TEXT文本类型,可以存比较大的文本段,搜索速度稍慢,因此如果不是特别大的内容,建议使用CHAR, VARCHAR来代替。还有TEXT类型不用加默认值,加了也没用。而且text和blob类型的数据删除后容易导致 “空洞”,使得文件碎片比较多,所以频繁使用的表不建议包含TEXT类型字段,建议单独分出去,单独用 一个表。
ENUM类型

ENUM类型也叫作枚举类型,ENUM类型的取值范围需要在定义字段时进行指定。设置字段值时,ENUM 类型只允许从成员中选取单个值,不能一次选取多个值。
其所需要的存储空间由定义ENUM类型时指定的成员个数决定。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 当ENUM类型包含1~255个成员时,需要1个字节的存储空间;
  2. 当ENUM类型包含256~65535个成员时,需要2个字节的存储空间。
  3. ENUM类型的成员个数的上限为65535个。
  4. 创建表如下:
CREATE TABLE test_enum(
season ENUM('春','夏','秋','冬','unknow')
);

INSERT INTO test_enum
VALUES('春'),('秋');
# 忽略大小写
INSERT INTO test_enum
VALUES('UNKNOW');
# 允许按照角标的方式获取指定索引位置的枚举值
INSERT INTO test_enum
VALUES('1'),(3);
# Data truncated for column 'season' at row 1
INSERT INTO test_enum
VALUES('ab');
# 当ENUM类型的字段没有声明为NOT NULL时,插入NULL也是有效的
INSERT INTO test_enum
VALUES(NULL);
SET类型

SET表示一个字符串对象,可以包含0个或多个成员,但成员个数的上限为 64 。设置字段值时,可以取 取值范围内的 0 个或多个值。
当SET类型包含的成员个数不同时,其所占用的存储空间也是不同的,具体如下:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
SET类型在存储数据时成员个数越多,其占用的存储空间越大。注意:SET类型在选取成员时,可以一次 选择多个成员,这一点与ENUM类型不同。

CREATE TABLE test_set(
s SET ('A', 'B', 'C')
);
向表中插入数据:
INSERT INTO test_set (s) VALUES ('A'), ('A,B');
#插入重复的SET类型成员时,MySQL会自动删除重复的成员
INSERT INTO test_set (s) VALUES ('A,B,C,A');
#向SET类型的字段插入SET成员中不存在的值时,MySQL会抛出错误。
INSERT INTO test_set (s) VALUES ('A,B,C,D');
SELECT *
FROM test_set;
举例:
CREATE TABLE temp_mul(
gender ENUM('男','女'),
hobby SET('吃饭','睡觉','打豆豆','写代码')
);


INSERT INTO temp_mul VALUES('男','睡觉,打豆豆'); #成功
# Data truncated for column 'gender' at row 1
INSERT INTO temp_mul VALUES('男,女','睡觉,写代码'); #失败
# Data truncated for column 'gender' at row 1
INSERT INTO temp_mul VALUES('妖','睡觉,写代码');#失败
INSERT INTO temp_mul VALUES('男','睡觉,写代码,吃饭'); #成功
二进制字符串类型

MySQL中的二进制字符串类型主要存储一些二进制数据,比如可以存储图片、音频和视频等二进制数 据。 MySQL中支持的二进制字符串类型主要包括BINARY、VARBINARY、TINYBLOB、BLOB、MEDIUMBLOB 和 LONGBLOB类型。

  1. BINARY与VARBINARY类型
  2. BINARY和VARBINARY类似于CHAR和VARCHAR,只是它们存储的是二进制字符串。
  3. BINARY (M)为固定长度的二进制字符串,M表示最多能存储的字节数,取值范围是0~255个字符。如果未 指定(M),表示只能存储 1个字节 。例如BINARY (8),表示最多能存储8个字节,如果字段值不足(M)个字 节,将在右边填充’\0’以补齐指定长度。
  4. VARBINARY (M)为可变长度的二进制字符串,M表示最多能存储的字节数,总字节数不能超过行的字节长 度限制65535,另外还要考虑额外字节开销,VARBINARY类型的数据除了存储数据本身外,还需要1或2个 字节来存储数据的字节数。VARBINARY类型 必须指定(M) ,否则报错。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
创建表:

CREATE TABLE test_binary1(
f1 BINARY,
f2 BINARY(3),
# f3 VARBINARY,
f4 VARBINARY(10)
);

添加数据:

INSERT INTO test_binary1(f1,f2)
VALUES('a','a');
INSERT INTO test_binary1(f1,f2)
VALUES('尚','尚');#失败


INSERT INTO test_binary1(f2,f4)
VALUES('ab','ab');
mysql> SELECT LENGTH(f2),LENGTH(f4)
-> FROM test_binary1;
+------------+------------+
| LENGTH(f2) | LENGTH(f4) |
+------------+------------+
| 3 | NULL |
| 3 | 2 |
+------------+------------+
2 rows in set (0.00 sec)
  1. BLOB类型
  2. BLOB是一个 二进制大对象 ,可以容纳可变数量的数据。
  3. MySQL中的BLOB类型包括TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB 4种类型,它们可容纳值的最大 长度不同。可以存储一个二进制的大对象,比如 图片 、 音频 和 视频 等。 需要注意的是,在实际工作中,往往不会在MySQL数据库中使用BLOB类型存储大对象数据,通常会将图 片、音频和视频文件存储到 服务器的磁盘上 ,并将图片、音频和视频的访问路径存储到MySQL中。
  4. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
CREATE TABLE test_blob1(
id INT,
img MEDIUMBLOB
);

TEXT和BLOB的使用注意事项: 在使用text和blob字段类型时要注意以下几点,以便更好的发挥数据库的性能。 ① BLOB和TEXT值也会引起自己的一些问题,特别是执行了大量的删除或更新操作的时候。删除这种值 会在数据表中留下很大的" 空洞 ",以后填入这些"空洞"的记录可能长度不同。为了提高性能,建议定期 使用 OPTIMIZE TABLE 功能对这类表进行 碎片整理 。 ② 如果需要对大文本字段进行模糊查询,MySQL 提供了 前缀索引 。但是仍然要在不必要的时候避免检 索大型的BLOB或TEXT值。例如,SELECT * 查询就不是很好的想法,除非你能够确定作为约束条件的 WHERE子句只会找到所需要的数据行。否则,你可能毫无目的地在网络上传输大量的值。 ③ 把BLOB或TEXT列 分离到单独的表 中。在某些环境中,如果把这些数据列移动到第二张数据表中,可 以让你把原数据表中的数据列转换为固定长度的数据行格式,那么它就是有意义的。这会 减少主表中的 碎片 ,使你得到固定长度数据行的性能优势。它还使你在主数据表上运行 SELECT * 查询的时候不会通过 网络传输大量的BLOB或TEXT值。

JSON 类型

JSON(JavaScript Object Notation)是一种轻量级的 数据交换格式 。简洁和清晰的层次结构使得 JSON 成 为理想的数据交换语言。它易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效 率。JSON 可以将 JavaScript 对象中表示的一组数据转换为字符串,然后就可以在网络或者程序之间轻 松地传递这个字符串,并在需要的时候将它还原为各编程语言所支持的数据格式。 在MySQL 5.7中,就已经支持JSON数据类型。在MySQL 8.x版本中,JSON类型提供了可以进行自动验证的 JSON文档和优化的存储结构,使得在MySQL中存储和读取JSON类型的数据更加方便和高效。 创建数据 表,表中包含一个JSON类型的字段 js 。

CREATE TABLE test_json(
js json
);
向表中插入JSON数据。

INSERT INTO test_json (js)
VALUES ('{"name":"songhk", "age":18, "address":{"province":"beijing",
"city":"beijing"}}');
查询t19表中的数据。
mysql> SELECT *
-> FROM test_json;

当需要检索JSON类型的字段中数据的某个具体值时,可以使用“->”和“->>”符号。

mysql> SELECT js -> '$.name' AS NAME,js -> '$.age' AS age ,js -> '$.address.province'
AS province, js -> '$.address.city' AS city
-> FROM test_json;
+----------+------+-----------+-----------+
| NAME | age | province | city |
+----------+------+-----------+-----------+
| "songhk" | 18 | "beijing" | "beijing" |
+----------+------+-----------+-----------+
1 row in set (0.00 sec)

通过“->”和“->>”符号,从JSON字段中正确查询出了指定的JSON数据的值。

空间类型

MySQL 空间类型扩展支持地理特征的生成、存储和分析。这里的地理特征表示世界上具有位置的任何东 西,可以是一个实体,例如一座山;可以是空间,例如一座办公楼;也可以是一个可定义的位置,例如 一个十字路口等等。MySQL中使用 Geometry(几何) 来表示所有地理特征。Geometry指一个点或点的 集合,代表世界上任何具有位置的事物。 MySQL的空间数据类型(Spatial Data Type)对应于OpenGIS类,包括单值类型:GEOMETRY、POINT、 LINESTRING、POLYGON以及集合类型:MULTIPOINT、MULTILINESTRING、MULTIPOLYGON、 GEOMETRYCOLLECTION 。
Geometry是所有空间集合类型的基类,其他类型如POINT、LINESTRING、POLYGON都是Geometry的 子类。 Point,顾名思义就是点,有一个坐标值。例如POINT(121.213342 31.234532),POINT(30 10), 坐标值支持DECIMAL类型,经度(longitude)在前,维度(latitude)在后,用空格分隔。 LineString,线,由一系列点连接而成。如果线从头至尾没有交叉,那就是简单的 (simple);如果起点和终点重叠,那就是封闭的(closed)。例如LINESTRING(30 10,10 30,40 40),点与点之间用逗号分隔,一个点中的经纬度用空格分隔,与POINT格式一致。
olygon,多边形。可以是一个实心平面形,即没有内部边界,也可以有空洞,类似纽扣。最 简单的就是只有一个外边界的情况,例如POLYGON((0 0,10 0,10 10, 0 10))。 下面展示几种常见的几何图形元素: 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
MultiPoint、MultiLineString、MultiPolygon、GeometryCollection 这4种类型都是集合类,是多个 Point、LineString或Polygon组合而成。 下面展示的是多个同类或异类几何图形元素的组合:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

小结及选择建议

在定义数据类型时,如果确定是 整数 ,就用 INT ; 如果是 小数 ,一定用定点数类型 DECIMAL(M,D) ; 如果是日期与时间,就用 DATETIME 。 这样做的好处是,首先确保你的系统不会因为数据类型定义出错。不过,凡事都是有两面的,可靠性 好,并不意味着高效。比如,TEXT 虽然使用方便,但是效率不如 CHAR(M) 和 VARCHAR(M)。 关于字符串的选择,建议参考如下阿里巴巴的《Java开发手册》规范:
阿里巴巴《Java开发手册》之MySQL数据库: 任何字段如果为非负数,必须是 UNSIGNED 【 强制 】小数类型为 DECIMAL,禁止使用 FLOAT 和 DOUBLE。 说明:在存储的时候,FLOAT 和 DOUBLE 都存在精度损失的问题,很可能在比较值的时候,得 到不正确的结果。如果存储的数据范围超过 DECIMAL 的范围,建议将数据拆成整数和小数并 分开存储。 【 强制 】如果存储的字符串长度几乎相等,使用 CHAR 定长字符串类型。 【 强制 】VARCHAR 是可变长字符串,不预先分配存储空间,长度不要超过 5000。如果存储长度大 于此值,定义字段类型为 TEXT,独立出来一张表,用主键来对应,避免影响其它字段索引效率

约束
约束(constraint)概述
  1. 为什么需要约束
  2. 数据完整性(Data Integrity)是指数据的精确性(Accuracy)和可靠性(Reliability)。它是防止数据库中 存在不符合语义规定的数据和防止因错误信息的输入输出造成无效操作或错误信息而提出的。
  3. 为了保证数据的完整性,SQL规范以约束的方式对表数据进行额外的条件限制。从以下四个方面考虑:
    1. 实体完整性(Entity Integrity) :例如,同一个表中,不能存在两条完全相同无法区分的记录
    2. 域完整性(Domain Integrity) :例如:年龄范围0-120,性别范围“男/女”
    3. 引用完整性(Referential Integrity) :例如:员工所在部门,在部门表中要能找到这个部门
    4. 用户自定义完整性(User-defined Integrity) :例如:用户名唯一、密码不能为空等,本部门 经理的工资不得高于本部门职工的平均工资的5倍。
  4. 什么是约束
  5. 约束是表级的强制规定。
    1. ** 可以在创建表时规定约束(通过 CREATE TABLE 语句),或者在表创建之后通过 ALTER TABLE 语句规定 约束。 **
  6. 约束的分类
  7. 根据约束数据列的限制,约束可分为:
    1. 单列约束:每个约束只约束一列
    2. 多列约束:每个约束可约束多列数据
  8. 根据约束的作用范围,约束可分为:
    1. 列级约束:只能作用在一个列上,跟在列的定义后面
    2. 表级约束:可以作用在多个列上,不与列一起,而是单独定义
  9. 位置 支持的约束类型 是否可以起约束名
  10. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  11. 根据约束起的作用,约束可分为:
    1. NOT NULL 非空约束,规定某个字段不能为空
    2. UNIQUE 唯一约束,规定某个字段在整个表中是唯一的
    3. PRIMARY KEY 主键(非空且唯一)约束
    4. FOREIGN KEY 外键约束
    5. CHECK 检查约束
    6. DEFAULT 默认值约束
    1. 注意: MySQL不支持check约束,但可以使用check约束,而没有任何效果
  7. 查看某个表已有的约束
#information_schema数据库名(系统库)
#table_constraints表名称(专门存储各个表的约束)
SELECT * FROM information_schema.table_constraints
WHERE table_name = '表名称';

非空约束
  1. 作用
  2. 限定某个字段/某列的值不允许为空

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. 关键字
  2. NOT NULL
  3. 特点
  4. 默认,所有的类型的值都可以是NULL,包括INT、FLOAT等数据类型
  5. 非空约束只能出现在表对象的列上,只能某个列单独限定非空,不能组合非空
  6. 一个表可以有很多列都分别限定了非空
  7. 空字符串’'不等于NULL,0也不等于NULL
  8. 添加非空约束
  9. 建表时
CREATE TABLE 表名称(
字段名 数据类型,
字段名 数据类型 NOT NULL,
字段名 数据类型 NOT NULL
);

举例:

CREATE TABLE emp(
id INT(10) NOT NULL,
NAME VARCHAR(20) NOT NULL,
sex CHAR NULL
);
CREATE TABLE student(
sid int,
sname varchar(20) not null,
tel char(11) ,
cardid char(18) not null
);
insert into student values(1,'张三','13710011002','110222198912032545'); #成功
insert into student values(2,'李四','13710011002',null);#身份证号为空
ERROR 1048 (23000): Column 'cardid' cannot be null
insert into student values(2,'李四',null,'110222198912032546');#成功,tel允许为空
insert into student values(3,null,null,'110222198912032547');#失败
ERROR 1048 (23000): Column 'sname' cannot be null

建表后
alter table 表名称 modify 字段名 数据类型 not null;
举例:
ALTER TABLE emp MODIFY sex VARCHAR(30) NOT NULL;
alter table student modify sname varchar(20) not null;

  1. 删除非空约束
alter table 表名称 modify 字段名 数据类型 NULL;#去掉not null,相当于修改某个非注解字段,该字段允
许为空
或
alter table 表名称 modify 字段名 数据类型;#去掉not null,相当于修改某个非注解字段,该字段允许为空


举例:

ALTER TABLE emp
MODIFY sex VARCHAR(30) NULL;

ALTER TABLE emp
MODIFY NAME VARCHAR(15) DEFAULT 'abc' NULL
唯一性约束
  1. 作用
  2. 用来限制某个字段/某列的值不能重复。
  3. 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  4. 关键字
  5. UNIQUE
  6. 特点
  7. 同一个表可以有多个唯一约束。
  8. 唯一约束可以是某一个列的值唯一,也可以多个列组合的值唯一。
  9. 唯一性约束允许列值为空。 在创建唯一约束的时候,如果不给唯一约束命名,就默认和列名相同。
  10. MySQL会给唯一约束的列上默认创建一个唯一索引。
  11. 添加唯一约束
  12. 建表时
create table 表名称(
字段名 数据类型,
字段名 数据类型 unique,
字段名 数据类型 unique key,
字段名 数据类型
);
create table 表名称(
字段名 数据类型,
字段名 数据类型,
字段名 数据类型,
[constraint 约束名] unique key(字段名)
);

举例:
create table student(
sid int,
sname varchar(20),
tel char(11) unique,
cardid char(18) unique key
);

CREATE TABLE t_course(
cid INT UNIQUE,
cname VARCHAR(100) UNIQUE,
description VARCHAR(200)
);

CREATE TABLE USER(
id INT NOT NULL,
NAME VARCHAR(25),
PASSWORD VARCHAR(16),
-- 使用表级约束语法
CONSTRAINT uk_name_pwd UNIQUE(NAME,PASSWORD)
);

表示用户名和密码组合不能重复  
insert into student values(1,'张三','13710011002','101223199012015623');
insert into student values(2,'李四','13710011003','101223199012015624');
mysql> select * from student;
+-----+-------+-------------+--------------------+
| sid | sname | tel | cardid |
+-----+-------+-------------+--------------------+
| 1 | 张三 | 13710011002 | 101223199012015623 |
| 2 | 李四 | 13710011003 | 101223199012015624 |
+-----+-------+-------------+--------------------+
2 rows in set (0.00 sec)

insert into student values(3,'王五','13710011004','101223199012015624'); #身份证号重复
ERROR 1062 (23000): Duplicate entry '101223199012015624' for key 'cardid'
insert into student values(3,'王五','13710011003','101223199012015625');
ERROR 1062 (23000): Duplicate entry '13710011003' for key 'tel'

建表后指定唯一键约束

#字段列表中如果是一个字段,表示该列的值唯一。如果是两个或更多个字段,那么复合唯一,即多个字段的组合是唯
一的
#方式1:
alter table 表名称 add unique key(字段列表);

#方式2:
alter table 表名称 modify 字段名 字段类型 unique;

举例:
ALTER TABLE USER
ADD UNIQUE(NAME,PASSWORD);
ALTER TABLE USER
ADD CONSTRAINT uk_name_pwd UNIQUE(NAME,PASSWORD);
ALTER TABLE USER
MODIFY NAME VARCHAR(20) UNIQUE;

举例:

create table student(
sid int primary key,
sname varchar(20),
tel char(11) ,
cardid char(18)
);

alter table student add unique key(tel);
alter table student add unique key(cardid);
  1. 关于复合唯一约束
create table 表名称(
字段名 数据类型,
字段名 数据类型,
字段名 数据类型,
unique key(字段列表) #字段列表中写的是多个字段名,多个字段名用逗号分隔,表示那么是复合唯一,即多
个字段的组合是唯一的
);
#学生表
create table student(
sid int, #学号
sname varchar(20), #姓名
tel char(11) unique key, #电话
cardid char(18) unique key #身份证号
);
#课程表
create table course(
cid int, #课程编号
cname varchar(20) #课程名称
);
#选课表
create table student_course(
id int,
sid int,
cid int,
score int,
unique key(sid,cid) #复合唯一
);
insert into student values(1,'张三','13710011002','101223199012015623');#成功
insert into student values(2,'李四','13710011003','101223199012015624');#成功
insert into course values(1001,'Java'),(1002,'MySQL');#成功

mysql> select * from student;


+-----+-------+-------------+--------------------+
| sid | sname | tel | cardid |
+-----+-------+-------------+--------------------+
| 1 | 张三 | 13710011002 | 101223199012015623 |
| 2 | 李四 | 13710011003 | 101223199012015624 |
+-----+-------+-------------+--------------------+
2 rows in set (0.00 sec)
mysql> select * from course;
+------+-------+
| cid | cname |
+------+-------+
| 1001 | Java |
| 1002 | MySQL |
+------+-------+
2 rows in set (0.00 sec)

insert into student_course values
(1, 1, 1001, 89),
(2, 1, 1002, 90),
(3, 2, 1001, 88),
(4, 2, 1002, 56);#成功

mysql> select * from student_course;
+----+------+------+-------+
| id | sid | cid | score |
+----+------+------+-------+
| 1 | 1 | 1001 | 89 |
| 2 | 1 | 1002 | 90 |
| 3 | 2 | 1001 | 88 |
| 4 | 2 | 1002 | 56 |
+----+------+------+-------+
4 rows in set (0.00 sec)

insert into student_course values (5, 1, 1001, 88);#失败
#ERROR 1062 (23000): Duplicate entry '1-1001' for key 'sid' 违反sid-cid的复合唯一

  1. 删除唯一约束
  2. 添加唯一性约束的列上也会自动创建唯一索引。
  3. 删除唯一约束只能通过删除唯一索引的方式删除。
  4. 删除时需要指定唯一索引名,唯一索引名就和唯一约束名一样。
  5. 如果创建唯一约束时未指定名称,如果是单列,就默认和列名相同;如果是组合列,那么默认和() 中排在第一个的列名相同。也可以自定义唯一性约束名。
SELECT * FROM information_schema.table_constraints WHERE table_name = '表名'; #查看都有哪
些约束

ALTER TABLE USER
DROP INDEX uk_name_pwd;


注意:可以通过 show index from 表名称; 查看表的索引
PRIMARY KEY 约束
  1. 作用
  2. 用来唯一标识表中的一行记录。
  3. 关键字
  4. primary key
  5. 特点
  6. 主键约束相当于唯一约束+非空约束的组合,主键约束列不允许重复,也不允许出现空值。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
一个表最多只能有一个主键约束,建立主键约束可以在列级别创建,也可以在表级别上创建。
主键约束对应着表中的一列或者多列(复合主键)
如果是多列组合的复合主键约束,那么这些列都不允许为空值,并且组合的值不允许重复。
MySQL的主键名总是PRIMARY,就算自己命名了主键约束名也没用。
当创建主键约束时,系统默认会在所在的列或列组合上建立对应的主键索引(能够根据主键查询 的,就根据主键查询,效率更高)。如果删除主键约束了,主键约束对应的索引就自动删除了。
需要注意的一点是,不要修改主键字段的值。因为主键是数据记录的唯一标识,如果修改了主键的 值,就有可能会破坏数据的完整性。

  1. 添加主键约束
  2. 建表时指定主键约束
create table 表名称(
字段名 数据类型 primary key, #列级模式
字段名 数据类型,
字段名 数据类型
);
create table 表名称(
字段名 数据类型,
字段名 数据类型,
字段名 数据类型,
[constraint 约束名] primary key(字段名) #表级模式
);

举例:
create table temp(
id int primary key,
name varchar(20)
);
mysql> desc temp;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int(11) | NO | PRI | NULL | |
| name | varchar(20) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)
insert into temp values(1,'张三');#成功
insert into temp values(2,'李四');#成功

mysql> select * from temp;
+----+------+
| id | name |
+----+------+
| 1 | 张三 |
| 2 | 李四 |
+----+------+
2 rows in set (0.00 sec)


insert into temp values(1,'张三');#失败
ERROR 1062 (23000): Duplicate(重复) entry(键入,输入) '1' for key 'PRIMARY'
insert into temp values(1,'王五');#失败
ERROR 1062 (23000): Duplicate entry '1' for key 'PRIMARY'
insert into temp values(3,'张三');#成功

mysql> select * from temp;
+----+------+
| id | name |
+----+------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 张三 |
+----+------+
3 rows in set (0.00 sec)

insert into temp values(4,null);#成功
insert into temp values(null,'李琦');#失败
ERROR 1048 (23000): Column 'id' cannot be null


mysql> select * from temp;
+----+------+
| id | name |
+----+------+
| 1 | 张三 |
| 2 | 李四 |
| 3 | 张三 |
| 4 | NULL |
+----+------+
4 rows in set (0.00 sec)

#演示一个表建立两个主键约束
create table temp(
id int primary key,
name varchar(20) primary key
);
ERROR 1068 (42000): Multiple(多重的) primary key defined(定义)
再举例:  

列级约束

CREATE TABLE emp4(
id INT PRIMARY KEY AUTO_INCREMENT ,
NAME VARCHAR(20)
);

表级约束

CREATE TABLE emp5(
id INT NOT NULL AUTO_INCREMENT,
NAME VARCHAR(20),
pwd VARCHAR(15),
CONSTRAINT emp5_id_pk PRIMARY KEY(id)
);
建表后增加主键约束
ALTER TABLE 表名称 ADD PRIMARY KEY(字段列表); #字段列表可以是一个字段,也可以是多个字段,如果是多
个字段的话,是复合主键
ALTER TABLE student ADD PRIMARY KEY (sid);
ALTER TABLE emp5 ADD PRIMARY KEY(NAME,pwd);
  1. 关于复合主键
create table 表名称(
字段名 数据类型,
字段名 数据类型,
字段名 数据类型,
primary key(字段名1,字段名2) #表示字段1和字段2的组合是唯一的,也可以有更多个字段
);

#学生表
create table student(
sid int primary key, #学号
sname varchar(20) #学生姓名
);
#课程表
create table course(
cid int primary key, #课程编号
cname varchar(20) #课程名称
);
#选课表
create table student_course(
sid int,
cid int,
score int,
primary key(sid,cid) #复合主键
);

insert into student values(1,'张三'),(2,'李四');
insert into course values(1001,'Java'),(1002,'MySQL');


mysql> select * from student;
+-----+-------+
| sid | sname |
+-----+-------+
| 1 | 张三 |
| 2 | 李四 |
+-----+-------+
2 rows in set (0.00 sec)
mysql> select * from course;
+------+-------+
| cid | cname |
+------+-------+
| 1001 | Java |
| 1002 | MySQL |
+------+-------+
2 rows in set (0.00 sec)

insert into student_course values(1, 1001, 89),(1,1002,90),(2,1001,88),(2,1002,56);


mysql> select * from student_course;
+-----+------+-------+
| sid | cid | score |
+-----+------+-------+
| 1 | 1001 | 89 |
| 1 | 1002 | 90 |
| 2 | 1001 | 88 |
| 2 | 1002 | 56 |
+-----+------+-------+
4 rows in set (0.00 sec)


insert into student_course values(1, 1001, 100);
ERROR 1062 (23000): Duplicate entry '1-1001' for key 'PRIMARY'

mysql> desc student_course;
+-------+---------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| sid | int(11) | NO | PRI | NULL | |
| cid | int(11) | NO | PRI | NULL | |
| score | int(11) | YES | | NULL | |
+-------+---------+------+-----+---------+-------+
3 rows in set (0.00 sec)

再举例

CREATE TABLE emp6(
id INT NOT NULL,
NAME VARCHAR(20),
pwd VARCHAR(15),
CONSTRAINT emp7_pk PRIMARY KEY(NAME,pwd)
);

  1. 删除主键约束
alter table 表名称 drop primary key;

举例

ALTER TABLE student DROP PRIMARY KEY;
ALTER TABLE emp5 DROP PRIMARY KEY;

说明:删除主键约束,不需要指定主键名,因为一个表只有一个主键,删除主键约束后,非空还存 在。

自增列:AUTO_INCREMENT
  1. 作用
  2. 某个字段的值自增
  3. 关键字
  4. auto_increment
  5. 特点和要求
  6. 一个表最多只能有一个自增长列
  7. 当需要产生唯一标识符或顺序值时,可设置自增长
  8. 自增长列约束的列必须是键列(主键列,唯一键列)
  9. 自增约束的列的数据类型必须是整数类型
  10. 如果自增列指定了 0 和 null,会在当前最大值的基础上自增;如果自增列手动指定了具体值,直接 赋值为具体值。
  11. 如何指定自增约束
  12. 建表时
create table 表名称(
字段名 数据类型 primary key auto_increment,
字段名 数据类型 unique key not null,
字段名 数据类型 unique key,
字段名 数据类型 not null default 默认值,
);
create table 表名称(
字段名 数据类型 default 默认值 ,
字段名 数据类型 unique key auto_increment,
字段名 数据类型 not null default 默认值,,
primary key(字段名)
);

create table employee(
eid int primary key auto_increment,
ename varchar(20)
);
mysql> desc employee;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| eid | int(11) | NO | PRI | NULL | auto_increment |
| ename | varchar(20) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

建表后
alter table 表名称 modify 字段名 数据类型 auto_increment;

例如:
create table employee(
eid int primary key ,
ename varchar(20)
);
alter table employee modify eid int auto_increment;

mysql> desc employee;
+-------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+----------------+
| eid | int(11) | NO | PRI | NULL | auto_increment |
| ename | varchar(20) | YES | | NULL | |
+-------+-------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

  1. 如何删除自增约束
#alter table 表名称 modify 字段名 数据类型 auto_increment;#给这个字段增加自增约束
alter table 表名称 modify 字段名 数据类型; #去掉auto_increment相当于删除

alter table employee modify eid int;

mysql> desc employee;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| eid | int(11) | NO | PRI | NULL | |
| ename | varchar(20) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

  1. MySQL 8.0新特性—自增变量的持久化
  2. 在MySQL 8.0之前,自增主键AUTO_INCREMENT的值如果大于max(primary key)+1,在MySQL重启后,会重 置AUTO_INCREMENT=max(primary key)+1,这种现象在某些情况下会导致业务主键冲突或者其他难以发 现的问题。 下面通过案例来对比不同的版本中自增变量是否持久化。 在MySQL 5.7版本中,测试步骤如 下: 创建的数据表中包含自增主键的id字段,语句如下:
CREATE TABLE test1(
id INT PRIMARY KEY AUTO_INCREMENT
);

插入4个空值,执行如下:

mysql> SELECT * FROM test1;
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
| 4 |
+----+
4 rows in set (0.00 sec)
	删除id为4的记录,语句如下:  
DELETE FROM test1 WHERE id = 4;
再次插入一个空值,语句如下:
INSERT INTO test1 VALUES(0);
查询此时数据表test1中的数据,结果如下:
mysql> SELECT * FROM test1;
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
| 5 |
+----+
4 rows in set (0.00 sec)

从结果可以看出,虽然删除了id为4的记录,但是再次插入空值时,并没有重用被删除的4,而是分配了 5。 删除id为5的记录,结果如下:
DELETE FROM test1 where id=5;
重启数据库,重新插入一个空值。
INSERT INTO test1 values(0);
再次查询数据表test1中的数据,结果如下:

mysql> SELECT * FROM test1;
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
| 4 |
+----+
4 rows in set (0.00 sec)

从结果可以看出,新插入的0值分配的是4,按照重启前的操作逻辑,此处应该分配6。出现上述结果的主 要原因是自增主键没有持久化。 在MySQL 5.7系统中,对于自增主键的分配规则,是由InnoDB数据字典 内部一个 计数器 来决定的,而该计数器只在 内存中维护 ,并不会持久化到磁盘中。当数据库重启时,该 计数器会被初始化。
在MySQL 8.0版本中,上述测试步骤最后一步的结果如下:

mysql> SELECT * FROM test1;
+----+
| id |
+----+
| 1 |
| 2 |
| 3 |
| 6 |
+----+
4 rows in set (0.00 sec)

从结果可以看出,自增变量已经持久化了。 MySQL 8.0将自增主键的计数器持久化到 重做日志 中。每次计数器发生改变,都会将其写入重做日志 中。如果数据库重启,InnoDB会根据重做日志中的信息来初始化计数器的内存值。

FOREIGN KEY 约束
  1. 作用
  2. 限定某个表的某个字段的引用完整性。
  3. 比如:员工表的员工所在部门的选择,必须在部门表能找到对应的部分。 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  4. 关键字
  5. FOREIGN KEY
  6. 主表和从表/父表和子表
  7. 主表(父表):被引用的表,被参考的表
  8. 从表(子表):引用别人的表,参考别人的表
  9. 例如:员工表的员工所在部门这个字段的值要参考部门表:部门表是主表,员工表是从表。
  10. 例如:学生表、课程表、选课表:选课表的学生和课程要分别参考学生表和课程表,学生表和课程表是 主表,选课表是从表。
  11. 特点
  12. 从表的外键列,必须引用/参考主表的主键或唯一约束的列 为什么?
    1. 因为被依赖/被参考的值必须是唯一的
  13. 在创建外键约束时,如果不给外键约束命名,默认名不是列名,而是自动产生一个外键名(例如 student_ibfk_1;),也可以指定外键约束名。
  14. 创建(CREATE)表时就指定外键约束的话,先创建主表,再创建从表
  15. 删表时,先删从表(或先删除外键约束),再删除主表
  16. 当主表的记录被从表参照时,主表的记录将不允许删除,如果要删除数据,需要先删除从表中依赖 该记录的数据,然后才可以删除主表的数据
  17. 在“从表”中指定外键约束,并且一个表可以建立多个外键约束
  18. 从表的外键列与主表被参照的列名字可以不相同,但是数据类型必须一样,逻辑意义一致。如果类 型不一样,创建子表时,就会出现错误“ERROR 1005 (HY000): Can’t create table’database.tablename’(errno: 150)”。
    1. 例如:都是表示部门编号,都是int类型
  19. 当创建外键约束时,系统默认会在所在的列上建立对应的普通索引。但是索引名是外键的约束 名。(根据外键查询效率很高)
  20. 删除外键约束后,必须 手动 删除对应的索引
  21. 添加外键约束
  22. 建表时
create table 主表名称(
字段1 数据类型 primary key,
字段2 数据类型
);
create table 从表名称(
字段1 数据类型 primary key,
字段2 数据类型,
[CONSTRAINT <外键约束名称>] FOREIGN KEY(从表的某个字段) references 主表名(被参考字段)
);
#(从表的某个字段)的数据类型必须与主表名(被参考字段)的数据类型一致,逻辑意义也一样
#(从表的某个字段)的字段名可以与主表名(被参考字段)的字段名一样,也可以不一样
-- FOREIGN KEY: 在表级指定子表中的列
-- REFERENCES: 标示在父表中的列
create table dept( #主表
did int primary key, #部门编号
dname varchar(50) #部门名称
);
create table emp(#从表
eid int primary key, #员工编号
ename varchar(5), #员工姓名
deptid int, #员工所在的部门
foreign key (deptid) references dept(did) #在从表中指定外键约束
#emp表的deptid和和dept表的did的数据类型一致,意义都是表示部门的编号
);
说明:
(1)主表dept必须先创建成功,然后才能创建emp表,指定外键成功。
(2)删除表时,先删除从表emp,再删除主表dept

建表后
一般情况下,表与表的关联都是提前设计好了的,因此,会在创建表的时候就把外键约束定义好。不 过,如果需要修改表的设计(比如添加新的字段,增加新的关联关系),但没有预先定义外键约束,那 么,就要用修改表的方式来补充定义。
格式:

ALTER TABLE 从表名 ADD [CONSTRAINT 约束名] FOREIGN KEY (从表的字段) REFERENCES 主表名(被引用
字段) [on update xx][on delete xx];

举例:

举例:
ALTER TABLE emp1
ADD [CONSTRAINT emp_dept_id_fk] FOREIGN KEY(dept_id) REFERENCES dept(dept_id);
举例:
create table dept(
did int primary key, #部门编号
dname varchar(50) #部门名称
);
create table emp(
eid int primary key, #员工编号
ename varchar(5), #员工姓名
deptid int #员工所在的部门
);
#这两个表创建时,没有指定外键的话,那么创建顺序是随意
alter table emp add foreign key (deptid) references dept(did);
  1. 演示问题
  2. 失败:不是键列
create table dept(
did int , #部门编号
dname varchar(50) #部门名称
);
create table emp(
eid int primary key, #员工编号
ename varchar(5), #员工姓名
deptid int, #员工所在的部门
foreign key (deptid) references dept(did)
);
#ERROR 1215 (HY000): Cannot add foreign key constraint 原因是dept的did不是键列
  1. 失败:数据类型不一致
create table dept(
did int primary key, #部门编号
dname varchar(50) #部门名称
);
create table emp(
eid int primary key, #员工编号
ename varchar(5), #员工姓名
deptid char, #员工所在的部门
foreign key (deptid) references dept(did)
);
#ERROR 1215 (HY000): Cannot add foreign key constraint 原因是从表的deptid字段和主表的did字
段的数据类型不一致,并且要它俩的逻辑意义一致

  1. 成功,两个表字段名一样
create table dept(
did int primary key, #部门编号
dname varchar(50) #部门名称
);
create table emp(
eid int primary key, #员工编号
ename varchar(5), #员工姓名
did int, #员工所在的部门
foreign key (did) references dept(did)
#emp表的deptid和和dept表的did的数据类型一致,意义都是表示部门的编号
#是否重名没问题,因为两个did在不同的表中
);
  1. 添加、删除、修改问题
create table dept(
did int primary key, #部门编号
dname varchar(50) #部门名称
);
create table emp(
eid int primary key, #员工编号
ename varchar(5), #员工姓名
deptid int, #员工所在的部门
foreign key (deptid) references dept(did)
#emp表的deptid和和dept表的did的数据类型一致,意义都是表示部门的编号
);

insert into dept values(1001,'教学部');
insert into dept values(1003, '财务部');
insert into emp values(1,'张三',1001); #添加从表记录成功,在添加这条记录时,要求部门表有1001部门
insert into emp values(2,'李四',1005);#添加从表记录失败
ERROR 1452 (23000): Cannot add(添加) or update(修改) a child row: a foreign key
constraint fails (`atguigudb`.`emp`, CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`deptid`)
REFERENCES `dept` (`did`)) 从表emp添加记录失败,因为主表dept没有1005部门

mysql> select * from dept;
+------+--------+
| did | dname |
+------+--------+
| 1001 | 教学部 |
| 1003 | 财务部 |
+------+--------+
2 rows in set (0.00 sec)
mysql> select * from emp;
+-----+-------+--------+
| eid | ename | deptid |
+-----+-------+--------+
| 1 | 张三 | 1001 |
+-----+-------+--------+
1 row in set (0.00 sec)


update emp set deptid = 1002 where eid = 1;#修改从表失败
ERROR 1452 (23000): Cannot add(添加) or update(修改) a child row(子表的记录): a
foreign key constraint fails(外键约束失败) (`atguigudb`.`emp`, CONSTRAINT `emp_ibfk_1`
FOREIGN KEY (`deptid`) REFERENCES `dept` (`did`)) #部门表did字段现在没有1002的值,所以员工
表中不能修改员工所在部门deptid为1002
update dept set did = 1002 where did = 1001;#修改主表失败
ERROR 1451 (23000): Cannot delete(删除) or update(修改) a parent row(父表的记录): a
foreign key constraint fails (`atguigudb`.`emp`, CONSTRAINT `emp_ibfk_1` FOREIGN KEY
(`deptid`) REFERENCES `dept` (`did`)) #部门表did的1001字段已经被emp引用了,所以部门表的1001字
段就不能修改了。
update dept set did = 1002 where did = 1003;#修改主表成功 因为部门表的1003部门没有被emp表引
用,所以可以修改
delete from dept where did=1001; #删除主表失败
ERROR 1451 (23000): Cannot delete(删除) or update(修改) a parent row(父表记录): a
foreign key constraint fails (`atguigudb`.`emp`, CONSTRAINT `emp_ibfk_1` FOREIGN KEY
(`deptid`) REFERENCES `dept` (`did`)) #因为部门表did的1001字段已经被emp引用了,所以部门表的
1001字段对应的记录就不能被删除

总结:约束关系是针对双方的
添加了外键约束后,主表的修改和删除数据受约束
添加了外键约束后,从表的添加和修改数据受约束
在从表上建立外键,要求主表必须存在
删除主表时,要求从表从表先删除,或将从表中外键引用该主表的关系先删除

  1. 约束等级
  2. Cascade方式 :在父表上update/delete记录时,同步update/delete掉子表的匹配记录
  3. Set null方式 :在父表上update/delete记录时,将子表上匹配记录的列设为null,但是要注意子 表的外键列不能为not null
  4. No action方式 :如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作 Restrict方式 :同no action, 都是立即检查外键约束
  5. Set default方式 (在可视化工具SQLyog中可能显示空白):父表有变更时,子表将外键列设置 成一个默认的值,但Innodb不能识别

如果没有指定等级,就相当于Restrict方式。 对

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值