Mysql的安装


一、 下载MySQL

Mysql官网下载地址https://downloads.mysql.com/archives/installer/

    **本篇文章选择的是5.7.31版本**

二、 安装MySQL

1. 选择设置类型

双击运行mysql-installer-community-5.7.31.0.msi,这里选择是自定义安装,所以直接选择“Custom”,点击“Next”

“Developer Default”是开发者默认

“Server only”仅作为服务器安装

“Client only”仅作为客户端安装

“Full”是完整安装

“Custom”是自定义安装

2. 选择安装的产品和功能

依次点开“MySQL Servers”、“MySQL Servers”、“MySQL Servers 5.7”、“MySQL Servers 5.7.31 -64”,然后点击绿色箭头将选中的版本移植到右边选中框中

注:这里选择的是64位系统

3. 先选中“MySQL Server 5.7.31” ,之后点击下方的蓝色“Advanced Options”

4. 自定义安装路径和数据存放路径,更改完成之后点击“OK”

注:虽说安装在C盘也没毛病,但是个人建议不要安装到C盘,我安装到了D盘

5. 直接点击“OK”

注:这里因为选择了我自己创建的路径,故程序提示路径已存在,是否要继续

6. 更改完成之后点击“Next”

7. 确认一下安装路径和数据存放路径对不对,没有问题的话就直接点击Next

8. 直接点击“Yes”

9. 先选中“MySQL Server5.7.31”,若电脑中没有这个环境,会提示安装“Microsoft Visual C++ 2013”环境,点击“Execute”

10. 勾选中之后点击“Install”

11. 安装完成,点击“Close”

12. 看到这个对勾就说明这个环境安装上了,直接点击“Next”

13. 直接点击“Execute”

14. 出现这个图标就说明安装成功了,直接点击“Next”

15. 直接点击“Next”

16. 直接点击“Next”

17. 选择安装类型

个人学习研究的话选择“Development Computer”则可以,我这里选择是“Development Computer”

Development Computer 适合个人开发学习使用的

Server Computer 是像主要用于像FTP,email,web服务器等等

Dedicated Computer 只用作MySQL服务器

18. MySQL的默认端口是3306,不需要作修改,直接点击“Next”

19. 这里需要设置MySQL的密码,不需要太复杂,设置完了直接点击“Next”

20. 直接点击“Next”

21. 直接点击“Execute”,开始安装,点击安装之后需要等待片刻

注意,如果卡到staring the server不动的话,看看自己电脑的名字是不是中文,如果是改成英文即可

22. 点击“Finish”

23. 点击“Next”

24. 点击“Finish”,此时MySQL安装完成

25. 在开始界面搜索mysql,点击第一个MySQL 5.7 Command Line Client

26. 输入刚才设置的MySQL的密码,按Enter键(回车键)

27. 出现下面界面就说明已经安装成功了

至此,MySQL数据库安装成功

三、 配置环境变量

1. 找到MySQL的安装路径

2. 右击此电脑选择“属性”

3. 先点击“高级系统设置”,再点击“环境变量”

4. 在“系统变量”中选择“Path”,点击“编辑”

5. 点击“新建”,将MySQL的安装路径添加上去,之后点击“确定”

6. 直接点击“确定”

7. 验证环境变量是否配置好,按下“window+R”键 弹出运行框,输入“cmd”进入window命令行

8. 输入“mysql -u root -p”按下回车

9. 输入密码,按下“Enter键(回车键)”,出现下面界面则是已经配置成功

制作不易,有帮助的话还希望能给个 点赞 支持下,谢谢大家。

四:问题解决

使用cmd运行命令,却显示“不是内部或外部命令,也不是可运行的程序或批处理文件。例如:
在这里插入图片描述
‘XXX’is not recognized as an internal or external command,operable program or batch file.报的英文版错误翻译也是前面的意思。一般就是path值的问题,进入环境变量调试就好。
1.快捷键win+R键弹出运行窗口,然后输入sysdm.cpl敲回车可直接进入系统属性窗口。

2.在系统环境变量下path中查找是否有下面三项,如果没有则分别新建三个变量值:
“%SystemRoot%\system32”
“%SystemRoot%;%SystemRoot%\System32\Wbem”
“C:\Windows\SysWOW64”
win7系统没有在path里面新建值的功能,只能是把这三个变量值每个后面加;然后一起填入进去即可。(长长的一条)
5.最后win+R,输入cmd回车,重新进入cmd界面就好了。

五.总结

image-20230614001048243

datagrip-mysql可视化软件

一.前言

日常开发中少不了各种可视化数据库管理工具。如果需要同时能连接多种数据库,大家肯定都会想到 DBeaverNavicat Premium。本文介绍另一个十分好用且强大的工具:DataGrip

DataGripJetBrains 公司推出的管理数据库的产品。对于 JetBrains 公司,开发者肯定都不陌生,IDEAReSharper 都是这个公司的产品,用户体验非常不错。


二.JetBrains DataGrip介绍:

如果用一句话来介绍DataGrip这款工具:

多种数据库,一个工具!在这里插入图片描述

DataGrip 提供 Windows 版本和 macOS 版本以及Linux版本,支持几乎所有主流的关系数据库产品,如:PostgreSQL、MySQL、Oracle Database、SQL Server、Azure、Amazon Redshift、SQLite、DB2、H2、Sybase、Exasol、Apache Derby、MariaDB、HyperSQL、ClickHouse、Cassandra、Vertica、Greenplum、Apache Hive、Snowflake 等,并且提供了简单易用的界面,开发者上手几乎不会遇到任何困难。


三.安装教程

下载安装
DataGrip官网下载

点击下载
在这里插入图片描述
选择运行平台,点击下载
在这里插入图片描述
等待下载即可
在这里插入图片描述
下载完成后,双击安装
在这里插入图片描述

点击Next
在这里插入图片描述
选择安装路径,点击Next
在这里插入图片描述

安装选项,选完之后点击Next
在这里插入图片描述

安装选项说明
在这里插入图片描述

点击install
在这里插入图片描述

等待安装
在这里插入图片描述

点击Finish
在这里插入图片描述

安装完成之后,双击图标
在这里插入图片描述
导入用户配置,没有配置的话直接点击 OK
在这里插入图片描述
进入去之后不用管,点击Evaluate for free,然后点击Evaluate
在这里插入图片描述


选择主题方案,然后点击Next: Database Options
在这里插入图片描述
设置默认的SQL语言和脚本目录

选择你常用的SQL设为默认

这里我选择MySQL

然后点击Start using DataGrip
在这里插入图片描述

四.默认是英文的,下面开始更换中文语言教程

进去之后在菜单栏选择File
在这里插入图片描述
点击Settings,或者按快捷键Ctrl+Alt+S
在这里插入图片描述
点击Plugins
在这里插入图片描述
搜索Chinese,选择第二个中文语言包,点击install
在这里插入图片描述
等待下载进度条
在这里插入图片描述
点击Restart IDE,等待自动重启即可
在这里插入图片描述
更换中文语言成功
在这里插入图片描述

更换中文语言教程结束


五.DataGrip的基本使用步骤【连接数据库】

点击新建/New > 点击数据源/Data Source > 选择你需要的SQL

这里我以MySQL示例!其它SQL都大同小异
在这里插入图片描述
输入名称用户名和密码

第一次连接mysql会报错,提示你缺少驱动,会自动提示你安装

点击Download Driver Files就会自动帮你安装连接驱动
在这里插入图片描述
连接之前检查一下MySQL服务是否开启
在这里插入图片描述

点击测试连接/Test Connection后报错:服务器返回无效的时区,需要设置”serverTimezone”属性Server returns invalid timezone. Go to 'Advanced' tab and set 'serverTimezon
在这里插入图片描述
英文版
在这里插入图片描述

解决方法:

检查MySQL设置时区:

Win+R打开运行cmd
在这里插入图片描述
打开cmd窗口
在这里插入图片描述
连接数据库,输入mysql -hlocalhost -uroot -p回车输入密码
在这里插入图片描述
输入show variables like'%time_zone';回车,显示时区配置
在这里插入图片描述
如果显示 SYSTEM 就是没有设置时区

输入set global time_zone = '+8:00';回车
在这里插入图片描述
显示Query OK, 0 rows affected (0.00 sec)就是设置成功了。

然后关闭cmd重新打开cmd重新连接数据库,连接成功后输入show variables like'%time_zone'; 回车
在这里插入图片描述
显示+08:00就可以了

这个时候重新连接数据库即可

点击 测试连接/Test Connection进行测试连接
在这里插入图片描述

时区问题解决


点击确定
在这里插入图片描述

连接数据库教程结束


六.使用DataGrip操作MySQL

如下图:

