diff --git a/DB.md b/DB.md
index 2729b15..7fd57da 100644
--- a/DB.md
+++ b/DB.md
@@ -4,13 +4,13 @@
### 数据库
-数据库:DataBase,简称DB,用于存储和管理数据的仓库,它的存储空间很大,可以存放百万上亿条数据。
+数据库:DataBase,简称 DB,存储和管理数据的仓库
数据库的优势:
- 可以持久化存储数据
- 方便存储和管理数据
-- 使用了统一的方式操作数据库 -- SQL
+- 使用了统一的方式操作数据库 SQL
数据库、数据表、数据的关系介绍:
@@ -22,17 +22,22 @@
- 数据表
- 数据库最重要的组成部分之一
- - 它由纵向的列和横向的行组成(类似excel表格)
+ - 由纵向的列和横向的行组成(类似 excel 表格)
- 可以指定列名、数据类型、约束等
- 一个表中可以存储多条数据
-- 数据
+- 数据:想要永久化存储的数据
+
+
+
+
+参考视频:https://www.bilibili.com/video/BV1zJ411M7TB
+
+参考专栏:https://time.geekbang.org/column/intro/139
+
+参考书籍:https://book.douban.com/subject/35231266/
- - 想要永久化存储的数据
- 
-
-
***
@@ -40,19 +45,19 @@
### MySQL
-MySQL数据库是一个最流行的关系型数据库管理系统之一。
+MySQL 数据库是一个最流行的关系型数据库管理系统之一,关系型数据库是将数据保存在不同的数据表中,而且表与表之间可以有关联关系,提高了灵活性
-关系型数据库是将数据保存在不同的数据表中,而不是将所有的数据放在一个大仓库内,而且表与表之间还可以有关联关系,这样就提高了访问速度以及提高了灵活性。
+缺点:数据存储在磁盘中,导致读写性能差,而且数据关系复杂,扩展性差
-MySQL所使用的SQL语句是用于访问数据库最常用的标准化语言。
+MySQL 所使用的 SQL 语句是用于访问数据库最常用的标准化语言
-MySQL配置:
+MySQL 配置:
-* MySQL安装:https://www.jianshu.com/p/ba48f1e386f0
+* MySQL 安装:https://www.jianshu.com/p/ba48f1e386f0
-* MySQL配置:
+* MySQL 配置:
- * 修改MySQL默认字符集
+ * 修改 MySQL 默认字符集:安装 MySQL 之后第一件事就是修改字符集编码
```mysql
vim /etc/mysql/my.cnf
@@ -66,13 +71,13 @@ MySQL配置:
default-character-set=utf8
```
- * 启动MySQL服务:
+ * 启动 MySQL 服务:
```shell
systemctl start/restart mysql
```
- * 登录MySQL:
+ * 登录 MySQL:
```shell
mysql -u root -p 敲回车,输入密码
@@ -95,7 +100,7 @@ MySQL配置:
set password=password('密码');
```
- * 授予远程连接权限(MySQL内输入):
+ * 授予远程连接权限(MySQL 内输入):
```mysql
-- 授权
@@ -104,67 +109,82 @@ MySQL配置:
flush privileges;
```
-* 修改MySQL绑定IP:
+* 修改 MySQL 绑定 IP:
```shell
cd /etc/mysql/mysql.conf.d
sudo chmod 666 mysqld.cnf
vim mysqld.cnf
- #bind-address = 127.0.0.1注释该行
+ # bind-address = 127.0.0.1注释该行
```
-* 关闭Linux防火墙
+* 关闭 Linux 防火墙
```shell
systemctl stop firewalld.service
- 放行3306端口
+ # 放行3306端口
```
-
+
***
-## 单表
-### SQL介绍
-- SQL
+## 体系架构
- - Structured Query Language:结构化查询语言
- - 定义了操作所有关系型数据库的规则。每一种数据库操作的方式可能会存在不一样的地方,我们称为“方言”。
+### 整体架构
+
+体系结构详解:
+
+* 第一层:网络连接层
+ * 一些客户端和链接服务,包含本地 Socket 通信和大多数基于客户端/服务端工具实现的 TCP/IP 通信,主要完成一些类似于连接处理、授权认证、及相关的安全方案
+ * 在该层上引入了**连接池** Connection Pool 的概念,管理缓冲用户连接,线程处理等需要缓存的需求
+ * 在该层上实现基于 SSL 的安全链接,服务器也会为安全接入的每个客户端验证它所具有的操作权限
+
+- 第二层:核心服务层
+ * 查询缓存、分析器、优化器、执行器等,涵盖 MySQL 的大多数核心服务功能,所有的内置函数(日期、数学、加密函数等)
+ * Management Serveices & Utilities:系统管理和控制工具,备份、安全、复制、集群等
+ * SQL Interface:接受用户的 SQL 命令,并且返回用户需要查询的结果
+ * Parser:SQL 语句分析器
+ * Optimizer:查询优化器
+ * Caches & Buffers:查询缓存,服务器会查询内部的缓存,如果缓存空间足够大,可以在大量读操作的环境中提升系统性能
+ * 所有**跨存储引擎的功能**在这一层实现,如存储过程、触发器、视图等
+ * 在该层服务器会解析查询并创建相应的内部解析树,并对其完成相应的优化如确定表的查询顺序,是否利用索引等, 最后生成相应的执行操作
+ * MySQL 中服务器层不管理事务,**事务是由存储引擎实现的**
+- 第三层:存储引擎层
+ - Pluggable Storage Engines:存储引擎接口,MySQL 区别于其他数据库的重要特点就是其存储引擎的架构模式是插件式的(存储引擎是基于表的,而不是数据库)
+ - 存储引擎**真正的负责了 MySQL 中数据的存储和提取**,服务器通过 API 和存储引擎进行通信
+ - 不同的存储引擎具有不同的功能,共用一个 Server 层,可以根据开发的需要,来选取合适的存储引擎
+- 第四层:系统文件层
+ - 数据存储层,主要是将数据存储在文件系统之上,并完成与存储引擎的交互
+ - File System:文件系统,保存配置文件、数据文件、日志文件、错误文件、二进制文件等
+
+
-- SQL通用语法
- - SQL 语句可以单行或多行书写,以**分号结尾**。
- - 可使用空格和缩进来增强语句的可读性。
- - MySQL 数据库的 SQL 语句不区分大小写,**关键字建议使用大写**。
- - 数据库的注释:
- - 单行注释:-- 注释内容 #注释内容(mysql特有)
- - 多行注释:/* 注释内容 */
-- SQL分类
+***
- - DDL(Data Definition Language)数据定义语言
- - 用来定义数据库对象:数据库,表,列等。关键字:create, drop,alter 等
- - DML(Data Manipulation Language)数据操作语言
+### 建立连接
- - 用来对数据库中表的数据进行增删改。关键字:insert, delete, update 等
+#### 连接器
- - DQL(Data Query Language)数据查询语言
+池化技术:对于访问数据库来说,建立连接的代价是比较昂贵的,因为每个连接对应一个用来交互的线程,频繁的创建关闭连接比较耗费资源,有必要建立数据库连接池,以提高访问的性能
- - 用来查询数据库中表的记录(数据)。关键字:select, where 等
+连接建立 TCP 以后需要做**权限验证**,验证成功后可以进行执行 SQL。如果这时管理员账号对这个用户的权限做了修改,也不会影响已经存在连接的权限,只有再新建的连接才会使用新的权限设置
- - DCL(Data Control Language)数据控制语言(了解)
+MySQL 服务器可以同时和多个客户端进行交互,所以要保证每个连接会话的隔离性(事务机制部分详解)
- - 用来定义数据库的访问权限和安全级别,及创建用户。关键字:GRANT, REVOKE 等
+整体的执行流程:
- 
+
@@ -172,643 +192,373 @@ MySQL配置:
-### DDL
+#### 权限信息
-#### 操作数据库
+grant 语句会同时修改数据表和内存,判断权限的时候使用的是内存数据
-* R(Retrieve):查询
+flush privileges 语句本身会用数据表(磁盘)的数据重建一份内存权限数据,所以在权限数据可能存在不一致的情况下使用,这种不一致往往是由于直接用 DML 语句操作系统权限表导致的,所以尽量不要使用这类语句
- * 查询所有数据库:
+
- ```mysql
- SHOW DATABASES;
- ```
- * 查询某个数据库的创建语句
- ```sql
- SHOW CREATE DATABASE 数据库名称; -- 标准语法
-
- SHOW CREATE DATABASE mysql; --查看mysql数据库的创建格式
- ```
-
-* C(Create):创建
- * 创建数据库
- ```mysql
- CREATE DATABASE 数据库名称;-- 标准语法
-
- CREATE DATABASE db1; -- 创建db1数据库
- ```
+****
- * 创建数据库(判断,如果不存在则创建)
- ```mysql
- CREATE DATABASE IF NOT EXISTS 数据库名称;
- ```
- * 创建数据库,并指定字符集
+#### 连接状态
- ```mysql
- CREATE DATABASE 数据库名称 CHARACTER SET 字符集名称;
- ```
+客户端如果长时间没有操作,连接器就会自动断开,时间是由参数 wait_timeout 控制的,默认值是 8 小时。如果在连接被断开之后,客户端**再次发送请求**的话,就会收到一个错误提醒:`Lost connection to MySQL server during query`
- * 例如:创建db4数据库、如果不存在则创建,指定字符集为gbk
+数据库里面,长连接是指连接成功后,如果客户端持续有请求,则一直使用同一个连接;短连接则是指每次执行完很少的几次查询就断开连接,下次查询再重新建立一个
- ```mysql
- -- 创建db4数据库、如果不存在则创建,指定字符集为gbk
- CREATE DATABASE IF NOT EXISTS db4 CHARACTER SET gbk;
-
- -- 查看db4数据库的字符集
- SHOW CREATE DATABASE db4;
- ```
+为了减少连接的创建,推荐使用长连接,但是**过多的长连接会造成 OOM**,解决方案:
-
+* 定期断开长连接,使用一段时间,或者程序里面判断执行过一个占用内存的大查询后,断开连接,之后要查询再重连
-* U(Update):修改
+ ```mysql
+ KILL CONNECTION id
+ ```
- * 修改数据库的字符集
+* MySQL 5.7 版本,可以在每次执行一个比较大的操作后,通过执行 mysql_reset_connection 来重新初始化连接资源,这个过程不需要重连和重新做权限验证,但是会将连接恢复到刚刚创建完时的状态
- ```mysql
- ALTER DATABASE 数据库名称 CHARACTER SET 字符集名称;
- ```
+SHOW PROCESSLIST:查看当前 MySQL 在进行的线程,可以实时地查看 SQL 的执行情况,其中的 Command 列显示为 Sleep 的这一行,就表示现在系统里面有一个空闲连接
- * 常用字符集:
+
- ```mysql
- --查询所有支持的字符集
- SHOW CHARSET;
- --查看所有支持的校对规则
- SHOW COLLATION;
-
- -- 字符集: utf8,latinI,GBK,,GBK是utf8的子集
- -- 校对规则: ci 大小定不敏感,cs或bin大小写敏感
- ```
+| 参数 | 含义 |
+| ------- | ------------------------------------------------------------ |
+| ID | 用户登录 mysql 时系统分配的 connection_id,可以使用函数 connection_id() 查看 |
+| User | 显示当前用户,如果不是 root,这个命令就只显示用户权限范围的 sql 语句 |
+| Host | 显示这个语句是从哪个 ip 的哪个端口上发的,可以用来跟踪出现问题语句的用户 |
+| db | 显示这个进程目前连接的是哪个数据库 |
+| Command | 显示当前连接的执行的命令,一般取值为休眠 Sleep、查询 Query、连接 Connect 等 |
+| Time | 显示这个状态持续的时间,单位是秒 |
+| State | 显示使用当前连接的 sql 语句的状态,以查询为例,需要经过 copying to tmp table、sorting result、sending data等状态才可以完成 |
+| Info | 显示执行的 sql 语句,是判断问题语句的一个重要依据 |
-
+**Sending data 状态**表示 MySQL 线程开始访问数据行并把结果返回给客户端,而不仅仅只是返回给客户端,是处于执行器过程中的任意阶段。由于在 Sending data 状态下,MySQL 线程需要做大量磁盘读取操作,所以是整个查询中耗时最长的状态
-* D(Delete):删除
- * 删除数据库:
- ```mysql
- DROP DATABASE 数据库名称;
- ```
- * 删除数据库(判断,如果存在则删除):
- ```mysql
- DROP DATABASE IF EXISTS 数据库名称;
- ```
-
-
+***
-* 使用数据库:
- * 查询当前正在使用的数据库名称
- ```mysql
- SELECT DATABASE();
- ```
+### 执行流程
- * 使用数据库
+#### 查询缓存
- ```mysql
- USE 数据库名称; -- 标准语法
- USE db4; -- 使用db4数据库
- ```
+##### 工作流程
-
+当执行完全相同的 SQL 语句的时候,服务器就会直接从缓存中读取结果,当数据被修改,之前的缓存会失效,修改比较频繁的表不适合做查询缓存
-#### 操作数据表
+查询过程:
-- R(Retrieve):查询
+1. 客户端发送一条查询给服务器
+2. 服务器先会检查查询缓存,如果命中了缓存,则立即返回存储在缓存中的结果(一般是 K-V 键值对),否则进入下一阶段
+3. 分析器进行 SQL 分析,再由优化器生成对应的执行计划
+4. 执行器根据优化器生成的执行计划,调用存储引擎的 API 来执行查询
+5. 将结果返回给客户端
- - 查询数据库中所有的数据表
+大多数情况下不建议使用查询缓存,因为查询缓存往往弊大于利
- ```mysql
- USE mysql;-- 使用mysql数据库
-
- SHOW TABLES;-- 查询库中所有的表
- ```
-
- - 查询表结构
+* 查询缓存的**失效非常频繁**,只要有对一个表的更新,这个表上所有的查询缓存都会被清空。因此很可能费力地把结果存起来,还没使用就被一个更新全清空了,对于更新压力大的数据库来说,查询缓存的命中率会非常低
+* 除非业务就是有一张静态表,很长时间才会更新一次,比如一个系统配置表,那这张表上的查询才适合使用查询缓存
- ```mysql
- DESC 表名;
- ```
-
- - 查询表字符集
-
- ```mysql
- SHOW TABLE STATUS FROM 库名 LIKE '表名';
- ```
-
-- C(Create):创建
+***
- - 创建数据表
- ```mysql
- CREATE TABLE 表名(
- 列名1 数据类型1,
- 列名2 数据类型2,
- ....
- 列名n 数据类型n
- );
- -- 注意:最后一列,不需要加逗号
- ```
- - 复制表
+##### 缓存配置
- ```mysql
- CREATE TABLE 表名 LIKE 被复制的表名; -- 标准语法
-
- CREATE TABLE product2 LIKE product; -- 复制product表到product2表
- ```
+1. 查看当前 MySQL 数据库是否支持查询缓存:
- - 数据类型
+ ```mysql
+ SHOW VARIABLES LIKE 'have_query_cache'; -- YES
+ ```
- | 数据类型 | 说明 |
- | --------- | ------------------------------------------------------------ |
- | INT | 整数类型 |
- | DOUBLE | 小数类型 |
- | DATE | 日期,只包含年月日:yyyy-MM-dd |
- | DATETIME | 日期,包含年月日时分秒:yyyy-MM-dd HH:mm:ss |
- | TIMESTAMP | 时间戳类型,包含年月日时分秒:yyyy-MM-dd HH:mm:ss
如果不给这个字段赋值或赋值为null,则默认使用当前的系统时间 |
- | VARCHAR | 字符串
name varchar(20):姓名最大20个字符:zhangsan8个字符,张三2个字符 |
+2. 查看当前 MySQL 是否开启了查询缓存:
- `INT(n)`:n代表位数
+ ```mysql
+ SHOW VARIABLES LIKE 'query_cache_type'; -- OFF
+ ```
- * 3:int(9)显示结果为000000010
- * 3:int(3)显示结果为010
-
- `varchar(n)`:n表示的是字符数
-
- - 例如:
-
- ```mysql
- -- 使用db3数据库
- USE db3;
-
- -- 创建一个product商品表
- CREATE TABLE product(
- id INT, -- 商品编号
- NAME VARCHAR(30), -- 商品名称
- price DOUBLE, -- 商品价格
- stock INT, -- 商品库存
- insert_time DATE -- 上架时间
- );
- ```
+ 参数说明:
-
+ * OFF 或 0:查询缓存功能关闭
-- U(Update):修改
+ * ON 或 1:查询缓存功能打开,查询结果符合缓存条件即会缓存,否则不予缓存;可以显式指定 SQL_NO_CACHE 不予缓存
- - 修改表名
+ * DEMAND 或 2:查询缓存功能按需进行,显式指定 SQL_CACHE 的 SELECT 语句才缓存,其它不予缓存
- ```mysql
- ALTER TABLE 表名 RENAME TO 新的表名;
- ```
-
- - 修改表的字符集
-
- ```mysql
- ALTER TABLE 表名 CHARACTER SET 字符集名称;
- ```
+ ```mysql
+ SELECT SQL_CACHE id, name FROM customer; -- SQL_CACHE:查询结果可缓存
+ SELECT SQL_NO_CACHE id, name FROM customer;-- SQL_NO_CACHE:不使用查询缓存
+ ```
- - 添加一列
-
- ```mysql
- ALTER TABLE 表名 ADD 列名 数据类型;
- ```
-
- - 修改列数据类型
-
- ```mysql
- ALTER TABLE 表名 MODIFY 列名 新数据类型;
- ```
+3. 查看查询缓存的占用大小:
- - 修改列名称和数据类型
+ ```mysql
+ SHOW VARIABLES LIKE 'query_cache_size';-- 单位是字节 1048576 / 1024 = 1024 = 1KB
+ ```
- ```mysql
- ALTER TABLE 表名 CHANGE 列名 新列名 新数据类型;
- ```
-
- - 删除列
-
- ```mysql
- ALTER TABLE 表名 DROP 列名;
- ```
+4. 查看查询缓存的状态变量:
-
-
-- D(Delete):删除
+ ```mysql
+ SHOW STATUS LIKE 'Qcache%';
+ ```
- - 删除数据表
+
- ```mysql
- DROP TABLE 表名;
- ```
-
- - 删除数据表(判断,如果存在则删除)
-
- ```mysql
- DROP TABLE IF EXISTS 表名;
- ```
-
-
+ | 参数 | 含义 |
+ | ----------------------- | ------------------------------------------------------------ |
+ | Qcache_free_blocks | 查询缓存中的可用内存块数 |
+ | Qcache_free_memory | 查询缓存的可用内存量 |
+ | Qcache_hits | 查询缓存命中数 |
+ | Qcache_inserts | 添加到查询缓存的查询数 |
+ | Qcache_lowmen_prunes | 由于内存不足而从查询缓存中删除的查询数 |
+ | Qcache_not_cached | 非缓存查询的数量(由于 query_cache_type 设置而无法缓存或未缓存) |
+ | Qcache_queries_in_cache | 查询缓存中注册的查询数 |
+ | Qcache_total_blocks | 查询缓存中的块总数 |
-***
+5. 配置 my.cnf:
+ ```sh
+ sudo chmod 666 /etc/mysql/my.cnf
+ vim my.cnf
+ # mysqld中配置缓存
+ query_cache_type=1
+ ```
+ 重启服务既可生效,执行 SQL 语句进行验证 ,执行一条比较耗时的 SQL 语句,然后再多执行几次,查看后面几次的执行时间;获取通过查看查询缓存的缓存命中数,来判定是否走查询缓存
-### DML
-#### INSERT
-* 新增表数据
+***
- * 新增格式1:给指定列添加数据
- ```mysql
- INSERT INTO 表名(列名1,列名2...) VALUES (值1,值2...);
- ```
- * 新增格式2:默认给全部列添加数据
+##### 缓存失效
- ```mysql
- INSERT INTO 表名 VALUES (值1,值2,值3,...);
- ```
+查询缓存失效的情况:
- * 新增格式3:批量添加数据
+* SQL 语句不一致,要想命中查询缓存,查询的 SQL 语句必须一致,因为**缓存中 key 是查询的语句**,value 是查询结构
- ```mysql
- -- 给指定列批量添加数据
- INSERT INTO 表名(列名1,列名2,...) VALUES (值1,值2,...),(值1,值2,...)...;
-
- -- 默认给所有列批量添加数据
- INSERT INTO 表名 VALUES (值1,值2,值3,...),(值1,值2,值3,...)...;
- ```
+ ```mysql
+ select count(*) from tb_item;
+ Select count(*) from tb_item; -- 不走缓存,首字母不一致
+ ```
-* 字符串拼接
+* 当查询语句中有一些不确定查询时,则不会缓存,比如:now()、current_date()、curdate()、curtime()、rand()、uuid()、user()、database()
```mysql
- CONCAT(string1,string2,'',...)
+ SELECT * FROM tb_item WHERE updatetime < NOW() LIMIT 1;
+ SELECT USER();
+ SELECT DATABASE();
```
-
-
-
-* 注意事项
- - 列名和值的数量以及数据类型要对应
- - 除了数字类型,其他数据类型的数据都需要加引号(单引双引都可以,推荐单引)
+* 不使用任何表查询语句:
+ ```mysql
+ SELECT 'A';
+ ```
+* 查询 mysql、information_schema、performance_schema 等系统表时,不走查询缓存:
+ ```mysql
+ SELECT * FROM information_schema.engines;
+ ```
+* 在**跨存储引擎**的存储过程、触发器或存储函数的主体内执行的查询,缓存失效
-#### UPDATE
+* 如果表更改,则使用该表的**所有高速缓存查询都将变为无效**并从高速缓存中删除,包括使用 MERGE 映射到已更改表的表的查询,比如:INSERT、UPDATE、DELETE、ALTER TABLE、DROP TABLE、DROP DATABASE
-* 修改表数据语法
-
- * 标准语法
+
- ```mysql
- UPDATE 表名 SET 列名1 = 值1,列名2 = 值2,... [where 条件];
- ```
+***
- * 修改电视的价格为1800、库存为36
- ```mysql
- UPDATE product SET price=1800,stock=36 WHERE NAME='电视';
- SELECT * FROM product;-- 查看所有商品信息
- ```
-* 注意事项
+#### 分析器
- - 修改语句中必须加条件
- - 如果不加条件,则将所有数据都修改
+没有命中查询缓存,就开始了 SQL 的真正执行,分析器会对 SQL 语句做解析
+```sql
+SELECT * FROM t WHERE id = 1;
+```
+解析器:处理语法和解析查询,生成一课对应的解析树
-#### DELETE
+* 先做**词法分析**,输入的是由多个字符串和空格组成的一条 SQL 语句,MySQL 需要识别出里面的字符串分别是什么代表什么。从输入的 select 这个关键字识别出来这是一个查询语句;把字符串 t 识别成 表名 t,把字符串 id 识别成列 id
+* 然后做**语法分析**,根据词法分析的结果,语法分析器会根据语法规则,判断你输入的这个 SQL 语句是否满足 MySQL 语法。如果语句不对,就会收到 `You have an error in your SQL syntax` 的错误提醒
-* 删除表数据语法
+预处理器:进一步检查解析树的合法性,比如数据表和数据列是否存在、别名是否有歧义等
- ```mysql
- DELETE FROM 表名 [WHERE 条件];
- ```
-* 注意事项
- * 删除语句中必须加条件
- * 如果不加条件,则将所有数据删除
+***
-
-***
+#### 优化器
+##### 成本分析
+优化器是在表里面有多个索引的时候,决定使用哪个索引;或者在一个语句有多表关联(join)的时候,决定各个表的连接顺序
-### DQL
+* 根据搜索条件找出所有可能的使用的索引
+* 成本分析,执行成本由 I/O 成本和 CPU 成本组成,计算全表扫描和使用不同索引执行 SQL 的代价
+* 找到一个最优的执行方案,用最小的代价去执行语句
-#### 查询语法
+在数据库里面,扫描行数是影响执行代价的因素之一,扫描的行数越少意味着访问磁盘的次数越少,消耗的 CPU 资源越少,优化器还会结合是否使用临时表、是否排序等因素进行综合判断
-数据库查询遵循条件在前的原则
-```mysql
-SELECT:字段列表
-FROM:表名列表
-WHERE:条件列表
-GROUP BY:分组字段
-HAVING:分组之后的条件
-ORDER BY:排序
-LIMIT:分页限定
-```
+***
-#### 查询全部
-* 查询全部的表数据
+##### 统计数据
- ```mysql
- -- 标准语法
- SELECT * FROM 表名;
-
- -- 查询product表所有数据(常用)
- SELECT * FROM product;
- ```
+MySQL 中保存着两种统计数据:
-* 查询指定字段的表数据
+* innodb_table_stats 存储了表的统计数据,每一条记录对应着一个表的统计数据
+* innodb_index_stats 存储了索引的统计数据,每一条记录对应着一个索引的一个统计项的数据
- ```mysql
- SELECT 列名1,列名2,... FROM 表名;
- ```
+MySQL 在真正执行语句之前,并不能精确地知道满足条件的记录有多少条,只能根据统计信息来估算记录,统计信息就是索引的区分度,一个索引上不同的值的个数(比如性别只能是男女,就是 2 ),称之为基数(cardinality),**基数越大说明区分度越好**
-* 去除重复查询
- 注意:只有全部重复的才可以去除
+通过**采样统计**来获取基数,InnoDB 默认会选择 N 个数据页,统计这些页面上的不同值得到一个平均值,然后乘以这个索引的页面数,就得到了这个索引的基数
- ```mysql
- SELECT DISTINCT 列名1,列名2,... FROM 表名;
- ```
+在 MySQL 中,有两种存储统计数据的方式,可以通过设置参数 `innodb_stats_persistent` 的值来选择:
-* 计算列的值(四则运算)
+* ON:表示统计信息会持久化存储(默认),采样页数 N 默认为 20,可以通过 `innodb_stats_persistent_sample_pages` 指定,页数越多统计的数据越准确,但消耗的资源更大
+* OFF:表示统计信息只存储在内存,采样页数 N 默认为 8,也可以通过系统变量设置(不推荐,每次重新计算浪费资源)
- ```mysql
- SELECT 列名1 运算符(+ - * /) 列名2 FROM 表名;
-
- /*如果某一列值为null,可以进行替换
- ifnull(表达式1,表达式2)
- 表达式1:想替换的列
- 表达式2:想替换的值*/
- ```
+数据表是会持续更新的,两种统计信息的更新方式:
- 例如:
+* 设置 `innodb_stats_auto_recalc` 为 1,当发生变动的记录数量超过表大小的 10% 时,自动触发重新计算,不过是**异步进行**
+* 调用 `ANALYZE TABLE t` 手动更新统计信息,只对信息做**重新统计**(不是重建表),没有修改数据,这个过程中加了 MDL 读锁并且是同步进行,所以会暂时阻塞系统
- ```mysql
- -- 查询商品名称和库存,库存数量在原有基础上加10
- SELECT NAME,stock+10 FROM product;
-
- -- 查询商品名称和库存,库存数量在原有基础上加10。进行null值判断
- SELECT NAME,IFNULL(stock,0)+10 FROM product;
- ```
+**EXPLAIN 执行计划在优化器阶段生成**,如果 explain 的结果预估的 rows 值跟实际情况差距比较大,可以执行 analyze 命令重新修正信息
-* **起别名**
- ```mysql
- SELECT 列名1,列名2,... AS 别名 FROM 表名;
- ```
- 例如:
+***
- ```mysql
- -- 查询商品名称和库存,库存数量在原有基础上加10。进行null值判断,起别名为getSum,AS可以省略。
- SELECT NAME,IFNULL(stock,0)+10 AS getsum FROM product;
- SELECT NAME,IFNULL(stock,0)+10 getsum FROM product;
- ```
-* **CONCAT()**:用于连接两个字段
- ```sql
- SELECT CONCAT(TRIM(col1), '(', TRIM(col2), ')') AS concat_col FROM mytable
- -- 许多数据库会使用空格把一个值填充为列宽,连接的结果出现一些不必要的空格,使用TRIM()可以去除首尾空格
- ```
+##### 错选索引
-
+采样统计本身是估算数据,或者 SQL 语句中的字段选择有问题时,可能导致 MySQL 没有选择正确的执行索引
-***
+解决方法:
+* 采用 force index 强行选择一个索引
+ ```sql
+ SELECT * FROM user FORCE INDEX(name) WHERE NAME='seazean';
+ ```
-#### 条件查询
+* 可以考虑修改 SQL 语句,引导 MySQL 使用期望的索引
-* 条件查询语法
+* 新建一个更合适的索引,来提供给优化器做选择,或删掉误用的索引
- ```mysql
- SELECT 列名 FROM 表名 WHERE 条件;
- ```
-* 条件分类
- | 符号 | 功能 |
- | ------------------- | ------------------------------------------------------------ |
- | > | 大于 |
- | < | 小于 |
- | >= | 大于等于 |
- | <= | 小于等于 |
- | = | 等于 |
- | <> 或 != | 不等于 |
- | BETWEEN ... AND ... | 在某个范围之内(都包含) |
- | **IN(...)** | 多选一 |
- | **LIKE 占位符** | 模糊查询 _单个任意字符 %多个任意字符,[] 匹配集合内的字符
`LIKE '[^AB]%' `:不以 A 和 B 开头的任意文本 |
- | IS NULL | 是NULL |
- | IS NOT NULL | 不是NULL |
- | AND 或 && | 并且 |
- | OR 或 \|\| | 或者 |
- | NOT 或 ! | 非,不是 |
+***
-* 例如:
- ```mysql
- -- 查询库存大于20的商品信息
- SELECT * FROM product WHERE stock > 20;
-
- -- 查询品牌为华为的商品信息
- SELECT * FROM product WHERE brand='华为';
-
- -- 查询金额在4000 ~ 6000之间的商品信息
- SELECT * FROM product WHERE price >= 4000 AND price <= 6000;
- SELECT * FROM product WHERE price BETWEEN 4000 AND 6000;
-
- -- 查询库存为14、30、23的商品信息
- SELECT * FROM product WHERE stock=14 OR stock=30 OR stock=23;
- SELECT * FROM product WHERE stock IN(14,30,23);
-
- -- 查询库存为null的商品信息
- SELECT * FROM product WHERE stock IS NULL;
- -- 查询库存不为null的商品信息
- SELECT * FROM product WHERE stock IS NOT NULL;
-
- -- 查询名称以'小米'为开头的商品信息
- SELECT * FROM product WHERE NAME LIKE '小米%';
-
- -- 查询名称第二个字是'为'的商品信息
- SELECT * FROM product WHERE NAME LIKE '_为%';
-
- -- 查询名称为四个字符的商品信息 4个下划线
- SELECT * FROM product WHERE NAME LIKE '____';
-
- -- 查询名称中包含电脑的商品信息
- SELECT * FROM product WHERE NAME LIKE '%电脑%';
- ```
- 
+#### 执行器
+开始执行的时候,要先判断一下当前连接对表有没有**执行查询的权限**,如果没有就会返回没有权限的错误,在工程实现上,如果命中查询缓存,会在查询缓存返回结果的时候,做权限验证。如果有权限,就打开表继续执行,执行器就会根据表的引擎定义,去使用这个引擎提供的接口
+***
-#### 函数查询
-##### 聚合函数
-* 聚合函数:将一列数据作为一个整体,进行纵向的计算
+#### 引擎层
-* 聚合函数语法
+Server 层和存储引擎层的交互是**以记录为单位的**,存储引擎会将单条记录返回给 Server 层做进一步处理,并不是直接返回所有的记录
- ```mysql
- SELECT 函数名(列名) FROM 表名 [WHERE 条件]
- ```
+工作流程:
-* 聚合函数分类
+* 首先根据二级索引选择扫描范围,获取第一条符合二级索引条件的记录,进行回表查询,将聚簇索引的记录返回 Server 层,由 Server 判断记录是否符合要求
+* 然后在二级索引上继续扫描下一个符合条件的记录
- | 函数名 | 功能 |
- | ----------- | -------------------------------- |
- | COUNT(列名) | 统计数量(一般选用不为null的列) |
- | MAX(列名) | 最大值 |
- | MIN(列名) | 最小值 |
- | SUM(列名) | 求和 |
- | AVG(列名) | 平均值(AVG() 会忽略 NULL 行) |
-* 例如
- ```mysql
- -- 计算product表中总记录条数 7
- SELECT COUNT(*) FROM product;
-
- -- 获取最高价格
- SELECT MAX(price) FROM product;
- -- 获取最高价格的商品名称
- SELECT NAME,price FROM product WHERE price = (SELECT MAX(price) FROM product);
-
- -- 获取最低库存
- SELECT MIN(stock) FROM product;
- -- 获取最低库存的商品名称
- SELECT NAME,stock FROM product WHERE stock = (SELECT MIN(stock) FROM product);
-
- -- 获取总库存数量
- SELECT SUM(stock) FROM product;
- -- 获取品牌为小米的平均商品价格
- SELECT AVG(price) FROM product WHERE brand='小米';
- ```
+推荐阅读:https://mp.weixin.qq.com/s/YZ-LckObephrP1f15mzHpA
-##### 文本处理
-| 函数名 | 功能 |
-| --------- | ------------------ |
-| LEFT() | 左边的字符 |
-| RIGHT() | 右边的字符 |
-| LOWER() | 转换为小写字符 |
-| UPPER() | 转换为大写字符 |
-| LTRIM() | 去除左边的空格 |
-| RTRIM() | 去除右边的空格 |
-| LENGTH() | 长度去除右边的空格 |
-| SOUNDEX() | 转换为语音值 |
+***
+### 终止流程
-***
+#### 终止语句
+终止线程中正在执行的语句:
+```mysql
+KILL QUERY thread_id
+```
-#### 排序查询
+KILL 不是马上终止的意思,而是告诉执行线程这条语句已经不需要继续执行,可以开始执行停止的逻辑(类似于打断)。因为对表做增删改查操作,会在表上加 MDL 读锁,如果线程被 KILL 时就直接终止,那这个 MDL 读锁就没机会被释放了
-* 排序查询语法
+命令 `KILL QUERYthread_id_A` 的执行流程:
- ```mysql
- SELECT 列名 FROM 表名 [WHERE 条件] ORDER BY 列名1 排序方式1,列名2 排序方式2;
- ```
+* 把 session A 的运行状态改成 THD::KILL_QUERY(将变量 killed 赋值为 THD::KILL_QUERY)
+* 给 session A 的执行线程发一个信号,让 session A 来处理这个 THD::KILL_QUERY 状态
-* 排序方式
+会话处于等待状态(锁阻塞),必须满足是一个可以被唤醒的等待,必须有机会去**判断线程的状态**,如果不满足就会造成 KILL 失败
- ```mysql
- ASC:升序
- DESC:降序
- ```
+典型场景:innodb_thread_concurrency 为 2,代表并发线程上限数设置为 2
- 注意:多个排序条件,当前边的条件值一样时,才会判断第二条件
+* session A 执行事务,session B 执行事务,达到线程上限;此时 session C 执行事务会阻塞等待,session D 执行 kill query C 无效
+* C 的逻辑是每 10 毫秒判断是否可以进入 InnoDB 执行,如果不行就调用 nanosleep 函数进入 sleep 状态,没有去判断线程状态
-* 例如
+补充:执行 Ctrl+C 的时候,是 MySQL 客户端另外启动一个连接,然后发送一个 KILL QUERY 命令
- ```mysql
- -- 按照库存升序排序
- SELECT * FROM product ORDER BY stock ASC;
-
- -- 查询名称中包含手机的商品信息。按照金额降序排序
- SELECT * FROM product WHERE NAME LIKE '%手机%' ORDER BY price DESC;
-
- -- 按照金额升序排序,如果金额相同,按照库存降序排列
- SELECT * FROM product ORDER BY price ASC,stock DESC;
- ```
-
***
-#### 分组查询
+#### 终止连接
-* 分组查询语法
+断开线程的连接:
- ````mysql
- SELECT 列名 FROM 表名 [WHERE 条件] GROUP BY 分组列名 [HAVING 分组后条件过滤] [ORDER BY 排序列名 排序方式];
- ````
+```mysql
+KILL CONNECTION id
+```
- WHERE 过滤行,HAVING 过滤分组,行过滤应当先于分组过滤
+断开连接后执行 SHOW PROCESSLIST 命令,如果这条语句的 Command 列显示 Killed,代表线程的状态是 KILL_CONNECTION,说明这个线程有语句正在执行,当前状态是停止语句执行中,终止逻辑耗时较长
- 分组规定:
+* 超大事务执行期间被 KILL,这时回滚操作需要对事务执行期间生成的所有新数据版本做回收操作,耗时很长
+* 大查询回滚,如果查询过程中生成了比较大的临时文件,删除临时文件可能需要等待 IO 资源,导致耗时较长
+* DDL 命令执行到最后阶段被 KILL,需要删除中间过程的临时文件,也可能受 IO 资源影响耗时较久
- * GROUP BY 子句出现在 WHERE 子句之后,ORDER BY 子句之前
- * NULL 的行会单独分为一组
- * 大多数 SQL 实现不支持 GROUP BY 列具有可变长度的数据类型
+总结:KILL CONNECTION 本质上只是把客户端的 SQL 连接断开,后面的终止流程还是要走 KILL QUERY
-* 例如
+一个事务被 KILL 之后,持续处于回滚状态,不应该强行重启整个 MySQL 进程,应该等待事务自己执行完成,因为重启后依然继续做回滚操作的逻辑
- ```mysql
- -- 按照品牌分组,获取每组商品的总金额
- SELECT brand,SUM(price) FROM product GROUP BY brand;
-
- -- 对金额大于4000元的商品,按照品牌分组,获取每组商品的总金额
- SELECT brand,SUM(price) FROM product WHERE price > 4000 GROUP BY brand;
-
- -- 对金额大于4000元的商品,按照品牌分组,获取每组商品的总金额,只显示总金额大于7000元的
- SELECT brand,SUM(price) AS getSum FROM product WHERE price > 4000 GROUP BY brand HAVING getSum > 7000;
-
- -- 对金额大于4000元的商品,按照品牌分组,获取每组商品的总金额,只显示总金额大于7000元的、并按照总金额的降序排列
- SELECT brand,SUM(price) AS getSum FROM product WHERE price > 4000 GROUP BY brand HAVING getSum > 7000 ORDER BY getSum DESC;
- ```
@@ -817,413 +567,11309 @@ LIMIT:分页限定
-#### 分页查询
+### 常用工具
-* 分页查询语法
+#### mysql
- ```mysql
- SELECT 列名 FROM 表名 [WHERE 条件] GROUP BY 分组列名 [HAVING 分组后条件过滤] [ORDER BY 排序列名 排序方式] LIMIT 开始索引,查询条数;
- ```
+mysql 不是指 mysql 服务,而是指 mysql 的客户端工具
-* 公式:开始索引 = (当前页码-1) * 每页显示的条数
+```sh
+mysql [options] [database]
+```
-* 例如
+* -u --user=name:指定用户名
+* -p --password[=name]:指定密码
+* -h --host=name:指定服务器IP或域名
+* -P --port=#:指定连接端口
+* -e --execute=name:执行SQL语句并退出,在控制台执行SQL语句,而不用连接到数据库执行
- ```mysql
- SELECT * FROM product LIMIT 0,2; -- 第一页 开始索引=(1-1) * 2
- SELECT * FROM product LIMIT 2,2; -- 第二页 开始索引=(2-1) * 2
- SELECT * FROM product LIMIT 4,2; -- 第三页 开始索引=(3-1) * 2
- SELECT * FROM product LIMIT 6,2; -- 第四页 开始索引=(4-1) * 2
- ```
+示例:
- 
+```sh
+mysql -h 127.0.0.1 -P 3306 -u root -p
+mysql -uroot -p2143 db01 -e "select * from tb_book";
+```
+***
-***
+#### admin
+mysqladmin 是一个执行管理操作的客户端程序,用来检查服务器的配置和当前状态、创建并删除数据库等
+通过 `mysqladmin --help` 指令查看帮助文档
-## 约束
+```sh
+mysqladmin -uroot -p2143 create 'test01';
+```
-### 概念
-约束:对表中的数据进行限定,保证数据的正确性、有效性、完整性!
-约束的分类:
+***
-| 约束 | 说明 |
-| ----------------------------- | -------------- |
-| PRIMARY KEY | 主键约束 |
-| PRIMARY KEY AUTO_INCREMENT | 主键、自动增长 |
-| UNIQUE | 唯一约束 |
-| NOT NULL | 非空约束 |
-| FOREIGN KEY | 外键约束 |
-| FOREIGN KEY ON UPDATE CASCADE | 外键级联更新 |
-| FOREIGN KEY ON DELETE CASCADE | 外键级联删除 |
+#### binlog
-***
+服务器生成的日志文件以二进制格式保存,如果需要检查这些文本,就要使用 mysqlbinlog 日志管理工具
+```sh
+mysqlbinlog [options] log-files1 log-files2 ...
+```
+* -d --database=name:指定数据库名称,只列出指定的数据库相关操作
-### 主键约束
+* -o --offset=#:忽略掉日志中的前 n 行命令。
-* 主键约束特点:
+* -r --result-file=name:将输出的文本格式日志输出到指定文件。
- * 主键约束默认包含**非空和唯一**两个功能
- * 一张表只能有一个主键
- * 主键一般用于表中数据的唯一标识
-
-* 建表时添加主键约束
-
- ```mysql
- CREATE TABLE 表名(
- 列名 数据类型 PRIMARY KEY,
- 列名 数据类型,
- ...
- );
- ```
+* -s --short-form:显示简单格式,省略掉一些信息。
-* 删除主键约束
+* --start-datatime=date1 --stop-datetime=date2:指定日期间隔内的所有日志
- ```mysql
- ALTER TABLE 表名 DROP PRIMARY KEY;
- ```
+* --start-position=pos1 --stop-position=pos2:指定位置间隔内的所有日志
-* 建表后单独添加主键约束
- ```mysql
- ALTER TABLE 表名 MODIFY 列名 数据类型 PRIMARY KEY;
- ```
-* 例如
+***
- ```mysql
- -- 创建student表
- CREATE TABLE student(
- id INT PRIMARY KEY -- 给id添加主键约束
- );
-
- -- 添加数据
- INSERT INTO student VALUES (1),(2);
- -- 主键默认唯一,添加重复数据,会报错
- INSERT INTO student VALUES (2);
- -- 主键默认非空,不能添加null的数据
- INSERT INTO student VALUES (NULL);
- ```
+#### dump
-***
+##### 命令介绍
+mysqldump 客户端工具用来备份数据库或在不同数据库之间进行数据迁移,备份内容包含创建表,及插入表的 SQL 语句
+```sh
+mysqldump [options] db_name [tables]
+mysqldump [options] --database/-B db1 [db2 db3...]
+mysqldump [options] --all-databases/-A
+```
-### 主键自增约束
+连接选项:
-主键自增约束可以为空,并自动增长。删除某条数据不影响自增的下一个数值,依然按照前一个值自增。
+* -u --user=name:指定用户名
+* -p --password[=name]:指定密码
+* -h --host=name:指定服务器 IP 或域名
+* -P --port=#:指定连接端口
-* 建表时添加主键自增约束
+输出内容选项:
- ```mysql
- CREATE TABLE 表名(
- 列名 数据类型 PRIMARY KEY AUTO_INCREMENT,
- 列名 数据类型,
- ...
- );
- ```
+* --add-drop-database:在每个数据库创建语句前加上 Drop database 语句
+* --add-drop-table:在每个表创建语句前加上 Drop table 语句 , 默认开启,不开启 (--skip-add-drop-table)
+* -n --no-create-db:不包含数据库的创建语句
+* -t --no-create-info:不包含数据表的创建语句
+* -d --no-data:不包含数据
+* -T, --tab=name:自动生成两个文件:一个 .sql 文件,创建表结构的语句;一个 .txt 文件,数据文件,相当于 select into outfile
-* 删除主键自增约束
+示例:
- ```mysql
- ALTER TABLE 表名 MODIFY 列名 数据类型;
- ```
+```sh
+mysqldump -uroot -p2143 db01 tb_book --add-drop-database --add-drop-table > a
+mysqldump -uroot -p2143 -T /tmp test city
+```
-* 建表后单独添加主键自增约束
- ```mysql
- ALTER TABLE 表名 MODIFY 列名 数据类型 AUTO_INCREMENT;
- ```
-* 例如
+***
- ```mysql
- -- 创建student2表
- CREATE TABLE student2(
- id INT PRIMARY KEY AUTO_INCREMENT -- 给id添加主键自增约束
- );
-
- -- 添加数据
- INSERT INTO student2 VALUES (1),(2);
- -- 添加null值,会自动增长
- INSERT INTO student2 VALUES (NULL),(NULL);-- 3,4
- ```
+##### 数据备份
-***
+命令行方式:
+* 备份命令:mysqldump -u root -p 数据库名称 > 文件保存路径
+* 恢复
+ 1. 登录MySQL数据库:`mysql -u root p`
+ 2. 删除已经备份的数据库
+ 3. 重新创建与备份数据库名称相同的数据库
+ 4. 使用该数据库
+ 5. 导入文件执行:`source 备份文件全路径`
+更多方式参考:https://time.geekbang.org/column/article/81925
-### 唯一约束
+图形化界面:
-唯一约束:约束不能有重复的数据
+* 备份
-* 建表时添加唯一约束
+ 
- ```mysql
- CREATE TABLE 表名(
- 列名 数据类型 UNIQUE,
- 列名 数据类型,
- ...
- );
- ```
+* 恢复
-* 删除唯一约束
+ 
- ```mysql
- ALTER TABLE 表名 DROP INDEX 列名;
- ```
-* 建表后单独添加唯一约束
- ```mysql
- ALTER TABLE 表名 MODIFY 列名 数据类型 UNIQUE;
- ```
-
***
-### 非空约束
+#### import
-* 建表时添加非空约束
+mysqlimport 是客户端数据导入工具,用来导入mysqldump 加 -T 参数后导出的文本文件
- ```mysql
- CREATE TABLE 表名(
- 列名 数据类型 NOT NULL,
- 列名 数据类型,
- ...
- );
- ```
+```sh
+mysqlimport [options] db_name textfile1 [textfile2...]
+```
-* 删除非空约束
+示例:
- ```mysql
- ALTER TABLE 表名 MODIFY 列名 数据类型;
- ```
+```sh
+mysqlimport -uroot -p2143 test /tmp/city.txt
+```
-* 建表后单独添加非空约束
+导入 sql 文件,可以使用 MySQL 中的 source 指令 :
- ```mysql
- ALTER TABLE 表名 MODIFY 列名 数据类型 NOT NULL;
- ```
+```mysql
+source 文件全路径
+```
-
-***
+***
-### 外键约束
- 外键约束:让表和表之间产生关系,从而保证数据的准确性!
+#### show
-* 建表时添加外键约束
+mysqlshow 客户端对象查找工具,用来很快地查找存在哪些数据库、数据库中的表、表中的列或者索引
- ```mysql
- CREATE TABLE 表名(
- 列名 数据类型 约束,
- ...
- CONSTRAINT 外键名 FOREIGN KEY (本表外键列名) REFERENCES 主表名(主表主键列名)
- );
- ```
+```sh
+mysqlshow [options] [db_name [table_name [col_name]]]
+```
-* 删除外键约束
+* --count:显示数据库及表的统计信息(数据库,表 均可以不指定)
- ```mysql
- ALTER TABLE 表名 DROP FOREIGN KEY 外键名;
- ```
+* -i:显示指定数据库或者指定表的状态信息
-* 建表后单独添加外键约束
+示例:
- ```mysql
- ALTER TABLE 表名 ADD CONSTRAINT 外键名 FOREIGN KEY (本表外键列名) REFERENCES 主表名(主表主键列名);
- ```
+```sh
+#查询每个数据库的表的数量及表中记录的数量
+mysqlshow -uroot -p1234 --count
+#查询test库中每个表中的字段书,及行数
+mysqlshow -uroot -p1234 test --count
+#查询test库中book表的详细情况
+mysqlshow -uroot -p1234 test book --count
+```
-* 例如
- ```mysql
- -- 创建user用户表
- CREATE TABLE USER(
- id INT PRIMARY KEY AUTO_INCREMENT, -- id
- name VARCHAR(20) NOT NULL -- 姓名
- );
- -- 添加用户数据
- INSERT INTO USER VALUES (NULL,'张三'),(NULL,'李四'),(NULL,'王五');
-
- -- 创建orderlist订单表
- CREATE TABLE orderlist(
- id INT PRIMARY KEY AUTO_INCREMENT, -- id
- number VARCHAR(20) NOT NULL, -- 订单编号
- uid INT, -- 订单所属用户
- CONSTRAINT ou_fk1 FOREIGN KEY (uid) REFERENCES USER(id) -- 添加外键约束
- );
- -- 添加订单数据
- INSERT INTO orderlist VALUES (NULL,'hm001',1),(NULL,'hm002',1),
- (NULL,'hm003',2),(NULL,'hm004',2),
- (NULL,'hm005',3),(NULL,'hm006',3);
-
- -- 添加一个订单,但是没有所属用户。无法添加
- INSERT INTO orderlist VALUES (NULL,'hm007',8);
- -- 删除王五这个用户,但是订单表中王五还有很多个订单呢。无法删除
- DELETE FROM USER WHERE NAME='王五';
- ```
-
-### 外键级联操作
-级联操作:当把主表中的数据进行删除或更新时,从表中有关联的数据也会随之删除(一般不建议使用)
-* 添加级联更新
- ```mysql
- ALTER TABLE 表名 ADD CONSTRAINT 外键名 FOREIGN KEY (本表外键列名) REFERENCES 主表名(主表主键列名) ON UPDATE CASCADE;
- ```
+****
-* 添加级联删除
- ```mysql
- ALTER TABLE 表名 ADD CONSTRAINT 外键名 FOREIGN KEY (本表外键列名) REFERENCES 主表名(主表主键列名) ON DELETE CASCADE;
- ```
-* 同时添加级联更新和级联删除
- ```mysql
- ALTER TABLE 表名 ADD CONSTRAINT 外键名 FOREIGN KEY (本表外键列名) REFERENCES 主表名(主表主键列名) ON UPDATE CASCADE ON DELETE CASCADE;
- ```
+## 单表操作
+### SQL
+- SQL
+ - Structured Query Language:结构化查询语言
+ - 定义了操作所有关系型数据库的规则,每种数据库操作的方式可能会存在不一样的地方,称为“方言”
-***
+- SQL 通用语法
+ - SQL 语句可以单行或多行书写,以**分号结尾**。
+ - 可使用空格和缩进来增强语句的可读性。
+ - MySQL 数据库的 SQL 语句不区分大小写,**关键字建议使用大写**。
+ - 数据库的注释:
+ - 单行注释:-- 注释内容 #注释内容(MySQL 特有)
+ - 多行注释:/* 注释内容 */
+- SQL 分类
-## 多表
+ - DDL(Data Definition Language)数据定义语言
-### 多表设计
+ - 用来定义数据库对象:数据库,表,列等。关键字:create、drop,、alter 等
-#### 多表介绍
+ - DML(Data Manipulation Language)数据操作语言
-多表:有多张数据表,而表与表之间有一定的关联关系,通过外键约束实现
+ - 用来对数据库中表的数据进行增删改。关键字:insert、delete、update 等
-多表分类:一对一、一对多、多对多
+ - DQL(Data Query Language)数据查询语言
-#### 一对一
+ - 用来查询数据库中表的记录(数据)。关键字:select、where 等
-举例:人和身份证
+ - DCL(Data Control Language)数据控制语言
-实现原则:在任意一个表建立外键,去关联另外一个表的主键
+ - 用来定义数据库的访问权限和安全级别,及创建用户。关键字:grant, revoke等
-```mysql
--- 创建person表
-CREATE TABLE person(
- id INT PRIMARY KEY AUTO_INCREMENT, -- 主键id
- NAME VARCHAR(20) -- 姓名
-);
--- 添加数据
-INSERT INTO person VALUES (NULL,'张三'),(NULL,'李四');
+ 
--- 创建card表
-CREATE TABLE card(
- id INT PRIMARY KEY AUTO_INCREMENT, -- 主键id
- number VARCHAR(20) UNIQUE NOT NULL, -- 身份证号
- pid INT UNIQUE, -- 外键列
- CONSTRAINT cp_fk1 FOREIGN KEY (pid) REFERENCES person(id)
-);
--- 添加数据
-INSERT INTO card VALUES (NULL,'12345',1),(NULL,'56789',2);
-```
-
+***
-#### 一对多
-举例:用户和订单、商品分类和商品
+### DDL
-实现原则:在多的一方,建立外键约束,来关联一的一方主键
+#### 数据库
-```mysql
--- 创建user表
-CREATE TABLE USER(
- id INT PRIMARY KEY AUTO_INCREMENT, -- 主键id
- NAME VARCHAR(20) -- 姓名
-);
--- 添加数据
-INSERT INTO USER VALUES (NULL,'张三'),(NULL,'李四');
+* R(Retrieve):查询
--- 创建orderlist表
-CREATE TABLE orderlist(
- id INT PRIMARY KEY AUTO_INCREMENT, -- 主键id
- number VARCHAR(20), -- 订单编号
- uid INT, -- 外键列
- CONSTRAINT ou_fk1 FOREIGN KEY (uid) REFERENCES USER(id)
-);
--- 添加数据
-INSERT INTO orderlist VALUES (NULL,'hm001',1),(NULL,'hm002',1),(NULL,'hm003',2),(NULL,'hm004',2);
-```
+ * 查询所有数据库:
-
+ ```mysql
+ SHOW DATABASES;
+ ```
+ * 查询某个数据库的创建语句
+ ```sql
+ SHOW CREATE DATABASE 数据库名称; -- 标准语法
+
+ SHOW CREATE DATABASE mysql; -- 查看mysql数据库的创建格式
+ ```
-#### 多对多
+
-举例:学生和课程。一个学生可以选择多个课程,一个课程也可以被多个学生选择
+* C(Create):创建
-实现原则:借助第三张表中间表,中间表至少包含两个列,这两个列作为中间表的外键,分别关联两张表的主键
+ * 创建数据库
-```mysql
+ ```mysql
+ CREATE DATABASE 数据库名称;-- 标准语法
+
+ CREATE DATABASE db1; -- 创建db1数据库
+ ```
+
+ * 创建数据库(判断,如果不存在则创建)
+
+ ```mysql
+ CREATE DATABASE IF NOT EXISTS 数据库名称;
+ ```
+
+ * 创建数据库,并指定字符集
+
+ ```mysql
+ CREATE DATABASE 数据库名称 CHARACTER SET 字符集名称;
+ ```
+
+ * 例如:创建db4数据库、如果不存在则创建,指定字符集为gbk
+
+ ```mysql
+ -- 创建db4数据库、如果不存在则创建,指定字符集为gbk
+ CREATE DATABASE IF NOT EXISTS db4 CHARACTER SET gbk;
+
+ -- 查看db4数据库的字符集
+ SHOW CREATE DATABASE db4;
+ ```
+
+
+
+* U(Update):修改
+
+ * 修改数据库的字符集
+
+ ```mysql
+ ALTER DATABASE 数据库名称 CHARACTER SET 字符集名称;
+ ```
+
+ * 常用字符集:
+
+ ```mysql
+ --查询所有支持的字符集
+ SHOW CHARSET;
+ --查看所有支持的校对规则
+ SHOW COLLATION;
+
+ -- 字符集: utf8,latinI,GBK,,GBK是utf8的子集
+ -- 校对规则: ci 大小定不敏感,cs或bin大小写敏感
+ ```
+
+
+
+* D(Delete):删除
+
+ * 删除数据库:
+
+ ```mysql
+ DROP DATABASE 数据库名称;
+ ```
+
+ * 删除数据库(判断,如果存在则删除):
+
+ ```mysql
+ DROP DATABASE IF EXISTS 数据库名称;
+ ```
+
+
+
+* 使用数据库:
+
+ * 查询当前正在使用的数据库名称
+
+ ```mysql
+ SELECT DATABASE();
+ ```
+
+ * 使用数据库
+
+ ```mysql
+ USE 数据库名称; -- 标准语法
+ USE db4; -- 使用db4数据库
+ ```
+
+
+
+
+***
+
+
+
+#### 数据表
+
+- R(Retrieve):查询
+
+ - 查询数据库中所有的数据表
+
+ ```mysql
+ USE mysql;-- 使用mysql数据库
+
+ SHOW TABLES;-- 查询库中所有的表
+ ```
+
+ - 查询表结构
+
+ ```mysql
+ DESC 表名;
+ ```
+
+ - 查询表字符集
+
+ ```mysql
+ SHOW TABLE STATUS FROM 库名 LIKE '表名';
+ ```
+
+
+
+- C(Create):创建
+
+ - 创建数据表
+
+ ```mysql
+ CREATE TABLE 表名(
+ 列名1 数据类型1,
+ 列名2 数据类型2,
+ ....
+ 列名n 数据类型n
+ );
+ -- 注意:最后一列,不需要加逗号
+ ```
+
+ - 复制表
+
+ ```mysql
+ CREATE TABLE 表名 LIKE 被复制的表名; -- 标准语法
+
+ CREATE TABLE product2 LIKE product; -- 复制product表到product2表
+ ```
+
+ - 数据类型
+
+ | 数据类型 | 说明 |
+ | --------- | ------------------------------------------------------------ |
+ | INT | 整数类型 |
+ | DOUBLE | 小数类型 |
+ | DATE | 日期,只包含年月日:yyyy-MM-dd |
+ | DATETIME | 日期,包含年月日时分秒:yyyy-MM-dd HH:mm:ss |
+ | TIMESTAMP | 时间戳类型,包含年月日时分秒:yyyy-MM-dd HH:mm:ss
如果不给这个字段赋值或赋值为 NULL,则默认使用当前的系统时间 |
+ | CHAR | 字符串,定长类型 |
+ | VARCHAR | 字符串,**变长类型**
name varchar(20) 代表姓名最大 20 个字符:zhangsan 8 个字符,张三 2 个字符 |
+
+ `INT(n)`:n 代表位数
+
+ * 3:int(9)显示结果为 000000010
+ * 3:int(3)显示结果为 010
+
+ `varchar(n)`:n 表示的是字符数
+
+ - 例如:
+
+ ```mysql
+ -- 使用db3数据库
+ USE db3;
+
+ -- 创建一个product商品表
+ CREATE TABLE product(
+ id INT, -- 商品编号
+ NAME VARCHAR(30), -- 商品名称
+ price DOUBLE, -- 商品价格
+ stock INT, -- 商品库存
+ insert_time DATE -- 上架时间
+ );
+ ```
+
+
+
+- U(Update):修改
+
+ - 修改表名
+
+ ```mysql
+ ALTER TABLE 表名 RENAME TO 新的表名;
+ ```
+
+ - 修改表的字符集
+
+ ```mysql
+ ALTER TABLE 表名 CHARACTER SET 字符集名称;
+ ```
+
+ - 添加一列
+
+ ```mysql
+ ALTER TABLE 表名 ADD 列名 数据类型;
+ ```
+
+ - 修改列数据类型
+
+ ```mysql
+ ALTER TABLE 表名 MODIFY 列名 新数据类型;
+ ```
+
+ - 修改列名称和数据类型
+
+ ```mysql
+ ALTER TABLE 表名 CHANGE 列名 新列名 新数据类型;
+ ```
+
+ - 删除列
+
+ ```mysql
+ ALTER TABLE 表名 DROP 列名;
+ ```
+
+
+
+- D(Delete):删除
+
+ - 删除数据表
+
+ ```mysql
+ DROP TABLE 表名;
+ ```
+
+ - 删除数据表(判断,如果存在则删除)
+
+ ```mysql
+ DROP TABLE IF EXISTS 表名;
+ ```
+
+
+
+***
+
+
+
+### DML
+
+#### INSERT
+
+* 新增表数据
+
+ * 新增格式 1:给指定列添加数据
+
+ ```mysql
+ INSERT INTO 表名(列名1,列名2...) VALUES (值1,值2...);
+ ```
+
+ * 新增格式 2:默认给全部列添加数据
+
+ ```mysql
+ INSERT INTO 表名 VALUES (值1,值2,值3,...);
+ ```
+
+ * 新增格式 3:批量添加数据
+
+ ```mysql
+ -- 给指定列批量添加数据
+ INSERT INTO 表名(列名1,列名2,...) VALUES (值1,值2,...),(值1,值2,...)...;
+
+ -- 默认给所有列批量添加数据
+ INSERT INTO 表名 VALUES (值1,值2,值3,...),(值1,值2,值3,...)...;
+ ```
+
+* 字符串拼接
+
+ ```mysql
+ CONCAT(string1,string2,'',...)
+ ```
+
+
+
+* 注意事项
+
+ - 列名和值的数量以及数据类型要对应
+ - 除了数字类型,其他数据类型的数据都需要加引号(单引双引都可以,推荐单引)
+
+
+
+***
+
+
+
+#### UPDATE
+
+* 修改表数据语法
+
+ * 标准语法
+
+ ```mysql
+ UPDATE 表名 SET 列名1 = 值1,列名2 = 值2,... [where 条件];
+ ```
+
+ * 修改电视的价格为1800、库存为36
+
+ ```mysql
+ UPDATE product SET price=1800,stock=36 WHERE NAME='电视';
+ SELECT * FROM product;-- 查看所有商品信息
+ ```
+
+* 注意事项
+
+ - 修改语句中必须加条件
+ - 如果不加条件,则将所有数据都修改
+
+
+
+***
+
+
+
+#### DELETE
+
+* 删除表数据语法
+
+ ```mysql
+ DELETE FROM 表名 [WHERE 条件];
+ ```
+
+* 注意事项
+ * 删除语句中必须加条件
+ * 如果不加条件,则将所有数据删除
+
+
+
+
+
+***
+
+
+
+### DQL
+
+#### 查询语法
+
+数据库查询遵循条件在前的原则
+
+```mysql
+SELECT DISTINCT
+