左边的数字代表一共有个数据库(例如我:默认4个数据库,我自己添加了一个,所以是5个

右边的数字代表一共选中了几个数据库(例如我:我一个都没选中所以是0

点击之后会展开所有数据库,可以选中取消选中
在这里插入图片描述
选中的数据库会在架构里面显示出来

比如这里我选中一个数据库
在这里插入图片描述
选中的数据库会在架构里面显示出来
在这里插入图片描述
点击还可以展开显示里面的表
在这里插入图片描述
选中要查询的数据库,右键:新建>查询控制台

在这里插入图片描述
DataGrip会给你智能代码补全
在这里插入图片描述
查询表的所有字段
在这里插入图片描述
右键:执行(或者上面有快捷键)
在这里插入图片描述
也可以点击这个绿色三角板
在这里插入图片描述
查询结果
在这里插入图片描述

JetBrains DataGrip安装和使用的详细教程到此结束

SQL基本介绍

SQL (Structured Query Language):结构化查询语言,是一种针对关系型数据库特殊标准化的编程语言

  • SQL是一种编程语言
  • 能够实现用户数据库查询和程序设计
  • SQL根据操作不同,分为几类
    • DQL: Data Query Language,数据查询语言,用于查询和检索数据
    • DML: Data Manipulation Language,数据操作语言,用于数据的写操作(增删改)
      TPL:Transaction Process Language,事务处理语言,辅助DML进行事务操作(因此也归属于DML)
    • DDL: Data Definition Language,数据定义语言,用于创建数据结构
    • DCL: Data Control Language,数据控制语言,用于用户权限管理

注意:

1、SQL虽然是编程语言,但是目前数据库通常只用来进行数据管理(逻辑部分给其他编程语言)

2、SQL虽然是针对关系型数据库的通用语言,但是不同的产品操作指令不完全通用

3、数据库连接资源有限,用完即关闭

1、SQL基本语法

目标:了解SQL的基本语法规则

SQL语法规则

  • 基础SQL指令通常是以行为单位
  • SQL指令需要语句结束符,默认是英文分号:;、\g、\G
    • \G:主要用于查询数据,立体展示结果
  • SQL指令类似自然语言
  • 编写的SQL中如果用到了关键字或者保留字,需要使用反引号``来包裹,让系统忽略

1、结构创建

  • create 结构类型 结构名 结构描述;

2、显示结构

  • 显示结构:show 结构类型(复数);
  • 显示结构创建详情:show create 结构类型 结构名;

3、数据操作(数据表)

  • 新增数据: insert into 表名 values;
  • 查看数据: select from 表名;
  • 更新数据: update 表名 set;
  • 删除数据: delete from 表名;

小结

1、SQL是一种类似于自然语言的编程语言

  • 基本SQL指令以行为单位
  • SQL指令需要语句结束符

2、根据数据库的对象层级,可以将基础SQL操作分为三类

  • 库操作:数据库相关操作
  • 表操作:数据表(字段)相关操作
  • 数据操作:数据相关操作

2.数据库中的数据类型

使用MySQL数据库存储数据时,不同的数据类型决定了 MySQL存储数据方式的不同。为此,MySQL数据库提供了多种数据类型,其中包括整数类型、浮点数类型、定点 数类型、日期和时间类型、字符串类型、二进制…等等数据类型。

img

1.整数类型

根据数值取值范围的不同MySQL 中的整数类型可分为5种,分别是TINYINT、SMALUNT、MEDIUMINT、INT和 BIGINT。下图列举了 MySQL不同整数类型所对应的字节大小和取值范围而最常用的为INT类型的,

数据类型 字节数 无符号数的取值范围 有符号数的取值范围
TINYINT 1 0~255 -128~127
SMALLINT 2 0~65535 -32768~32768
MEDIUMINT 3 0~16777215 -8388608~8388608
INT 4 0~4294967295 -2147483648~ 2147483648
BIGINT 8 0~18446744073709551615 -9223372036854775808~9223372036854775808

2.浮点数类型和定点数类型

在MySQL数据库中使用浮点数和定点数来存储小数。浮点数的类型有两种:单精度浮点数类型(FLOAT)和双精度浮点数类型(DOUBLE)。而定点数类型只有一种即DECIMAL类型。下图列举了 MySQL中浮点数和定点数类型所对应的字节大小及其取值范围:

数据类型 字节数 有符号的取值范围 无符号的取值范围
FLOAT 4 -3.402823466E+38~-1.175494351E-38 0和1.175494351E-38~3.402823466E+38
DOUBLE 8 -1.7976931348623157E+308~2.2250738585072014E-308 0和2.2250738585072014E-308~1.7976931348623157E+308
DECIMAL(M,D) M+2 -1.7976931348623157E+308~2.2250738585072014E-308 0和2.2250738585072014E-308~1.7976931348623157E+308

从上图中可以看出:DECIMAL类型的取值范围与DOUBLE类型相同。但是,请注意:DECIMAL类型的有效取值范围是由M和D决定的。其中,M表示的是数据的长 度,D表示的是小数点后的长度。比如,将数据类型为DECIMAL(6,2)的数据6.5243 插人数据库后显示的结果为6.52

img

3.字符串类型

在MySQL中常用CHARVARCHAR 表示字符串。两者不同的是:VARCHAR存储可变长度的字符串。
当数据为CHAR(M)类型时,不管插入值的长度是实际是多少它所占用的存储空间都是M个字节;而VARCHAR(M)所对应的数据所占用的字节数为实际长度加1

插入值 CHAR(3) 存储需求 VARCHAR(3) 存储需求
‘’ ‘’ 3个字节 ‘’ 1个字节
‘a’ ‘a’ 3个字节 ‘a’ 2个字节
‘ab’ ‘ab’ 3个字节 ‘ab’ 3个字节
‘abc’ ‘ab’ 3个字节 ‘abc’ 4个字节
‘abcd’ ‘ab’ 3个字节 ‘abc’ 4字节

img

4.字符串类型

文本类型用于表示大文本数据,例如,文章内容、评论、详情等,它的类型分为如下4种:

数据类型 储存范围
TINYTEXT 0~255字节
TEXT 0~65535字节
MEDIUMTEXT 0~16777215字节
LONGTEXT 0~4294967295字节

5.日期与时间类型

MySQL提供的表示日期和时间的数据类型分别是 :YEAR、DATE、TIME、DATETIME 和 TIMESTAMP。下图列举了日期和时间数据类型所对应的字节数、取值范围、日期格式以及零值:

数据类型 字节数 取值范围 日期格式 零值
YEAR 1 1901~2155 YYYY 0000
DATE 4 1000-01-01~9999-12-31 YYYY-MM-DD 0000-00-00
TIME 3 -838:59:59~ 838:59:59 HH:MM:SS 00:00:00
DATETIME 8 1000-01-01 00:00:00~9999-12-31 23:59:59 YYYY-MM-DD HH:MM:SS 0000-00-00 00:00:00
TIMESTAMP 4 1970-01-01 00:00:01~2038-01-19 03:14:07 YYYY-MM-DD HH:MM:SS 0000-00-00 00:00:00
5.1 YEAR类型

YEAR类型用于表示年份,在MySQL中,可以使用以下三种格式指定YEAR类型 的值。
1、使用4位字符串或数字表示,范围为’1901’—’2155’或1901—2155。例如,输人 ‘2019’或2019插人到数据库中的值均为2019。
2、使用两位字符串表示,范围为’00’—‘99’。其中,‘00’—’69’范围的值会被转换为 2000—2069范围的YEAR值,‘70’—’99’范围的值会被转换为1970—1999范围的YEAR 值。例如,输人’19’插人到数据库中的值为2019。
3、使用两位数字表示,范围为1—99。其中,1—69范围的值会被转换为2001— 2069范围的YEAR值,70—99范围的值会被转换为1970—1999范围的YEAR值。例 如,输人19插入到数据库中的值为2019。
请注意:当使用YEAR类型时,一定要区分’0’和0。因为字符串格式的’0’表示的YEAR值是2000而数字格式的0表示的YEAR值是0000。

5.2 TIME类型

TIME类型用于表示时间值,它的显示形式一般为HH:MM:SS,其中,HH表示小时, MM表示分,SS表示秒。在MySQL中,可以使用以下3种格式指定TIME类型的值。
1、以’D HH:MM:SS’字符串格式表示。其中,D表示日可取0—34之间的值, 插人数据时,小时的值等于(DX24+HH)。例如,输入’2 11:30:50’插人数据库中的日期为59:30:50。
2、以’HHMMSS’字符串格式或者HHMMSS数字格式表示。 例如,输人’115454’或115454,插入数据库中的日期为11:54:54
3、使用CURRENT_TIME或NOW()输人当前系统时间。

5.3 DATETIME类型

DATETIME类型用于表示日期和时间,它的显示形式为’YYYY-MM-DD HH: MM:SS’,其中,YYYY表示年,MM表示月,DD表示日,HH表示小时,MM表示分,SS 表示秒。在MySQL中,可以使用以下4种格式指定DATETIME类型的值。
以’YYYY-MM-DD HH:MM:SS’或者’YYYYMMDDHHMMSS’字符串格式表示的日期和时间,取值范围为’1000-01-01 00:00:00’—‘9999-12-3 23:59:59’。例如,输人’2019-01-22 09:01:23’或 ‘20140122_0_90123’插人数据库中的 DATETIME 值都为 2019-01-22 09:01:23。
1、以’YY-MM-DD HH:MM:SS’或者’YYMMDDHHMMSS’字符串格式表示的日期和时间,其中YY表示年,取值范围为’00’—‘99’。与DATE类型中的YY相同,‘00’— ’69’范围的值会被转换为2000—2069范围的值,‘70’—’99’范围的值会被转换为1970—1999范围的值。
2、以YYYYMMDDHHMMSS或者YYMMDDHHMMSS数字格式表示的日期 和时间。例如,插入20190122090123或者190122090123,插人数据库中的DATETIME值都 为 2019-01-22 09:01:23。
3、使用NOW来输人当前系统的日期和时间。

5.4 TIMESTAMP类型

TIMESTAMP类型用于表示日期和时间,它的显示形式与DATETIME相同但取值范围比DATETIME小。在此,介绍几种TIMESTAMP类型与DATATIME类型不同的形式:
1、使用CURRENT_TIMESTAMP输人系统当前日期和时间。
2、输人NULL时系统会输人系统当前日期和时间。
3、无任何输人时系统会输入系统当前日期和时间。

6.二进制类型

在MySQL中常用BLOB存储二进制类型的数据,例如:图片、PDF文档等。BLOB类型分为如下四种:

数据类型 储存范围
TINYBLOB 0~255字节
BLOB 0~65535字节
MEDIUMBLOB 0~16777215字节
LONGBLOB 0~4294967295字节

3、SQL库操作

目标:掌握数据库相关SQL指令

  • 创建数据库
  • 显示数据库
  • 使用数据库
  • 修改数据库
  • 删除数据库

DDL

目标:学习基本的SQL操作,实现数据库的基本管理

  • SQL基本语法
  • SQL库操作
  • SQL表操作
  • SQL数据操作

1.数据库操作

1) 创建数据库

创建数据库:根据项目需求创建一个存储数据的仓库

  • 使用create database 数据库名字创建
    • 数据库层面可以指定字符集:charset / character set
    • 数据库层面可以指定校对集:collate
  • 创建数据库会在磁盘指定存放处产生一个文件夹
  • 创建语法: create database 数据库名字 [数据库选项];

1、创建一个指定名字的数据库

1
2
create database db_1;
1

2、创建一个指定字符集的数据库

1
2
create database db_2 charset utf8MB4;
1

3、创建一个指定校对集的数据库

1
2
create database db_3 charset utf8MB4 collate utf8mb4_general_ci;
1

小结

1、数据库的创建是存储数据的基础,数据库的创建通常是一次性的

2、创建数据库的语法包含几个部分

  • 关键字: create database

  • 数据库名字: 自定义名字

    • 数字、字母和下划线组成
    • 不区分大小写
    • 数字不能开头
    • 使用下划线法创建复杂数据库名字
  • 数据库选项:

    非必须的规定

    • 字符集:charset /character set 字符集。非必须,默认继承DBMS
    • 校对集:collate 校对集。非必须,依赖字符集

3、创建好的数据库可以在数据存储指定地点(安装时指定)看到

2) 显示数据库

显示数据库:通过客户端指令来查看已有数据库

  • 数据库的查看是根据用户权限限定的
  • 数据库的查看分为两种查看方式:
    • 查看全部数据库: show databases;
    • 查看数据库创建指令: show create database 数据库名字;

1、显示所有数据库

1
show databases;

2、显示数据库创建指令

1
show create database db_1;

小结

1、查看数据库分为两种方式

  • 查看全部
  • 查看具体创建指令

2、查看数据库的目的和应用

  • 开发人员确认数据库是否存在
  • 数据库管理员维护

3) 使用数据库

使用数据库:指在进行具体SQL指令之前,让系统知道操作针对的是哪个数据库

  • 数据库的操作通常是针对数据表或者数据
  • 通过使用数据库可以让后续指令默认针对具体数据库环境
  • 使用数据库语法:use 数据库名字;

1、使用某个数据库

1
use db_1;

小结

1、使用数据库的指令是:use 数据库名字;

2、使用数据库的目标

  • 让系统知道后续SQL指令都是针对当前选择的数据库
  • 简化后续SQL指令的复杂度(如果不指定数据库,那么所有的SQL操作都必须强制指定数据库名字)

4) 修改数据库

修改数据库:修改数据库的相关库选项

  • 数据库名字不可修改(老版本可以)
    • 先新增
    • 后迁移
    • 最后删除
  • 数据库修改分为两个部分(库选项)
    • 字符集
    • 校对集
  • 数据库修改指令(与创建指令差不多): alter database 数据库名字 库选项

1、修改数据库字符集

1
2
alter database db_2 charset gbk;
1

2、修改数据库校对集(如果字符集修改必须同时改变字符集)

1
2
alter database db_3 charset gbk collate gbk_chinese_ci;
1

小结

1、数据库的修改只能修改库选项,不能修改名字

2、字符集的修改指令使用alter,其他跟创建指令一致

3、数据库修改通常有两部分

  • 字符集修改
  • 校对集修改(校对集必须对应字符集)

4、一般都不会使用数据库修改(一般要改也是删除后新增)

5) 删除数据库

删除数据库:将当前已有数据库删除

  • 删除数据库会删除数据库内所有的表和数据
  • 删除数据库操作要慎重(删前备份)
  • 删除数据库后,对应的存储文件夹就会消失
  • 删除语法:drop database[if exists] 数据库名字;

1、删除某个数据库

1
2
drop database[if exists] db_1;
truncate table db_1;删除指定表,并重新创建该表

小结

1、删除数据库使用指令:drop database 数据库名字;

truncate table表名;删除指定表,并重新创建该表

2、数据库的删除不可逆

  • 删除会清空当前数据库内的所有数据表(表里数据一并删除)
  • 删除数据库会将对应的文件夹从磁盘抹掉
  • 数据库删除要谨慎(一般不建议删除)

6) 总结

1、数据库的操作通常是一次性的,即在进行业务代码开展之前将数据库维护好

2、数据库的删除需要非常慎重,尤其是生产环境,数据库的删除是不可逆(会将数据库中的所有数据全部删除)

2.表(字段)操作

目标:掌握数据库和字段的相关操作指令,熟练运用这些指令完成数据表的增删改查

  • 创建数据表
  • 显示数据表
  • 查看表结构
  • 更改数据表
  • 更改字段
  • 删除数据表

1) 创建数据表

目标:了解数据表创建的语法,掌握创建规则

创建数据表:根据业务需求,确定数据表的字段信息,然后创建表结构

  • 表与字段不分家,相辅相成
  • 表的创建需要指定存储的数据库
    • 明确指定数据库:数据库.表名
    • 先使用数据库:use 数据库名字
  • 字段至少需要指定名字、类型
  • 数据库表不限定字段数量
    • 每个字段间使用逗号,分隔
    • 最后一个字段不需要逗号
  • 表可以指定表选项(都有默认值)
    • 存储引擎:engine [=] 具体存储引擎
    • 字符集:[default] charset 具体字符集(继承数据库)
    • 校对集:collate(继承数据库)
  • 表创建语法: create table [数据库名.]表名(字段名 字段类型,...字段名 字段类型)表选项;

1、创建简单数据表(指定数据库创建数据表)

1
2
3
4
create table db_2.t_1(
name varchar(50)
);
123

2、创建数据表——多字段

1
2
3
4
5
6
7
8
# 使用数据库(进入数据库环境)
use db_2;
create table t_2(
name varchar(50),
age int,
gender varchar(10)
);
1234567

3、创建数据表——表选项

1
2
3
4
create table t_3(
name varchar(50)
)engine Innodb charset utf8MB4;
123

小结

1、创建数据库表是为了存储具体数据

2、数据表的创建与字段是同时存在的

3、数据表的创建需要指定数据库

  • 在表名字前指定数据库:数据库名.表名
  • 进入数据库环境(常用)

4、一张数据表用来存一组相关数据

5、扩展:存储引擎是指数据存储和管理的方式,MySQL中提供了多种存储引擎,一般使用默认存储引擎

  • InnoDB
    • 默认存储引擎
    • 支持事务处理和外键
    • 数据统一管理
  • MyIsam
    • 不支持事务和外键
    • 数据、表结构、索引独立管理
    • MySQL5.6以后不再维护

6、扩展:如果想创建一个与已有表一样的数据表,MySQL提供了一种便捷的复制模式

  • create table 表名 like 数据库名字.表名;

2) 显示数据表

目标:了解如何查看数据表结构

显示数据表:客户端通过指令显示已有的数据表

  • 数据表的显示跟用户权限有关
  • 显示数据表有两种方式
    • 显示所有数据表:show tables [from 指定数据库];
    • 显示部分:show tables like 'pattern';
    • 显示具体数据表的创建指令:show create table 表名;

1、显示所有数据表——当前数据库下

1
2
show tables;
1

2、显示所有数据表——指定数据库

1
2
show tables from db_3;
1

3、显示部分关联数据表——匹配

1
2
show tables like '%like';	# _表示匹配一个字符(固定位置),%表示匹配N个字符
1

4、显示数据表的创建指令

1
2
show create table t_1; # 看到的结果未必一定是真实创建的指令(系统会加工)
1

小结

1、显示数据表有两种形式

  • 显示所有数据表:show tables [from 指定数据库];
  • 显示部分:show tables like ‘pattern’; 匹配模式:_匹配单个字符,%匹配不限量字符
  • 显示数据表创建指令:show create table 表名;

2、显示数据表通常是为了验证数据表是否存在或者验证数据表的创建指令是否正确

3、在显示数据的时候可以使用不同的语句结束符

  • \g:与普通分号无区别
  • \G:纵向显示列数据

3) 查看数据表

目标:了解查看数据表的概念和掌握数据表查看的语法

查看数据表:指查看数据表中的具体结构

  • 通常是查看字段信息
  • 详细的显示字段的各项信息
  • 查看语法有三种(效果一样)
    • desc 表名;
    • describe 表名;
    • show columns from 表名;
1
2
desc t_1;
1

小结

1、数据表的查看是为了查看表中具体字段的信息

2、查看数据表的指令有多个,效果都一样

  • desc 表名;(常用)
  • describe 表名;
  • show columns from 表名;

3、查看表结构的原因通常是在开发过程中为了更清晰的了解数据的存储形式和要求

4) 更改数据表

目标:了解数据表的修改内容以及修改语法

更改数据表:修改表名字和表选项

  • 修改表名:rename table 表名 to 新表名;
  • 修改表选项:alter table 表名 表选项;

1、修改表名

1
2
rename table t_1 to t1;
1

注意:如果有时候想要跨库修改的话,需要使用数据库名.表名

2、修改表选项

1
2
alter table t1 charset utf8;
1

小结

1、更改数据表分为两个部分

  • 更改表名:rename table 原表名 to 新表名;
  • 更改表选项:alter table 表名 表选项

2、通常我们较少使用更改数据表,数据表应该在创建时就定义好

5) 更改字段

目标:了解字段更改的类型和基本语法的使用

更改字段:指针对表创建好后,里面字段的增删改

  • 字段操作包含字段名字、类型和属性的操作
  • 字段操作分为四类
    • 新增字段:add [column]
    • 更改字段名:change
    • 修改类型:modify
    • 删除字段:drop
  • 字段操作还有位置处理
  • 字段操作通常是在表已经存在数据后进行

① 新增字段

新增字段:在表创建好后往里面增加其他字段

  • 字段的新增必须同时存在字段类型
  • 新增语法:alter table 表名 add [column] 字段名 字段类型 [字段属性] [字段位置]

1、给已经存在的t_3表增加一个字段age

1
2
alter table t_3 add age int;
1

2、给已经存在的t_3表增加一个字段nickname

1
2
alter table t_3 add column nickname varchar(10);
1

小结

1、新增字段就是给已有表追加一个字段(较少)

2、字段新增必须指定字段类型

3、字段新增语法为:alter table 表名 add [column] 字段名 字段类型;

4、字段的追加默认是在所有字段之后

② 字段位置

字段位置:指字段放到某个指定字段之后

  • 字段位置分为两种
    • 第一个字段:first
    • 某个字段后:after 已经存在字段名
  • 字段位置适用于追加字段、修改字段、更改字段名
  • 字段位置语法:alter table 表名 字段操作 字段位置;

1、为t_3表增加一个id字段,放到最前面

1
2
alter table t_3 add id int first;
1

2、在t_3表name字段后增加一个身份证字段card

1
2
alter table t_3 add card varchar(18) after name;
1

小结

1、字段位置是配合字段操作的(新增、修改)

2、字段位置分两种

  • 最前面(第一个字段):first
  • 字段后面:after 已存在字段名

③ 更改字段名

更改字段名:指对已经存在的字段名进行修改

  • 字段名的修改也必须跟上字段类型
  • 字段名修改语法:alter table 表名 change 原字段名 新字段名 字段类型 [字段属性] [位置]

1、修改字段名card为sfz

1
alter table t_3 change card sfz varchar(18);

小结

1、字段名更改通常只是修改字段名字,但是也必须跟随类型

2、字段名修改语法:alter table 表名 change 原字段名 新字段名 字段类型

3、字段名修改change其实也可以修改字段类型、属性和位置,但是通常不使用(专人专事)

④ 修改字段

修改字段:指修改字段的相关信息

  • 修改字段类型、字段属性和位置
  • 修改字段语法:****alter table 表名 modify 字段名 字段类型 [字段属性] [位置];****

1、修改身份证的类型为char(18)并且位置放到id后面

1
alter table t_3 modify sfz char(18) after id;

小结

1、修改字段包含多个操作

  • 字段类型修改
  • 字段属性修改
  • 字段位置修改
    2、修改字段语法:alter table 表名 modify 字段名 字段类型 [字段属性] [位置];

6)删除字段

目标:了解字段删除语法

删除字段:即将某个不要的字段从表中剔除

  • 删除字段会将数据也删除(不可逆)
  • 删除字段语法:alter table 表名 drop 字段名;

1、删除年龄字段

1
2
alter table t_3 drop age;
1

小结

1、字段删除在删除字段名的同时会删除字段对应的数据,而且不可逆

2、字段删除语法:alter table 表名 drop 字段名

7)总结

1、数据表结构的操作是数据操作的基础

2、一般情况下新手都不会接触数据表的设计,但是作为一名新手一定要在使用数据表之前查看数据表的结构信息

3、不要轻易的修改或者删除数据表结构(数据会一并被处理掉

4、数据表结构的维护通常是一次性的,在业务开展前尽可能好的设计好数据表,而不要后期再进行其他维护

DML

目标:掌握mysql中数据的增删改查的基本操作

  • 新增数据
  • 查看数据
  • 更新数据
  • 删除数据

1)新增数据

目标:了解数据的新增指令和逻辑,实现数据的入库操作

新增数据:将数据插入到数据表永久存储

  • 新增数据是根据表的字段顺序和数据类型要求将数据存放到数据表中
  • 数据表中的数据以行(row) 为存储单位,实际存储属于 字段(field) 存储数据
  • 数据插入分两种方式:
    • 全字段插入:insert into 表名 values(字段列表顺序对应的所有值);
    • 部分字段插入:insert into 表名 (字段列表) values(字段列表对应的值顺序列表);
    • 插入多条数据: insert into 表名 values(字段列表顺序对应的所有值),(字段列表顺序对应的所有值);

1、给t_3表插入一条完整数据

1
2
insert into t_3 values(1,'440111200011111101','Jim','Green');
1

2、根据字段插入数据

1
2
insert into t_3 (id,sfz,name) values(2,'441000200011111211','Tom');

3.插入多条数据

1
insert into t_3  values(2,'441000200011111211','Tom'),(3,'441000200011111211','jack'),(4,'441000200011111211','jerry');

小结

1、数据插入是利用insert指令将数据永久存储到数据表中

2、数据存储以行为单位,字段为最小实际存储单位

3、数据插入分为两种方式插入数据

  • 全字段插入
    • insert into 表名 values(值列表)
    • 值列表必须与字段列表顺序一致
    • 值列表的每个数据类型必须与字段类型一致
  • 部分字段插入
    • insert into 表名 (字段列表) values(值列表)
    • 字段列表可以顺序随意
    • 值列表必须与指定的字段列表顺序一致
    • 值列表元素的类型必须与字段列表的类型一致

2、查看数据

目标:了解数据查看的指令,掌握数据查看实际操作

查看数据:将表中已经存在的数据按照指定的要求显示出来

  • 查到的数据显示出来是一张二维表
  • 数据显示包含字段名数据本身
  • 数据查看分两种方式
    • 查看全部字段:使用*代替所有字段
    • 查看部分字段:明确字段名,使用逗号分隔
  • 查看数据很多时候也是根据条件查询部分数据
  • 查看语法:select */字段列表 from 表名;

1、查看t_3表中所有数据

1
2
select * from t_3;
1

2、查看t_3表中的name和身份证信息

1
2
select name,sfz from t_3;
1

3、查看t_3表中id值为1的信息

1
2
select * from t_3 where id = 1;
1

小结

1、数据查看是数据库中最常用的操作(99%)

2、数据查看分两种情况

  • 查看全部:select 叫做通配符)
  • 查看部分:select 字段列表(建议)

3、实际使用时通常会带where条件进行数据筛选

3) 更新数据

目标:了解更新数据的语法,掌握数据更新操作

更新数据:即更新某个已有字段的值

  • 更新数据通常是根据条件更新某些数据,而不是全部记录都更新
  • 更新数据语法:update 表名 set 字段 = 新值[,字段 = 新值] [where条件筛选];

1、更新所有记录的身份证信息

1
2
update t_3 set sfz = '440100200010100001';
1

2、更新某个记录的多个字段数据

1
2
update t_3 set name = 'Lily',sfz = '440100200010100002' where id = 1;
1

小结

1、更新数据的针对记录的字段数据进行更新

2、更新通常是限定条件更新(一般不会更新全部)

4、删除数据

目标:了解删除语法,掌握删除操作实现

删除数据:将数据从已有数据表中清除(针对的是记录record)

  • 删除数据是一种不可逆操作
  • 数据删除通常都是有条件删除
  • 数据删除语法:delete from 表名 [where条件];

1、删除t_3表中id为2的数据

1
2
delete from t_3 where id = 2;
1

小结

1、数据删除是不可逆的操作

2、数据删除通常都匹配条件部分删除

5)总结

1、数据操作不论是初级开发者还是高级开发者都频繁接触的操作

2、数据操作中读操作,往往占据了整个操作的99%以上

3、基本的增删改查是作为进阶的必要知识,必须熟练掌握和频繁练习(尽可能使用原码,集成工具后期开发时使用)

DQL(数据库查询语言):

*DQL(data query language)数据查询语言,专门用来查询数据。

select查询

  • 一般使用的都是Select 所有的查询操作都是这个
  • 简单的查询和复杂的查询都能做
  • 数据库中最核心的语言,也是最重要的语句。
  • 使用频率最高的语句

查询所有

1
2
   select * from 表名;  -- 一般不推荐使用 sql优化不推荐使用
1

查询指定字段

1
2
select id , name from	表名;
1

查询起别名,然后通过别名得到数据

as 可以给字段起别名,也可以给表起别名

如 : from 表名 as ‘别名’

1
select 列名1 as '别名1' , 列名2 as '别名2' ... from 表名;

去重查询distinct

去除查到的重复的值

1
2
3
4
5
6
7
8
9
select distinct 列名 from 表名;  -- 查询表中重复的数据,使得重复的数据只显示一条。

select version() ; -- 查询系统版本
select 100*3 -1 as '结果' ; -- 用来计算
select @@auto_increment_increment ; -- 查询自增的步长(变量)

-- 例子 查询考试成绩 + 1 查看
select id , scode + 1 as '加分后' from 表名;
12345678

数据库中表达式:

文本值,列,NULL,函数,计算表达式,系统变量…

1
select 表达式 from 表名;

WHERE条件

一般作为检索数据中的符合条件的值

搜索的条件一般由一个或多个条件组成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
select id , score from 表名;
-- 查询成绩在95 - 100 直接的 id
select id from 表名 where score >=95 and score <= 100;

-- and &&
select id from 表名 where score >=95 && score <= 100;

-- 模糊查询(between)
select id from 表名 where score between 95 and 100;

-- 除了1000号同学以为的所有同学的成绩
select id ,score from 表名 where id != 1000;

-- != not
select id ,score from 表名 where not id = 1000;
123456789101112131415

了解逻辑运算符:

运算符 语法 描述
and && a and b a && b 逻辑与,两真为真
or \ \ a \ \ b a or b 逻辑或 ,一真为真,全假为假
not ! not a !a 逻辑非,真为假,假为真

模糊查询

运算符 语法 描述
is null a is null 如果操作符为null,结果为真
is not null a is not null 如果操作符不为空,结果为真
between a between b and c 如果a在b和c之间,则结果为真
like a like b sql匹配成功,如果a匹配b,则结果为真
in a in b a 在 b 中

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
-- 查询姓刘的同学的名字
select id , name from 表名 where name like '刘%'

-- 查询姓刘的同学,且只有一个字的
select id , name from 表名 where name like '刘_'

-- 查询姓刘的同学,且只有两个字的
select id , name from 表名 where name like '刘__'

-- 查询名字中有嘉的同学,%嘉%
select id , name from 表名 where name like '%嘉%'

-- =================== in(具体的一个或多个值) ================
-- 查询101,102,103 号学员
select id, name from 表名 where id in ( 101,102,103);

-- 用in查询名字等于张三,李四。。。的学生
select id , name from 表名 where name in ('张三', '李四' ,'王五'...);

-- 查询姓名为空的学习(当然我们设置默认值,必定不存在这个值)
select id , name from 表名 where name = '' or name is null;


-- 查询姓名不为空的同学
select id , name from 表名 where name != '' or name is not null;
12345678910111213141516171819202122232425

五种常用的聚合函数

什么是聚合函数

聚合函数作用于一组数据,并对一组数据返回一个值。
在这里插入图片描述

聚合函数不能嵌套调用。比如不能出现类似“AVG(SUM(字段名称))”形式的调用。

**null值不参与所有聚合函数运算*

AVG和SUM函数

AVG函数:求平均值
SUM函数:求总和

AVG / SUM :只适用于数值类型的字段(或变量)

例如:

1
2
3
SELECT AVG(salary),SUM(salary),AVG(salary) * 107
FROM employees [where];
12

结果:

在这里插入图片描述

MIN和MAX函数

MIN函数:求最小值
MAX函数:求最大值

可以对任意数据类型的数据使用 MIN 和 MAX 函数。

例如:

1
2
3
4
5
6
SELECT MAX(salary),MIN(salary)
FROM employees;

SELECT MAX(last_name),MIN(last_name),MAX(hire_date),MIN(hire_date)
FROM employees;
12345

结果:
在这里插入图片描述
在这里插入图片描述

既然在ORDER BY中字符串可以排序,那么显然字符串是有大有小的,在MySQL中会根据字符串的ASCII码值进行大小的比较。(先比较第一个字符,如果相同则继续往后比较,如此继续下去)

COUNT函数

COUNT函数:计算指定字段在查询结构中出现的个数

注意:计算指定字段出现的个数时,是不计算NULL值的。

例如:

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

结果:
在这里插入图片描述
注:employees表中一共有107条员工的信息,有些员工的相关字段会有NULL值

公式:AVG = SUM / COUNT

1
2
3
4
SELECT AVG(commission_pct),SUM(commission_pct)/COUNT(commission_pct),
SUM(commission_pct) / 107
FROM employees;
123

结果:
在这里插入图片描述
由此我们可知:SUM函数、AVG函数是不会把NULL值计算进去的

有关COUNT的效率问题

如何需要统计表中的记录数,使用COUNT()、COUNT(1)、COUNT(具体字段) 哪个效率更高呢?
如果使用的是MyISAM 存储引擎,则三者效率相同,都是O(1)
如果使用的是InnoDB 存储引擎,则三者效率:COUNT(
) = COUNT(1)> COUNT(字段)

GROUP BY

基本实现

在这里插入图片描述
作用:将表中的数据分成若干组
语法:

1
2
3
4
5
6
SELECT column, group_function(column)
FROM table
[WHERE condition]
[GROUP BY group_by_expression]
[ORDER BY column];
12345

明确:GROUP BY 声明在FROM后面、WHERE后面,ORDER BY 前面、LIMIT前面

使用多个列分组

在这里插入图片描述
例如:

1
2
3
4
SELECT department_id dept_id, job_id, SUM(salary)
FROM employees
GROUP BY department_id, job_id ;
123

结果:
在这里插入图片描述

注意:SELECT中出现的非组函数的字段必须声明在GROUP BY 中。反之,GROUP BY中声明的字段可以不出现在SELECT中。

GROUP BY中使用WITH ROLLUP

使用 WITH ROLLUP 关键字之后,在所有查询出的分组记录之后增加一条记录,该记录计算查询出的所有记录的总和,即统计记录数量。
例如:

1
2
3
4
SELECT department_id,AVG(salary)
FROM employees
GROUP BY department_id WITH ROLLUP;
123

结果:
在这里插入图片描述

注意:当使用ROLLUP时,不能同时使用ORDER BY子句进行结果排序,即ROLLUP和ORDER BY是互相排斥的。

HAVING

HAVING的使用

HAVING的作用:过滤
在这里插入图片描述
使用要求:

  • 如果过滤条件中使用了聚合函数,则必须使用HAVING来替换WHERE。否则,报错。
  • HAVING 必须声明在 GROUP BY 的后面。
  • 开发中,我们使用HAVING的前提是SQL中使用了GROUP BY。

例如:

1
2
3
4
5
SELECT department_id,MAX(salary)
FROM employees
GROUP BY department_id
HAVING MAX(salary) > 10000;
1234

WHERE和HAVING的对比

区别1:WHERE 可以直接使用表中的字段作为筛选条件,但不能使用分组中的计算函数作为筛选条件;HAVING 必须要与 GROUP BY 配合使用,可以把分组计算的函数和分组字段作为筛选条件。

这决定了,在需要对数据进行分组统计的时候,HAVING 可以完成 WHERE 不能完成的任务。这是因为,在查询语法结构中,WHERE 在 GROUP BY 之前,所以无法对分组结果进行筛选。HAVING 在 GROUP BY 之后,可以使用分组字段和分组中的计算函数,对分组的结果集进行筛选,这个功能是 WHERE 无法完成的。另外,WHERE排除的记录不再包括在分组中。

区别2:如果需要通过连接从关联表中获取需要的数据,WHERE 是先筛选后连接,而 HAVING 是先连接后筛选。

这一点,就决定了在关联查询中,WHERE 比 HAVING 更高效。因为 WHERE 可以先筛选,用一个筛选后的较小数据集和关联表进行连接,这样占用的资源比较少,执行效率也比较高。HAVING 则需要先把结果集准备好,也就是用未被筛选的数据集进行关联,然后对这个大的数据集进行筛选,这样占用的资源就比较多,执行效率也较低。

在这里插入图片描述
开发中的选择:
WHERE 和 HAVING 也不是互相排斥的,我们可以在一个查询里面同时使用 WHERE 和 HAVING。包含分组统计函数的条件用 HAVING,普通条件用 WHERE。这样,我们就既利用了 WHERE 条件的高效快速,又发挥了 HAVING 可以使用包含分组统计函数的查询条件的优点。当数据量特别大的时候,运行效率会有很大的差别。

结论:当过滤条件中有聚合函数时,则此过滤条件必须声明在HAVING中。
当过滤条件中没有聚合函数时,则此过滤条件声明在WHERE中或HAVING中都可以。但是,最好声明在WHERE中,如此效率更高。

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

1
2
3
4
5
SELECT department_id,MAX(salary)
FROM employees
WHERE department_id IN (10,20,30,40)
GROUP BY department_id
HAVING MAX(salary) > 10000;

执行顺序

一、SQL

我们要编写一段查询数据的SQL语句,经常会用到如下字段。(编写顺序)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SELECT
字段
FROM
表名
join on
表名
WHERE
条件列表
GROUP BY
分组字段列表
HAVING
分组后条件列表
ORDER BY
排序字段列表
LIMIT
分页参数

你有没有思考过,上诉几个关键字的执行顺序,又是怎样的呢?

二、执行顺序

正确的执行顺序如下所示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

FROM
表名
WHERE
条件列表
GROUP BY
分组字段列表
HAVING
分组后条件列表
SELECT
字段
ORDER BY
排序字段列表
LIMIT
分页参数



连表查询(JOIN on)

在这里插入图片描述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 -- 连接两个表
select s.id , s.name ,cid ,cname
from stu s inner join class as r
where s.id = r.id;

-- 右连接 right join
select s.id , name , cid , cname from stu s
right join class c
on s.id = c.id

-- 左连接 left join
select s.id , name , cid , cname from stu s
left join class c
on s.id = c.id

-- join on 连接查询
-- where 等值查询

-- 查询name为空的同学
select s.id , sname , cid, cname
from stu s left join
class c on s.id = c.id
where name is null;

-- 查询时间为空的学生信息
select s.id ,date, name ,cid , cname from stu s
left join class c
on s.id = c.id
where date is null;
1234567891011121314151617181920212223242526272829
操作 描述
inner join 如果表中至少有一个匹配值,那么就返回行
left join 会从左表中返回所有的值,即使右表中没有
right join 会从右表中返回所有的值,即使左表中没有
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  -- 测试数据库
create table `table_a` (
`aid` int(11) NOT NULL AUTO_INCREMENT,
`a_name` varchar(255) NOT NULL,
`age` smallint NOT NULL,
PRIMARY KEY(`aid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '测试表A';

create table `table_b` (
`bid` int(11) NOT NULL AUTO_INCREMENT,
`b_name` varchar(255) NOT NULL,
`age` smallint NOT NULL,
PRIMARY KEY(`bid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '测试表B';

# 插入测试数据
INSERT INTO `table_a`(aid, a_name, age) VALUES(1, 'test1', 1),(2, 'test2', 2),(3, 'test3', 3);
INSERT INTO `table_b`(bid, b_name, age) VALUES(1, 'test2', 2),(2, 'test3', 3),(4, 'test4', 4);
123456789101112131415161718

这三条SQL都是等价的

1
2
3
4
SELECT * FROM table_a JOIN table_b;
SELECT * FROM table_a INNER JOIN table_b;
SELECT * FROM table_a CROSS JOIN table_b;
123

结果如下:

inner join连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
   -- 相当于笛卡尔积
mysql> SELECT * FROM table_a INNER JOIN table_b;
+-----+--------+-----+-----+--------+-----+
| aid | a_name | age | bid | b_name | age |
+-----+--------+-----+-----+--------+-----+
| 1 | test1 | 1 | 1 | test2 | 2 |
| 2 | test2 | 2 | 1 | test2 | 2 |
| 3 | test3 | 3 | 1 | test2 | 2 |
| 1 | test1 | 1 | 2 | test3 | 3 |
| 2 | test2 | 2 | 2 | test3 | 3 |
| 3 | test3 | 3 | 2 | test3 | 3 |
| 1 | test1 | 1 | 4 | test4 | 4 |
| 2 | test2 | 2 | 4 | test4 | 4 |
| 3 | test3 | 3 | 4 | test4 | 4 |
+-----+--------+-----+-----+--------+-----+
123456789101112131415

有ON和WHERE筛选条件,此时得到的结果是两张表的交集(中间的图),对于内连接,ON和WHERE是等价的,但是对于外连接则不是,在下面会讲到。

1
2
3
4
5
6
7
8
9
10
11
12
13
 # 这两条SQL是等价的,不过建议使用ON关键字,约定俗成
SELECT * FROM table_a a INNER JOIN table_b b ON a.a_name=b.b_name;
SELECT * FROM table_a a INNER JOIN table_b b WHERE a.a_name=b.b_name;

# 结果如下:
mysql> SELECT * FROM table_a a INNER JOIN table_b b ON a.a_name=b.b_name;
+-----+--------+-----+-----+--------+-----+
| aid | a_name | age | bid | b_name | age |
+-----+--------+-----+-----+--------+-----+
| 2 | test2 | 2 | 1 | test2 | 2 |
| 3 | test3 | 3 | 2 | test3 | 3 |
+-----+--------+-----+-----+--------+-----+
123456789101112

右连接(right join)

右连接的关键字是 RIGHT JOIN,从上图可以得到(右边的图),右连接其实就是两个表的交集+右表剩下的数据 ,当然这是在没其他过滤条件的情况下。

1
2
3
4
5
6
7
8
9
mysql> SELECT * FROM `table_a` a RIGHT JOIN `table_b` b ON a.a_name=b.b_name;
+------+--------+------+-----+--------+-----+
| aid | a_name | age | bid | b_name | age |
+------+--------+------+-----+--------+-----+
| 2 | test2 | 2 | 1 | test2 | 2 |
| 3 | test3 | 3 | 2 | test3 | 3 |
| NULL | NULL | NULL | 4 | test4 | 4 |
+------+--------+------+-----+--------+-----+
12345678

左连接(left join)

左连接的关键字是 LEFT JOIN,从上图可以得到(左边的图),左连接其实就是两个表的交集+左表剩下的数据 ,当然这是在没其他过滤条件的情况下。

1
2
3
4
5
6
7
8
9
10
11
  # 没找到的被置为NULL
mysql> SELECT * FROM `table_a` a LEFT JOIN `table_b` b ON a.a_name=b.b_name;
+-----+--------+-----+------+--------+------+
| aid | a_name | age | bid | b_name | age |
+-----+--------+-----+------+--------+------+
| 2 | test2 | 2 | 1 | test2 | 2 |
| 3 | test3 | 3 | 2 | test3 | 3 |
| 1 | test1 | 1 | NULL | NULL | NULL |
+-----+--------+-----+------+--------+------+

12345678910

WHERE子句中的过滤条件就是我们常见的那种,不管是内连接还是外连接,只要不符合WHERE子句的过滤条件,都会被过滤掉。

而ON子句中的过滤条件对于内连接和外连接是不同的,对于内连接,ON和WHERE的作用是一致的,因为匹配不到的都会过滤,所以你可以看到内连接并不强制需要 ON 关键字;但是对于外连接,ON决定匹配不到的是否要过滤,所以你可以看到外连接是强制需要 ON 关键字的。

我要查询哪些数据 select …

1
从那几个表中查 from 表 XXX join 连接的表 on 交叉条件

假设存在一种多张表查询,慢慢来,先查询两张表 然后在慢慢添加。

自连接(了解)

自己的表和自己的表连接: 核心是一张表拆为两张表使用

将数据同一张表中数据进行查分,然后起别名。将一张表变成两张表处理。

分为父和子,也就是拆分的表可以成为父和子关系或者说包含和被包含关系。

分页和排序—-limit 和 order by

limit(分页)

limit 的第一个参数代表从 第几个位置开始索引。—— 索引位置从0开始计算。
limit 的第二个参数代表 要查的行数数量是多少

假设limit第一个参数是 index ,第二个参数是 pageSizecurrentPage 代表当前页数
做分页查询有个公式 : index = (currentPage-1) * pageSize

假如我们要查询第一页的数据 ?

因为是第一页,所以当前页码是1,也就是 currentPage = 1,一页大小为6条数据,也就是 pageSize = 6,代入公式 index = (currentPage-1) * pageSize

index = (1-1)* 6 = 0,求出 index = 0

最后进入 sql 语句 select * from book limit 0,6; 完成第一页完成分页操作。

第一和第二个参数可以分别理解成从第几个数据开始查找,向后面查找多少个数据

1
2
3
4
limit  当前页,页面大小

limit 0 , 5 --前边数字表示第几页,后边数据每页显示的数据条数
123

order by (排序)

1
2
3
4
5
- 升序	asc
order by id asc 根据id升序排序
- 降序 desc
order by id desc根据id降序排序
1234

案例

(1)查询年龄为20,21,22,23岁的员工信息。

1
2
select * from emp where gender = '女' and age in(20,21,22,23);
1

(2)查询性别为 男 ,并且年龄在 20-40 岁(含)以内的姓名为三个字的员工。

1
2
3
select * from emp where gender = '男' and ( age between 20 and 40 ) and name like
'___';
12

(3)统计员工表中, 年龄小于60岁的 , 男性员工和女性员工的人数。

1
2
select gender, count(*) from emp where age < 60 group by gender;
1

(4)查询所有年龄小于等于35岁员工的姓名和年龄,并对查询结果按年龄升序排序,如果年龄相同按
入职时间降序排序。

1
2
select name , age from emp where age <= 35 order by age asc , entrydate desc;
1

(5)查询性别为男,且年龄在20-40 岁(含)以内的前5个员工信息,对查询的结果按年龄升序排序,
年龄相同按入职时间升序排序。

1
2
select * from emp where gender = '男' and age between 20 and 40 order by age asc ,
entrydate asc limit 5 ;

DCL语句目录

DCL英文全称是Data Control Language(数据控制语言),用来管理数据库用户、控制数据库的访
问权限。

一、管理用户

🍀(1)查询用户

1
select * from mysql.user;

查询的结果如下:

其中 Host代表当前用户访问的主机, 如果为localhost, 仅代表只能够在当前本机访问,是不可以
远程访问的。 User代表的是访问该数据库的用户名。在MySQL中需要通过Host和User来唯一标识一
个用户。

🍀(2)创建用户

1
CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';

🍀(3)修改用户密码

1
ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码' ;

🍀(4) 删除用户

1
DROP USER '用户名'@'主机名' ;

注意事项:

  • 在MySQL中需要通过用户名@主机名的方式,来唯一标识一个用户。
  • 主机名可以使用 % 通配。
  • 这类SQL开发人员操作的比较少,主要是DBA( Database Administrator 数据库管理员)使用。

🍀(5)案例

A. 创建用户itcast, 只能够在当前主机localhost访问, 密码123456;

1
create user 'itcast'@'localhost' identified by '123456';

B. 创建用户heima, 可以在任意主机访问该数据库, 密码123456;

1
create user 'heima'@'%' identified by '123456';

C. 修改用户heima的访问密码为1234;

1
alter user 'heima'@'%' identified with mysql_native_password by '1234';

D. 删除 itcast@localhost 用户

1
drop user 'itcast'@'localhost';

二、权限控制

MySQL中定义了很多种权限,但是常用的就以下几种:

权限 说明
ALL, ALL PRIVILEGES 所有权限
SELECT 查询数据
INSERT 插入数据
UPDATE 修改数据
DELETE 删除数据
ALTER 修改表
DROP 删除数据库/表/视图
CREATE 创建数据库/表

上述只是简单罗列了常见的几种权限描述,其他权限描述及含义,可以直接参考官方文档。

🍀(1)查询权限

1
2
SHOW GRANTS FOR '用户名'@'主机名' ;
1

🍀(2)授予权限

1
2
GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机名';
1

🍀(3)撤销权限

1
2
REVOKE 权限列表 ON 数据库名.表名 FROM '用户名'@'主机名';
1

注意事项:

  • 多个权限之间,使用逗号分隔
  • 授权时, 数据库名和表名可以使用 * 进行通配,代表所有。

🍀(4)案例

A. 查询 ‘heima’@‘%’ 用户的权限

1
show grants for 'heima'@'%';

B. 授予 ‘heima’@‘%’ 用户itcast数据库所有表的所有操作权限

1
grant all on itcast.* to 'heima'@'%';

C. 撤销 ‘heima’@‘%’ 用户的itcast数据库的所有权限

1
revoke all on itcast.* from 'heima'@'%';

函数

函数 是指一段可以直接被另一段程序调用的程序或代码。 也就意味着,这一段程序或代码在MySQL中
已经给我们提供了,我们要做的就是在合适的业务场景调用对应的函数完成对应的业务需求即可。 那
么,函数到底在哪儿使用呢?

我们先来看两个场景:

在这里插入图片描述

  • (1)在企业的OA或其他的人力系统中,经常会提供的有这样一个功能,每一个员工登录上来之后都能 够看到当前员工入职的天数。而在数据库中,存储的都是入职日期,如 2000-11-12,那如果快速计 算出天数呢?
  • (2)在做报表这类的业务需求中,我们要展示出学员的分数等级分布。而在数据库中,存储的是学生的分数值,如98/75,如何快速判定分数的等级呢?

其实,上述的这一类的需求呢,我们通过MySQL中的函数都可以很方便的实现 。

MySQL中的函数主要分为以下四类: 字符串函数数值函数日期函数流程函数

一、字符串函数

MySQL中内置了很多字符串函数,常用的几个如下(有时候前面什么都没有的时候要加select):

函数 功能
CONCAT(S1,S2,…Sn) 字符串拼接,将S1,S2,… Sn拼接成一个字符串
LOWER(str) 将字符串str全部转为小写
UPPER(str) 将字符串str全部转为大写
LPAD(str,n,pad) 左填充,用字符串pad对str的左边进行填充,达到n个字符串长度
RPAD(str,n,pad) 右填充,用字符串pad对str的右边进行填充,达到n个字符串长度
TRIM(str) 去掉字符串头部和尾部的空格
SUBSTRING(str,start,len) 返回从字符串str从start位置起的len个长度的字符串,下标从1开始

演示如下:

A. concat : 字符串拼接

1
2
select concat('Hello' , ' MySQL');
1

B. lower : 全部转小写

1
2
select lower('Hello');
1

C. upper : 全部转大写

1
2
select upper('Hello');
1

D. lpad : 左填充

1
2
select lpad('01', 5, '-');
1

E. rpad : 右填充

1
2
select rpad('01', 5, '-');
1

F. trim : 去除空格

1
2
select trim(' Hello MySQL ');
1

G. substring : 截取子字符串

1
2
select substring('Hello MySQL',1,5);
1

案例:

在这里插入图片描述

由于业务需求变更,企业员工的工号,统一为5位数,目前不足5位数的全部在前面补0。比如: 1号员
工的工号应该为00001。

1
2
update emp set workno = lpad(workno, 5, '0');
1

处理完毕后, 具体的数据为:

在这里插入图片描述

二、数值函数

常见的数值函数如下:

函数 功能
CEIL(x) 向上取整
FLOOR(x) 向下取整
MOD(x,y) 返回x/y的模
RAND() 返回0~1内的随机数
ROUND(x,y) 求参数x的四舍五入的值,保留y位小数

演示如下:

A. ceil:向上取整

1
2
select ceil(1.1);

B. floor:向下取整

1
2
select floor(1.9);

C. mod:取模

1
2
select mod(7,4);

D. rand:获取随机数

1
2
select rand();

E. round:四舍五入

1
2
select round(2.344,2);

案例:

通过数据库的函数,生成一个六位数的随机验证码。

思路: 获取随机数可以通过rand()函数,但是获取出来的随机数是在0-1之间的,所以可以在其基础
上乘以1000000,然后舍弃小数部分,如果长度不足6位,补0

1
2
select lpad(round(rand()*1000000 , 0), 6, '0');

三、日期函数

常见的日期函数如下:

函数 功能
CURDATE() 返回当前日期
CURTIME() 返回当前时间
NOW() 返回当前日期和时间
YEAR(date) 获取指定date的年份
MONTH(date) 获取指定date的月份
DAY(date) 获取指定date的日期
DATE_ADD(date, INTERVAL exprtype) 返回一个日期/时间值加上一个时间间隔expr后的时间值
DATEDIFF(date1,date2) 返回起始时间date1 和 结束时间date2之间的天数

演示如下:

A. curdate:当前日期

1
2
select curdate();

B. curtime:当前时间

1
2
select curtime();

C. now:当前日期和时间

1
2
select now();

D. YEAR , MONTH , DAY:当前年、月、日

1
2
3
4
select YEAR(now());
select MONTH(now());
select DAY(now());

E. date_add:增加指定的时间间隔

1
2
select date_add(now(), INTERVAL 70 YEAR );

F. datediff:获取两个日期相差的天数(前减后)

1
2
select datediff('2021-10-01', '2021-12-01');

案例:

查询所有员工的入职天数,并根据入职天数倒序排序。

思路: 入职天数,就是通过当前日期 - 入职日期,所以需要使用datediff函数来完成。

1
2
select name, datediff(curdate(), entrydate) as 'entrydays' from emp order by entrydays desc;

四、流程函数

流程函数也是很常用的一类函数,可以在SQL语句中实现条件筛选,从而提高语句的效率。

函数 功能
IF(value , t , f) 如果value为true,则返回t,否则返回f
IFNULL(value1 , value2) 如果value1不为空,返回value1,否则返回value2
CASE WHEN [ val1 ] THEN [res1] …ELSE [ default ] END 如果val1为true,返回res1,… 否则返回default默认值
CASE [ expr ] WHEN [ val1 ] THEN [res1] … ELSE [ default ] END 如果expr的值等于val1,返回res1,… 否则返回default默认值

演示如下:

A. if

1
2
select if(false, 'Ok', 'Error');
1

B. ifnull

1
2
3
4
select ifnull('Ok','Default');
select ifnull('','Default');
select ifnull(null,'Default');
123

C. case when then else end
需求: 查询emp表的员工姓名和工作地址 (北京/上海 ——> 一线城市 , 其他 ——> 二线城市)

1
2
3
4
5
6
select
name,
( case workaddress when '北京' then '一线城市' when '上海' then '一线城市' else '二线城市' end )
as '工作地址'
from emp;
12345

案例:

1
2
3
4
5
6
7
8
9
10
create table score(
id int comment 'ID',
name varchar(20) comment '姓名',
math int comment '数学',
english int comment '英语',
chinese int comment '语文'
) comment '学员成绩表';
insert into score(id, name, math, english, chinese) VALUES (1, 'Tom', 67, 88, 95
), (2, 'Rose' , 23, 66, 90),(3, 'Jack', 56, 98, 76);
123456789

具体的SQL语句如下:

1
2
3
4
5
6
7
8
9
10
11
select
id,
name,
(case when math >= 85 then '优秀' when math >=60 then '及格' else '不及格' end )
'数学',
(case when english >= 85 then '优秀' when english >=60 then '及格' else '不及格' end )
'英语',
(case when chinese >= 85 then '优秀' when chinese >=60 then '及格' else '不及格' end )
'语文'
from score;
12345678910

MySQL的常见函数我们学习完了,那接下来,我们就来分析一下,在前面讲到的两个函数的案例场景,
思考一下需要用到什么样的函数来实现?

  • (1)数据库中,存储的是入职日期,如 2000-01-01,如何快速计算出入职天数呢? ————> 答案: datediff
  • (2)数据库中,存储的是学生的分数值,如98、75,如何快速判定分数的等级呢? ————> 答案: case … when …

约束

一、约束概述

概念: 约束是作用于表中字段上的规则,用于限制存储在表中的数据。

目的: 保证数据库中数据的正确、有效性和完整性。

分类:

约束 描述 关键字
非空约束 限制该字段的数据不能为null NOT NULL
唯一约束 保证该字段的所有数据都是唯一、不重复的 UNIQUE
主键约束 主键是一行数据的唯一标识,要求非空且唯一 PRIMARY KEY
默认约束 保存数据时,如果未指定该字段的值,则采用默认值 DEFAULT
检查约束(8.0.16版本之后) 保证字段值满足某一个条件 CHECK
外键约束 用来让两张表的数据之间建立连接,保证数据的一致性和完整性 FOREIGNKEY

注意: 约束是作用于表中字段上的,可以在创建表/修改表的时候添加约束。

二、约束演示

上面我们介绍了数据库中常见的约束,以及约束涉及到的关键字,那这些约束我们到底如何在创建表、
修改表的时候来指定呢,接下来我们就通过一个案例,来演示一下。

案例需求: 根据需求,完成表结构的创建。需求如下:

字段名 字段名 字段含义 字段类型 约束条件 约束关键字
id ID唯一标识 int 主键,并且自动增长 PRIMARY KEY,AUTO_INCREMENT
name 姓名 varchar(10) 不为空,并且唯一 NOT NULL , UNIQUE
age 年龄 int 大于0,并且小于等于120 CHECK
status 状态 char(1) 如果没有指定该值,默认为1 DEFAULT
gender 性别 char(1)

**注意,当指定为unique后若添加两个一样的值,主键为自动加一**

对应的建表语句为:

1
2
3
4
5
6
7
8
CREATE TABLE tb_user(
id int AUTO_INCREMENT PRIMARY KEY COMMENT 'ID唯一标识',
name varchar(10) NOT NULL UNIQUE COMMENT '姓名' ,
age int check (age > 0 && age <= 120) COMMENT '年龄' ,
status char(1) default '1' COMMENT '状态',
gender char(1) COMMENT '性别'
);
1234567

在为字段添加约束时,我们只需要在字段之后加上约束的关键字即可,需要关注其语法。我们执行上面
的SQL把表结构创建完成,然后接下来,就可以通过一组数据进行测试,从而验证一下,约束是否可以
生效。

1
2
3
4
5
6
7
8
9
insert into tb_user(name,age,status,gender) values ('Tom1',19,'1','男'),('Tom2',25,'0','男');
insert into tb_user(name,age,status,gender) values ('Tom3',19,'1','男');
insert into tb_user(name,age,status,gender) values (null,19,'1','男');
insert into tb_user(name,age,status,gender) values ('Tom3',19,'1','男');
insert into tb_user(name,age,status,gender) values ('Tom4',80,'1','男');
insert into tb_user(name,age,status,gender) values ('Tom5',-1,'1','男');
insert into tb_user(name,age,status,gender) values ('Tom5',121,'1','男');
insert into tb_user(name,age,gender) values ('Tom5',120,'男');
12345678

上面,我们是通过编写SQL语句的形式来完成约束的指定,那加入我们是通过图形化界面来创建表结构
时,又该如何来指定约束呢? 只需要在创建表的时候,根据我们的需要选择对应的约束即可。

三、外键约束

1️⃣外键约束介绍

外键:用来让两张表的数据之间建立连接,从而保证数据的一致性和完整性。

我们来看一个例子:

在这里插入图片描述

左侧的emp表是员工表,里面存储员工的基本信息,包含员工的ID、姓名、年龄、职位、薪资、入职日
期、上级主管ID、部门ID,在员工的信息中存储的是部门的ID dept_id,而这个部门的ID是关联的
部门表dept的主键id,那emp表的dept_id就是外键,关联的是另一张表的主键。

注意: 目前上述两张表,只是在逻辑上存在这样一层关系;在数据库层面,并未建立外键关联,
所以是无法保证数据的一致性和完整性的。

没有数据库外键关联的情况下,能够保证一致性和完整性呢,我们来测试一下。

准备数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
create table dept(
id int auto_increment comment 'ID' primary key,
name varchar(50) not null comment '部门名称'
)comment '部门表';
INSERT INTO dept (id, name) VALUES (1, '研发部'), (2, '市场部'),(3, '财务部'), (4,
'销售部'), (5, '总经办');
create table emp(
id int auto_increment comment 'ID' primary key,
name varchar(50) not null comment '姓名',
age int comment '年龄',
job varchar(20) comment '职位',
salary int comment '薪资',
entrydate date comment '入职时间',
managerid int comment '直属领导ID',
dept_id int comment '部门ID'
)comment '员工表';
INSERT INTO emp (id, name, age, job,salary, entrydate, managerid, dept_id)
VALUES
(1, '金庸', 66, '总裁',20000, '2000-01-01', null,5),(2, '张无忌', 20,
'项目经理',12500, '2005-12-05', 1,1),
(3, '杨逍', 33, '开发', 8400,'2000-11-03', 2,1),(4, '韦一笑', 48, '开
发',11000, '2002-02-05', 2,1),
(5, '常遇春', 43, '开发',10500, '2004-09-07', 3,1),(6, '小昭', 19, '程
序员鼓励师',6600, '2004-10-12', 2,1);
123456789101112131415161718192021222324

在这里插入图片描述

接下来,我们可以做一个测试,删除id为1的部门信息。

在这里插入图片描述

结果,我们看到删除成功,而删除成功之后,部门表不存在id为1的部门,而在emp表中还有很多的员
工,关联的为id为1的部门,此时就出现了数据的不完整性。 而要想解决这个问题就得通过数据库的
外键约束。

2️⃣语法

🍀(1)添加外键

1
2
3
4
5
6
7
8
9
CREATE TABLE 表名(
字段名 数据类型,
...
[CONSTRAINT] [外键名称] FOREIGN KEY (外键字段名) REFERENCES 主表 (主表列名)
);
12345
ALTER TABLE 子表名 ADD CONSTRAINT 自定义的外键名称 FOREIGN KEY (子表内的外键字段名)
REFERENCES 父表 (父表列名) ;
12

案例:

为emp表的dept_id字段添加外键约束,关联dept表的主键id。

1
2
3
alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references
dept(id);
12

在这里插入图片描述

添加了外键约束之后,我们再到dept表(父表)删除id为1的记录,然后看一下会发生什么现象。 此时
将会报错,不能删除或更新父表记录,因为存在外键约束。

在这里插入图片描述

🍀(2)删除外键

1
2
ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;
1

案例:

删除emp表的外键fk_emp_dept_id。

1
2
alter table emp drop foreign key fk_emp_dept_id;
1

🍀(3)删除/更新行为

添加了外键之后,再删除父表数据时产生的约束行为,我们就称为删除/更新行为。具体的删除/更新行
为有以下几种:

行为 说明
NO ACTION 当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新。 (与 RESTRICT 一致) 默认行为
RESTRICT 当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新。 (与 NO ACTION 一致) 默认行为
CASCADE 当在父表中删除/更新对应记录时,首先检查该记录是否有对应外键,如果有,则也删除/更新外键在子表中的记录。
SET NULL 当在父表中删除对应记录时,首先检查该记录是否有对应外键,如果有则设置子表中该外键值为null(这就要求该外键允许取null)。
SET DEFAULT 父表有变更时,子表将外键列设置成一个默认的值 (Innodb不支持)

具体语法为:

1
2
3
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段) REFERENCES
主表名 (主表字段名) ON UPDATE CASCADE ON DELETE CASCADE;
12

演示如下:

由于NO ACTION 是默认行为,我们前面语法演示的时候,已经测试过了,就不再演示了,这里我们再
演示其他的两种行为:CASCADE、SET NULL。

(1)CASCADE

1
2
3
alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references
dept(id) on update cascade on delete cascade ;
12

A. 修改父表id为1的记录,将id修改为6

在这里插入图片描述
我们发现,原来在子表中dept_id值为1的记录,现在也变为6了,这就是cascade级联的效果。

注意: 在一般的业务系统中,不会修改一张表的主键值。

B. 删除父表id为6的记录

在这里插入图片描述

我们发现,父表的数据删除成功了,但是子表中关联的记录也被级联删除了。

(2)SET NULL

在进行测试之前,我们先需要删除上面建立的外键 fk_emp_dept_id。然后再通过数据脚本,将
emp、dept表的数据恢复了。

1
2
3
alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references
dept(id) on update set null on delete set null ;
12

接下来,我们删除id为1的数据,看看会发生什么样的现象。

在这里插入图片描述

我们发现父表的记录是可以正常的删除的,父表的数据删除之后,再打开子表 emp,我们发现子表emp
的dept_id字段,原来dept_id为1的数据,现在都被置为NULL了。

在这里插入图片描述

这就是SET NULL这种删除/更新行为的效果。

多表查询

之前在介绍SQL语句的时候,介绍到了DQL语句,也就是数据查询语句,但是之前介绍的查询都是单
表查询,而本问将要介绍的则是多表查询操作,主要从以下几个方面进行介绍。

一、多表关系

项目开发中,在进行数据库表结构设计时,会根据业务需求及业务模块之间的关系,分析并设计表结
构,由于业务之间相互关联,所以各个表结构之间也存在着各种联系,基本上分为三种:

  • 一对多(多对一)
  • 多对多
  • 一对一

1️⃣一对多

  • 案例: 部门 与 员工的关系
  • 关系: 一个部门对应多个员工,一个员工对应一个部门
  • 实现: 在多的一方建立外键,指向一的一方的主键

在这里插入图片描述

2️⃣多对多

  • 案例: 学生 与 课程的关系
  • 关系: 一个学生可以选修多门课程,一门课程也可以供多个学生选择
  • 实现: 建立第三张中间表,中间表至少包含两个外键,分别关联两方主键

在这里插入图片描述

对应的SQL脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
create table student(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '姓名',
no varchar(10) comment '学号'
) comment '学生表';
insert into student values (null, '黛绮丝', '2000100101'),(null, '谢逊',
'2000100102'),(null, '殷天正', '2000100103'),(null, '韦一笑', '2000100104');

create table course(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '课程名称'
) comment '课程表';
insert into course values (null, 'Java'), (null, 'PHP'), (null , 'MySQL') ,
(null, 'Hadoop');

create table student_course(
id int auto_increment comment '主键' primary key,
studentid int not null comment '学生ID',
courseid int not null comment '课程ID',
constraint fk_courseid foreign key (courseid) references course (id),
constraint fk_studentid foreign key (studentid) references student (id)
)comment '学生课程中间表';
insert into student_course values (null,1,1),(null,1,2),(null,1,3),(null,2,2),
(null,2,3),(null,3,4);
123456789101112131415161718192021222324

3️⃣一对一

  • 案例: 用户 与 用户详情的关系
  • 关系: 一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他详情字段放在另 一张表中,以提升操作效率
  • 实现: 在任意一方加入外键,关联另外一方的主键,并且设置外键为唯一的(UNIQUE)

在这里插入图片描述

对应的SQL脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
create table tb_user(
id int auto_increment primary key comment '主键ID',
name varchar(10) comment '姓名',
age int comment '年龄',
gender char(1) comment '1: 男 , 2: 女',
phone char(11) comment '手机号'
) comment '用户基本信息表';

create table tb_user_edu(
id int auto_increment primary key comment '主键ID',
degree varchar(20) comment '学历',
major varchar(50) comment '专业',
primaryschool varchar(50) comment '小学',
middleschool varchar(50) comment '中学',
university varchar(50) comment '大学',
userid int unique comment '用户ID',
constraint fk_userid foreign key (userid) references tb_user(id)
) comment '用户教育信息表';

insert into tb_user(id, name, age, gender, phone) values
(null,'黄渤',45,'1','18800001111'),
(null,'冰冰',35,'2','18800002222'),
(null,'码云',55,'1','18800008888'),
(null,'李彦宏',50,'1','18800009999');

insert into tb_user_edu(id, degree, major, primaryschool, middleschool, university, userid) values
(null,'本科','舞蹈','静安区第一小学','静安区第一中学','北京舞蹈学院',1),
(null,'硕士','表演','朝阳区第一小学','朝阳区第一中学','北京电影学院',2),
(null,'本科','英语','杭州市第一小学','杭州市第一中学','杭州师范大学',3),
(null,'本科','应用数学','阳泉第一小学','阳泉区第一中学','清华大学',4);
123456789101112131415161718192021222324252627282930

二、多表查询概述

1️⃣数据准备

(1)删除之前 emp, dept表的测试数据

(2)执行如下脚本,创建emp表与dept表并插入测试数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
-- 创建dept表,并插入数据
create table dept(
id int auto_increment comment 'ID' primary key,
name varchar(50) not null comment '部门名称'
)comment '部门表';

INSERT INTO dept (id, name) VALUES (1, '研发部'), (2, '市场部'),(3, '财务部'), (4,
'销售部'), (5, '总经办'), (6, '人事部');

-- 创建emp表,并插入数据
create table emp(
id int auto_increment comment 'ID' primary key,
name varchar(50) not null comment '姓名',
age int comment '年龄',
job varchar(20) comment '职位',
salary int comment '薪资',
entrydate date comment '入职时间',
managerid int comment '直属领导ID',
dept_id int comment '部门ID'
)comment '员工表';

-- 添加外键
alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references
dept(id);

INSERT INTO emp (id, name, age, job,salary, entrydate, managerid, dept_id)
VALUES
(1, '金庸', 66, '总裁',20000, '2000-01-01', null,5),
(2, '张无忌', 20, '项目经理',12500, '2005-12-05', 1,1),
(3, '杨逍', 33, '开发', 8400,'2000-11-03', 2,1),
(4, '韦一笑', 48, '开发',11000, '2002-02-05', 2,1),
(5, '常遇春', 43, '开发',10500, '2004-09-07', 3,1),
(6, '小昭', 19, '程序员鼓励师',6600, '2004-10-12', 2,1),
(7, '灭绝', 60, '财务总监',8500, '2002-09-12', 1,3),
(8, '周芷若', 19, '会计',48000, '2006-06-02', 7,3),
(9, '丁敏君', 23, '出纳',5250, '2009-05-13', 7,3),
(10, '赵敏', 20, '市场部总监',12500, '2004-10-12', 1,2),
(11, '鹿杖客', 56, '职员',3750, '2006-10-03', 10,2),
(12, '鹤笔翁', 19, '职员',3750, '2007-05-09', 10,2),
(13, '方东白', 19, '职员',5500, '2009-02-12', 10,2),
(14, '张三丰', 88, '销售总监',14000, '2004-10-12', 1,4),
(15, '俞莲舟', 38, '销售',4600, '2004-10-12', 14,4),
(16, '宋远桥', 40, '销售',4600, '2004-10-12', 14,4),
(17, '陈友谅', 42, null,2000, '2011-10-12', 1,null);
1234567891011121314151617181920212223242526272829303132333435363738394041424344

dept表共6条记录,emp表共17条记录。

2️⃣概述

多表查询就是指从多张表中查询数据。

原来查询单表数据,执行的SQL形式为:select * from emp;

那么我们要执行多表查询,就只需要使用逗号分隔多张表即可,如: select * from emp , dept; 具体的执行结果如下:

在这里插入图片描述

此时,我们看到查询结果中包含了大量的结果集,总共102条记录,而这其实就是员工表emp所有的记录
(17) 与 部门表dept所有记录(6) 的所有组合情况,这种现象称之为笛卡尔积。接下来,就来简单介绍下笛卡尔积。

笛卡尔积: 笛卡尔乘积是指在数学中,两个集合A集合 和 B集合的所有组合情况。

在这里插入图片描述

而在多表查询中,我们是需要消除无效的笛卡尔积的,只保留两张表关联部分的数据。

在这里插入图片描述

在这里插入图片描述

在SQL语句中,如何来去除无效的笛卡尔积呢? 我们可以给多表查询加上连接查询的条件即可。

1
2
select * from emp , dept where emp.dept_id = dept.id;
1

在这里插入图片描述

而由于id为17的员工,没有dept_id字段值,所以在多表查询时,根据连接查询的条件并没有查询
到。

3️⃣分类

连接查询

  • 内连接:相当于查询A、B交集部分数据
  • 外连接:左外连接:查询左表所有数据,以及两张表交集部分数据;右外连接:查询右表所有数据,以及两张表交集部分数据
  • 自连接:当前表与自身的连接查询,自连接必须使用表别名

子查询

在这里插入图片描述

三、内连接

在这里插入图片描述
内连接查询的是两张表交集部分的数据。(也就是绿色部分的数据)

内连接的语法分为两种: 隐式内连接、显式内连接。先来学习一下具体的语法结构。

🍀(1)隐式内连接

1
2
SELECT 字段列表 FROM1 , 表2 WHERE 条件 ... ;
1

🍀(2)显式内连接

1
2
SELECT 字段列表 FROM1 [ INNER ] JOIN2 ON 连接条件 ... ;
1

案例:

A. 查询每一个员工的姓名 , 及关联的部门的名称 (隐式内连接实现)

表结构: emp , dept
连接条件: emp.dept_id = dept.id

1
2
3
4
select emp.name , dept.name from emp , dept where emp.dept_id = dept.id ;
-- 为每一张表起别名,简化SQL编写
select e.name,d.name from emp e , dept d where e.dept_id = d.id;
123

B. 查询每一个员工的姓名 , 及关联的部门的名称 (显式内连接实现) — INNER JOIN …ON …

表结构: emp , dept
连接条件: emp.dept_id = dept.id

1
2
3
4
select e.name, d.name from emp e inner join dept d on e.dept_id = d.id;
-- 为每一张表起别名,简化SQL编写
select e.name, d.name from emp e join dept d on e.dept_id = d.id;
123

表的别名:

  • ①. tablea as 别名1 , tableb as 别名2 ;
  • ②. tablea 别名1 , tableb 别名2 ;

注意事项: 一旦为表起了别名,就不能再使用表名来指定对应的字段了,此时只能够使用别名来指定字
段。

四、外连接

在这里插入图片描述
外连接分为两种,分别是:左外连接 和 右外连接。具体的语法结构为:

🍀(1) 左外连接

1
2
SELECT 字段列表 FROM1 LEFT [ OUTER ] JOIN2 ON 条件 ... ;
1

左外连接相当于查询表1(左表)的所有数据,当然也包含表1和表2交集部分的数据。

🍀(2) 右外连接

1
2
SELECT 字段列表 FROM1 RIGHT [ OUTER ] JOIN2 ON 条件 ... ;
1

右外连接相当于查询表2(右表)的所有数据,当然也包含表1和表2交集部分的数据。

案例:

A. 查询emp表的所有数据, 和对应的部门信息

由于需求中提到,要查询emp的所有数据,所以是不能内连接查询的,需要考虑使用外连接查询。

表结构: emp, dept
连接条件: emp.dept_id = dept.id

1
2
3
select e.*, d.name from emp e left outer join dept d on e.dept_id = d.id;
select e.*, d.name from emp e left join dept d on e.dept_id = d.id;
12

B. 查询dept表的所有数据, 和对应的员工信息(右外连接)

由于需求中提到,要查询dept表的所有数据,所以是不能内连接查询的,需要考虑使用外连接查询。

表结构: emp, dept
连接条件: emp.dept_id = dept.id

1
2
3
select d.*, e.* from emp e right outer join dept d on e.dept_id = d.id;
select d.*, e.* from dept d left outer join emp e on e.dept_id = d.id;
12

注意事项: 左外连接和右外连接是可以相互替换的,只需要调整在连接查询时SQL中,表结构的先后顺序就可以了。而我们在日常开发使用时,更偏向于左外连接。

五、自连接

1️⃣自连接查询

自连接查询,顾名思义,就是自己连接自己,也就是把一张表连接查询多次。我们先来学习一下自连接
的查询语法:

1
2
SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件 ... ;
1

而对于自连接查询,可以是内连接查询,也可以是外连接查询。

案例:

A. 查询员工 及其 所属领导的名字

表结构: emp

1
2
select a.name , b.name from emp a , emp b where a.managerid = b.id;
1

B. 查询所有员工 emp 及其领导的名字 emp , 如果员工没有领导, 也需要查询出来

表结构: emp a , emp b

1
2
3
select a.name '员工', b.name '领导' from emp a left join emp b on a.managerid =
b.id;
12

注意事项: 在自连接查询中,必须要为表起别名,要不然我们不清楚所指定的条件、返回的字段,到底是哪一张表的字段。

2️⃣联合查询

对于union查询,就是把多次查询的结果合并起来,形成一个新的查询结果集。

1
2
3
4
SELECT 字段列表 FROM 表A ...
UNION [ ALL ]
SELECT 字段列表 FROM 表B ....;
123
  • 对于联合查询的多张表的列数必须保持一致,字段类型也需要保持一致。
  • union all 会将全部的数据直接合并在一起,union 会对合并之后的数据去重。

案例:

A. 将薪资低于 5000 的员工 , 和 年龄大于 50 岁的员工全部查询出来.

当前对于这个需求,我们可以直接使用多条件查询,使用逻辑运算符 or 连接即可。 那这里呢,我们也可以通过union/union all来联合查询.

1
2
3
4
select * from emp where salary < 5000
union all
select * from emp where age > 50;
123

在这里插入图片描述
union all查询出来的结果,仅仅进行简单的合并,并未去重。

1
2
3
4
select * from emp where salary < 5000
union
select * from emp where age > 50;
123

在这里插入图片描述
union 联合查询,会对查询出来的结果进行去重处理。

注意:

如果多条查询语句查询出来的结果,字段数量不一致,在进行union/union all联合查询时,将会报错,如:
在这里插入图片描述

六、子查询

1️⃣概述

🍀(1)概念

SQL语句中嵌套SELECT语句,称为嵌套查询,又称子查询。

1
2
SELECT * FROM t1 WHERE column1 = ( SELECT column1 FROM t2 );
1

子查询外部的语句可以是INSERT / UPDATE / DELETE / SELECT 的任何一个。

🍀(2)分类

根据子查询结果不同,分为:

  • A. 标量子查询(子查询结果为单个值)
  • B. 列子查询(子查询结果为一列)
  • C. 行子查询(子查询结果为一行)
  • D. 表子查询(子查询结果为多行多列)

根据子查询位置,分为:

  • A. WHERE之后
  • B. FROM之后
  • C. SELECT之后

2️⃣标量子查询

子查询返回的结果是单个值(数字、字符串、日期等),最简单的形式,这种子查询称为标量子查询。

常用的操作符:= , <>, > , >=, <, <=

案例:

A. 查询 “销售部” 的所有员工信息

完成这个需求时,我们可以将需求分解为两步:

①. 查询 “销售部” 部门ID

1
2
select id from dept where name = '销售部';
1

②. 根据 “销售部” 部门ID, 查询员工信息

1
2
select * from emp where dept_id = (select id from dept where name = '销售部');
1

B. 查询在 “方东白” 入职之后的员工信息

完成这个需求时,我们可以将需求分解为两步:

①. 查询 方东白 的入职日期

1
2
select entrydate from emp where name = '方东白';
1

②. 查询指定入职日期之后入职的员工信息

1
2
3
select * from emp where entrydate > (select entrydate from emp where name = '方东
白');
12

3️⃣列子查询

子查询返回的结果是一列(可以是多行),这种子查询称为列子查询。

常用的操作符:IN 、NOT IN 、 ANY 、SOME 、 ALL

操作符 描述
IN 在指定的集合范围之内,多选一
NOT IN 不在指定的集合范围之内
ANY 子查询返回列表中,有任意一个满足即可
SOME 与ANY等同,使用SOME的地方都可以使用ANY
ALL 子查询返回列表的所有值都必须满足

案例:

A. 查询 “销售部” 和 “市场部” 的所有员工信息

分解为以下两步:

①. 查询 “销售部” 和 “市场部” 的部门ID

1
2
select id from dept where name = '销售部' or name = '市场部';
1

②. 根据部门ID, 查询员工信息

1
2
3
select * from emp where dept_id in (select id from dept where name = '销售部' or
name = '市场部');
12

B. 查询比 财务部 所有人工资都高的员工信息

分解为以下两步:

①. 查询所有 财务部 人员工资

1
2
3
select id from dept where name = '财务部';
select salary from emp where dept_id = (select id from dept where name = '财务部');
12

②. 比 财务部 所有人工资都高的员工信息

1
2
3
select * from emp where salary > all ( select salary from emp where dept_id =
(select id from dept where name = '财务部') );
12

C. 查询比研发部其中任意一人工资高的员工信息

分解为以下两步:

①. 查询研发部所有人工资

1
2
select salary from emp where dept_id = (select id from dept where name = '研发部');
1

②. 比研发部其中任意一人工资高的员工信息

1
2
3
select * from emp where salary > any ( select salary from emp where dept_id =
(select id from dept where name = '研发部') );
12

4️⃣行子查询

子查询返回的结果是一行(可以是多列),这种子查询称为行子查询。

常用的操作符:= 、<> 、IN 、NOT IN

案例:

A. 查询与 “张无忌” 的薪资及直属领导相同的员工信息 ;

这个需求同样可以拆解为两步进行:

①. 查询 “张无忌” 的薪资及直属领导

1
2
select salary, managerid from emp where name = '张无忌';
1

②. 查询与 “张无忌” 的薪资及直属领导相同的员工信息 ;

1
2
3
select * from emp where (salary,managerid) = (select salary, managerid from emp
where name = '张无忌');
12

5️⃣表子查询

子查询返回的结果是多行多列,这种子查询称为表子查询。

常用的操作符:IN

案例:

A. 查询与 “鹿杖客” , “宋远桥” 的职位和薪资相同的员工信息

分解为两步执行:

①. 查询 “鹿杖客” , “宋远桥” 的职位和薪资

1
2
select job, salary from emp where name = '鹿杖客' or name = '宋远桥';
1

②. 查询与 “鹿杖客” , “宋远桥” 的职位和薪资相同的员工信息

1
2
3
select * from emp where (job,salary) in ( select job, salary from emp where name =
'鹿杖客' or name = '宋远桥' );
12

B. 查询入职日期是 “2006-01-01” 之后的员工信息 , 及其部门信息

分解为两步执行:

①. 入职日期是 “2006-01-01” 之后的员工信息

1
2
select * from emp where entrydate > '2006-01-01';
1

②. 查询这部分员工, 对应的部门信息;

1
2
3
select e.*, d.* from (select * from emp where entrydate > '2006-01-01') e left
join dept d on e.dept_id = d.id ;
12

七、多表查询案例

数据环境准备:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
create table salgrade(
grade int,
losal int,
hisal int
) comment '薪资等级表';

insert into salgrade values (1,0,3000);
insert into salgrade values (2,3001,5000);
insert into salgrade values (3,5001,8000);
insert into salgrade values (4,8001,10000);
insert into salgrade values (5,10001,15000);
insert into salgrade values (6,15001,20000);
insert into salgrade values (7,20001,25000);
insert into salgrade values (8,25001,30000);
1234567891011121314

在这个案例中,我们主要运用上面所讲解的多表查询的语法,完成以下的12个需求即可,而这里主要涉
及到的表就三张:emp员工表、dept部门表、salgrade薪资等级表 。

(1)查询员工的姓名、年龄、职位、部门信息 (隐式内连接)

表: emp , dept
连接条件: emp.dept_id = dept.id

1
2
select e.name , e.age , e.job , d.name from emp e , dept d where e.dept_id = d.id;
1

(2)查询年龄小于30岁的员工的姓名、年龄、职位、部门信息(显式内连接)

表: emp , dept
连接条件: emp.dept_id = dept.id

1
2
3
select e.name , e.age , e.job , d.name from emp e inner join dept d on e.dept_id =
d.id where e.age < 30;
12

(3)查询拥有员工的部门ID、部门名称

表: emp , dept
连接条件: emp.dept_id = dept.id

1
2
select distinct d.id , d.name from emp e , dept d where e.dept_id = d.id;
1

(4)查询所有年龄大于40岁的员工, 及其归属的部门名称; 如果员工没有分配部门, 也需要展示出来(外连接)

表: emp , dept
连接条件: emp.dept_id = dept.id

1
2
3
select e.*, d.name from emp e left join dept d on e.dept_id = d.id where e.age >
40 ;
12

(5)查询所有员工的工资等级

表: emp , salgrade
连接条件 : emp.salary >= salgrade.losal and emp.salary <= salgrade.hisal

1
2
3
4
5
6
7
-- 方式一
select e.* , s.grade , s.losal, s.hisal from emp e , salgrade s where e.salary >=
s.losal and e.salary <= s.hisal;
-- 方式二
select e.* , s.grade , s.losal, s.hisal from emp e , salgrade s where e.salary
between s.losal and s.hisal;
123456

(6)查询 “研发部” 所有员工的信息及 工资等级

表: emp , salgrade , dept
连接条件 : emp.salary between salgrade.losal and salgrade.hisal , emp.dept_id = dept.id
查询条件 : dept.name = ‘研发部’

1
2
3
select e.* , s.grade from emp e , dept d , salgrade s where e.dept_id = d.id and (
e.salary between s.losal and s.hisal ) and d.name = '研发部';
12

(7)查询 “研发部” 员工的平均工资

表: emp , dept
连接条件 : emp.dept_id = dept.id

1
2
3
select avg(e.salary) from emp e, dept d where e.dept_id = d.id and d.name = '研发
部';
12

(8)查询工资比 “灭绝” 高的员工信息

①. 查询 “灭绝” 的薪资

1
2
select salary from emp where name = '灭绝';
1

②. 查询比她工资高的员工数据

1
2
select * from emp where salary > ( select salary from emp where name = '灭绝' );
1

(9)查询比平均薪资高的员工信息

①. 查询员工的平均薪资

1
2
select avg(salary) from emp;
1

②. 查询比平均薪资高的员工信息

1
2
select * from emp where salary > ( select avg(salary) from emp );
1

(10)查询低于本部门平均工资的员工信息

①. 查询指定部门平均薪资

1
2
3
select avg(e1.salary) from emp e1 where e1.dept_id = 1;
select avg(e1.salary) from emp e1 where e1.dept_id = 2;
12

②. 查询低于本部门平均工资的员工信息

1
2
3
select * from emp e2 where e2.salary < ( select avg(e1.salary) from emp e1 where
e1.dept_id = e2.dept_id );
12

(11)查询所有的部门信息, 并统计部门的员工人数

1
2
3
select d.id, d.name , ( select count(*) from emp e where e.dept_id = d.id ) '人数'
from dept d;
12

(12)查询所有学生的选课情况, 展示出学生名称, 学号, 课程名称

表: student , course , student_course
连接条件: student.id = student_course.studentid , course.id = student_course.courseid

1
2
3
select s.name , s.no , c.name from student s , student_course sc , course c where
s.id = sc.studentid and sc.courseid = c.id ;
12

注意: 以上需求的实现方式可能会很多, SQL写法也有很多,只要能满足我们的需求,查询出符合条
件的记录即可。

事务

一、事务简介

事务 是一组操作的集合,它是一个不可分割的工作单位,事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求,即这些操作要么同时成功,要么同时失败。

比如: 张三给李四转账1000块钱,张三银行账户的钱减少1000,而李四银行账户的钱要增加1000。 这一组操作就必须在一个事务的范围内,要么都成功,要么都失败。

在这里插入图片描述
在这里插入图片描述

正常情况: 转账这个操作, 需要分为以下这么三步来完成 , 三步完成之后, 张三减少1000, 而李四增加1000, 转账成功 :

在这里插入图片描述

异常情况: 转账这个操作, 也是分为以下这么三步来完成 , 在执行第三步是报错了, 这样就导致张三减少1000块钱, 而李四的金额没变, 这样就造成了数据的不一致, 就出现问题了。

在这里插入图片描述

为了解决上述的问题,就需要通过数据的事务来完成,我们只需要在业务逻辑执行之前开启事务,执行完毕后提交事务。如果执行过程中报错,则回滚事务,把数据恢复到事务开始之前的状态。

在这里插入图片描述

注意: 默认MySQL的事务是自动提交的,也就是说,当执行完一条DML语句时,MySQL会立即隐
式的提交事务。

二、事务操作

数据准备:

1
2
3
4
5
6
7
8
9
10
drop table if exists account;

create table account(
id int primary key AUTO_INCREMENT comment 'ID',
name varchar(10) comment '姓名',
money double(10,2) comment '余额'
) comment '账户表';

insert into account(name, money) VALUES ('张三',2000), ('李四',2000);
123456789

1️⃣未控制事务

🍀(1)测试正常情况

1
2
3
4
5
6
7
-- 1. 查询张三余额
select * from account where name = '张三';
-- 2. 张三的余额减少1000
update account set money = money - 1000 where name = '张三';
-- 3. 李四的余额增加1000
update account set money = money + 1000 where name = '李四';

测试完毕之后检查数据的状态, 可以看到数据操作前后是一致的。

在这里插入图片描述

🍀(2)测试异常情况

1
2
3
4
5
6
7
8
-- 1. 查询张三余额
select * from account where name = '张三';
-- 2. 张三的余额减少1000
update account set money = money - 1000 where name = '张三';
出错了....
-- 3. 李四的余额增加1000
update account set money = money + 1000 where name = '李四';

我们把数据都恢复到2000, 然后再次一次性执行上述的SQL语句(出错了… 这句话不符合SQL语
法,执行就会报错),检查最终的数据情况, 发现数据在操作前后不一致了。

在这里插入图片描述

2️⃣控制事务一

🍀(1)查看/设置事务提交方式

1
2
3
SELECT @@autocommit ;
SET @@autocommit = 0 ;

🍀(2)提交事务

1
2
COMMIT;可以理解成保存

🍀(3)回滚事务

1
2
ROLLBACK;撤销

注意: 上述的这种方式,我们是修改了事务的自动提交行为, 把默认的自动提交修改为了手动提
交, 此时我们执行的DML语句都不会提交, 需要手动的执行commit进行提交。

3️⃣控制事务二

🍀(1)开启事务

1
START TRANSACTION 或 BEGIN ;

🍀(2)提交事务

1
COMMIT;

🍀(3)回滚事务

1
ROLLBACK;

转账案例:

1
2
3
4
5
6
7
8
9
10
11
12
-- 开启事务
start transaction 可理解为打开某个软件
-- 1. 查询张三余额
select * from account where name = '张三';
-- 2. 张三的余额减少1000
update account set money = money - 1000 where name = '张三';
-- 3. 李四的余额增加1000
update account set money = money + 1000 where name = '李四';
-- 如果正常执行完毕, 则提交事务
commit;保存软件
-- 如果执行过程中报错, 则回滚事务
-- rollback;撤销操作

三、事务四大特性

  • 原子性(Atomicity):事务是不可分割的最小操作单元,要么全部成功,要么全部失败。
  • 一致性(Consistency):事务完成时,必须使所有的数据都保持一致状态。
  • 隔离性(Isolation):数据库系统提供的隔离机制,保证事务在不受外部并发操作影响的独立环境下运行。
  • 持久性(Durability):事务一旦提交或回滚,它对数据库中的数据的改变就是永久的。

上述就是事务的四大特性,简称ACID

在这里插入图片描述

四、并发事务问题

🍀(1)赃读:一个事务读到另外一个事务还没有提交的数据。

在这里插入图片描述

比如B读取到了A未提交的数据。

🍀(2)不可重复读:一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。

在这里插入图片描述

事务A两次读取同一条记录,但是读取到的数据却是不一样的。

🍀(3)幻读:一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在,好像出现了 “幻影”。

在这里插入图片描述

五、事务隔离级别

为了解决并发事务所引发的问题,在数据库中引入了事务隔离级别。主要有以下几种:

隔离级别 脏读 不可重复读 幻读
Read uncommitted
Read committed ×
Repeatable Read(默认) × ×
Serializable × × ×

🍀(1)查看事务隔离级别

1
2
SELECT @@TRANSACTION_ISOLATION;
1

🍀(2)设置事务隔离级别

1
2
3
SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL { READ UNCOMMITTED |
READ COMMITTED | REPEATABLE READ | SERIALIZABLE }
12

注意: 事务隔离级别越高,数据越安全,但是性能越低。

1.不可重复读是由update引起的

2.幻读是由insert引起的

基础篇到此结束,感谢陪伴!