Mybatis全局配置文件解析

MyBatis入门简述

1.创建MyBatis全局配置文件 – MyBatis 的全局配置文件包含了影响 MyBatis 行为甚深 的设置(settings)和属性(properties)信息、如数据 库连接池信息等。指导着MyBatis进行工作。我们可以 参照官方文件的配置示例。

2.创建SQL映射文件 – 映射文件的作用就相当于是定义Dao接口的实现类如何 工作。这也是我们使用MyBatis时编写的最多的文件。

待续

Mybatis快速入门

Test:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//1.读取配置文件
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
//2.创建SqlSessionFactory工厂
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.使用工厂生产SqlSession对象
SqlSession session = factory.openSession();
//4.使用SqlSession创建Dao接口的代理对象
IUserDao userDao = session.getMapper(IUserDao.class);
//5.使用代理对象执行方法
List<User> users = userDao.findAll();
for(User user : users){
System.out.println(user);
}
//6.释放资源
session.close();
in.close();

实体类User.java

1
2
3
4
5
private Integer id;
private String username;
private Date birthday;
private String sex;
private String address;

IUserDao.java

1
2
3
4
5
6
7
8
9
public interface IUserDao {

/**
* 查询所有操作
* @return
*/
List<User> findAll();
}

Mybati全局配置文件SqlMapConfig.xml

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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<!-- mybatis的主配置文件 -->
<configuration>
<!-- 配置环境 -->
<environments default="mysql">
<!-- 配置mysql的环境-->
<environment id="mysql">
<!-- 配置事务的类型-->
<transactionManager type="JDBC"></transactionManager>
<!-- 配置数据源(连接池) -->
<dataSource type="POOLED">
<!-- 配置连接数据库的4个基本信息 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ee50"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>

<!-- 指定映射配置文件的位置,映射配置文件指的是每个dao独立的配置文件 -->
<mappers>
<mapper resource="IUserDao.xml"/> [Xml位置] // 需路径相同
</mappers>
</configuration>

Mybati Sql配置文件IUserDao.xml

1
2
3
4
5
6
7
8
9
10
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.itheima.dao.IUserDao">
<!--配置查询所有-->
<select id="findAll" resultType="domain.User">
select * from user
</select>
</mapper>

Mybatis全局配置文件

MySQL课堂笔记总结

1.0 MySQL基础课堂笔记

  1. 数据库的基本概念

  2. MySQL数据库软件

    1. 安装

    2. 卸载

  3. 配置

  4. SQL

数据库的基本概念

1. 数据库的英文单词: DataBase 简称 : DB
2. 什么数据库?
    * 用于存储和管理数据的仓库。

3. 数据库的特点:
    1. 持久化存储数据的。其实数据库就是一个文件系统
    2. 方便存储和管理数据
    3. 使用了统一的方式操作数据库 -- SQL


​ 4. 常见的数据库软件
​ * 参见《MySQL基础.pdf》

MySQL数据库软件

1. 安装

* 略

2. 卸载

1. 去mysql的安装目录找到my.ini文件
    * 复制 datadir="C:/ProgramData/MySQL/MySQL Server 5.5/Data/"
2. 卸载MySQL
3. 删除C:/ProgramData目录下的MySQL文件夹。

3. 配置

* MySQL服务启动
    1. 手动。
    2. cmd--> services.msc 打开服务的窗口
    3. 使用管理员打开cmd
        * net start mysql : 启动mysql的服务
        * net stop mysql:关闭mysql服务
* MySQL登录
    1. mysql -uroot -p密码
    2. mysql -hip -uroot -p连接目标的密码
    3. mysql --host=ip --user=root --password=连接目标的密码
* MySQL退出
    1. exit
    2. quit

* MySQL目录结构
    1. MySQL安装目录:basedir="D:/develop/MySQL/"
        * 配置文件 my.ini
    2. MySQL数据目录:datadir="C:/ProgramData/MySQL/MySQL Server 5.5/Data/"
        * 几个概念
            * 数据库:文件夹
            * 表:文件
            * 数据:数据

SQL

1.什么是SQL?

Structured Query Language:结构化查询语言
其实就是定义了操作所有关系型数据库的规则。每一种数据库操作的方式存在不一样的地方,称为“方言”。

2.SQL通用语法

1. SQL 语句可以单行或多行书写,以分号结尾。
2. 可使用空格和缩进来增强语句的可读性。
3. MySQL 数据库的 SQL 语句不区分大小写,关键字建议使用大写。
4. 3 种注释
    * 单行注释: -- 注释内容 或 # 注释内容(mysql 特有) 
    * 多行注释: /* 注释 */

3. SQL分类

1) DDL(Data Definition Language)数据定义语言
    用来定义数据库对象:数据库,表,列等。关键字:create, drop,alter 等
2) DML(Data Manipulation Language)数据操作语言
    用来对数据库中表的数据进行增删改。关键字:insert, delete, update 等
3) DQL(Data Query Language)数据查询语言
    用来查询数据库中表的记录(数据)。关键字:select, where 等
4) DCL(Data Control Language)数据控制语言(了解)
    用来定义数据库的访问权限和安全级别,及创建用户。关键字:GRANT, REVOKE 等

DDL:操作数据库、表

1. 操作数据库:CRUD

1. C(Create):创建

* 创建数据库:
    * create database 数据库名称;
* 创建数据库,判断不存在,再创建:
    * create database if not exists 数据库名称;
* 创建数据库,并指定字符集
    * create database 数据库名称 character set 字符集名;

* 练习: 创建db4数据库,判断是否存在,并制定字符集为gbk
    * create database if not exists db4 character set gbk;

2. R(Retrieve):查询

* 查询所有数据库的名称:
    * show databases;
* 查询某个数据库的字符集:查询某个数据库的创建语句
    * show create database 数据库名称;

3. U(Update):修改

* 修改数据库的字符集
    * alter database 数据库名称 character set 字符集名称;

4. D(Delete):删除

* 删除数据库
    * drop database 数据库名称;
* 判断数据库存在,存在再删除
    * drop database if exists 数据库名称;

5. 使用数据库

* 查询当前正在使用的数据库名称
    * select database();
* 使用数据库
    * use 数据库名称;

2. 操作表

1. C(Create):创建

1. 语法:
    create table 表名(
        列名1 数据类型1,
        列名2 数据类型2,
        ....
        列名n 数据类型n
    );
    * 注意:最后一列,不需要加逗号(,)
    * 数据库类型:
        1. int:整数类型
            * age int,
        2. double:小数类型
            * score double(5,2)
        3. date:日期,只包含年月日,yyyy-MM-dd
        4. datetime:日期,包含年月日时分秒     yyyy-MM-dd HH:mm:ss
        5. timestamp:时间错类型    包含年月日时分秒     yyyy-MM-dd HH:mm:ss    
            * 如果将来不给这个字段赋值,或赋值为null,则默认使用当前的系统时间,来自动赋值

        6. varchar:字符串
            * name varchar(20):姓名最大20个字符
            * zhangsan 8个字符  张三 2个字符


* 创建表
    create table student(
        id int,
        name varchar(32),
        age int ,
        score double(4,1),
        birthday date,
        insert_time timestamp
    );
* 复制表:
    * create table 表名 like 被复制的表名;          

2. R(Retrieve):查询

* 查询某个数据库中所有的表名称
    * show tables;
* 查询表结构
    * desc 表名;

3. U(Update):修改

1. 修改表名
    alter table 表名 rename to 新的表名;
2. 修改表的字符集
    alter table 表名 character set 字符集名称;
3. 添加一列
    alter table 表名 add 列名 数据类型;
4. 修改列名称 类型
    alter table 表名 change 列名 新列别 新数据类型;
    alter table 表名 modify 列名 新数据类型;
5. 删除列
    alter table 表名 drop 列名;

4. D(Delete):删除

* drop table 表名;
* drop table  if exists 表名 ;
  • 客户端图形化工具:SQLYog

DML:增删改表中数据

1. 添加数据:

* 语法:
    * insert into 表名(列名1,列名2,...列名n) values(值1,值2,...值n);
* 注意:
    1. 列名和值要一一对应。
    2. 如果表名后,不定义列名,则默认给所有列添加值
        insert into 表名 values(值1,值2,...值n);
    3. 除了数字类型,其他类型需要使用引号(单双都可以)引起来

2. 删除数据:

* 语法:
    * delete from 表名 [where 条件]
* 注意:
    1. 如果不加条件,则删除表中所有记录。
    2. 如果要删除所有记录
        1. delete from 表名; -- 不推荐使用。有多少条记录就会执行多少次删除操作
        2. TRUNCATE TABLE 表名; -- 推荐使用,效率更高 先删除表,然后再创建一张一样的表。

3. 修改数据:

* 语法:
    * update 表名 set 列名1 = 值1, 列名2 = 值2,... [where 条件];

* 注意:
    1. 如果不加任何条件,则会将表中所有记录全部修改。

DQL:查询表中的记录

* select * from 表名;

1. 语法:

select
    字段列表
from
    表名列表
where
    条件列表
group by
    分组字段
having
    分组之后的条件
order by
    排序
limit
    分页限定

2. 基础查询

1. 多个字段的查询
    select 字段名1,字段名2... from 表名;
    * 注意:
        * 如果查询所有字段,则可以使用*来替代字段列表。
2. 去除重复:
    * distinct
3. 计算列
    * 一般可以使用四则运算计算一些列的值。(一般只会进行数值型的计算)
    * ifnull(表达式1,表达式2):null参与的运算,计算结果都为null
        * 表达式1:哪个字段需要判断是否为null
        * 如果该字段为null后的替换值。
4. 起别名:
    * as:as也可以省略

3. 条件查询

1. where子句后跟条件
2. 运算符
    * > 、< 、<= 、>= 、= 、<>
    * BETWEEN...AND  
    * IN( 集合) 
    * LIKE:模糊查询
        * 占位符:
            * _:单个任意字符
            * %:多个任意字符
    * IS NULL  
    * and  或 &&
    * or  或 || 
    * not  或 !

        -- 查询年龄大于20岁

        SELECT * FROM student WHERE age > 20;

        SELECT * FROM student WHERE age >= 20;

        -- 查询年龄等于20岁
        SELECT * FROM student WHERE age = 20;

        -- 查询年龄不等于20岁
        SELECT * FROM student WHERE age != 20;
        SELECT * FROM student WHERE age <> 20;

        -- 查询年龄大于等于20 小于等于30

        SELECT * FROM student WHERE age >= 20 &&  age <=30;
        SELECT * FROM student WHERE age >= 20 AND  age <=30;
        SELECT * FROM student WHERE age BETWEEN 20 AND 30;

        -- 查询年龄22岁,18岁,25岁的信息
        SELECT * FROM student WHERE age = 22 OR age = 18 OR age = 25
        SELECT * FROM student WHERE age IN (22,18,25);

        -- 查询英语成绩为null
        SELECT * FROM student WHERE english = NULL; -- 不对的。null值不能使用 = (!=) 判断

        SELECT * FROM student WHERE english IS NULL;

        -- 查询英语成绩不为null
        SELECT * FROM student WHERE english  IS NOT NULL;



        -- 查询姓马的有哪些? like
        SELECT * FROM student WHERE NAME LIKE '马%';
        -- 查询姓名第二个字是化的人

        SELECT * FROM student WHERE NAME LIKE "_化%";

        -- 查询姓名是3个字的人
        SELECT * FROM student WHERE NAME LIKE '___';


​ – 查询姓名中包含德的人
​ SELECT * FROM student WHERE NAME LIKE ‘%德%’;

2.0 MySQL约束课堂笔记

1. DQL:查询语句
    1. 排序查询
    2. 聚合函数
    3. 分组查询
    4. 分页查询

2. 约束
3. 多表之间的关系
4. 范式
5. 数据库的备份和还原

DQL:查询语句

1. 排序查询

* 语法:order by 子句
    * order by 排序字段1 排序方式1 ,  排序字段2 排序方式2...

* 排序方式:
    * ASC:升序,默认的。
    * DESC:降序。

* 注意:
    * 如果有多个排序条件,则当前边的条件值一样时,才会判断第二条件。

2. 聚合函数:将一列数据作为一个整体,进行纵向的计算。

1. count:计算个数
    1. 一般选择非空的列:主键
    2. count(*)
2. max:计算最大值
3. min:计算最小值
4. sum:计算和
5. avg:计算平均值


* 注意:聚合函数的计算,排除null值。
    解决方案:
        1. 选择不包含非空的列进行计算
        2. IFNULL函数

3. 分组查询:

1. 语法:group by 分组字段;
2. 注意:
    1. 分组之后查询的字段:分组字段、聚合函数
    2. where 和 having 的区别?
        1. where 在分组之前进行限定,如果不满足条件,则不参与分组。having在分组之后进行限定,如果不满足结果,则不会被查询出来
        2. where 后不可以跟聚合函数,having可以进行聚合函数的判断。

    -- 按照性别分组。分别查询男、女同学的平均分

    SELECT sex , AVG(math) FROM student GROUP BY sex;

    -- 按照性别分组。分别查询男、女同学的平均分,人数

    SELECT sex , AVG(math),COUNT(id) FROM student GROUP BY sex;

    --  按照性别分组。分别查询男、女同学的平均分,人数 要求:分数低于70分的人,不参与分组
    SELECT sex , AVG(math),COUNT(id) FROM student WHERE math > 70 GROUP BY sex;

    --  按照性别分组。分别查询男、女同学的平均分,人数 要求:分数低于70分的人,不参与分组,分组之后。人数要大于2个人
    SELECT sex , AVG(math),COUNT(id) FROM student WHERE math > 70 GROUP BY sex HAVING COUNT(id) > 2;

    SELECT sex , AVG(math),COUNT(id) 人数 FROM student WHERE math > 70 GROUP BY sex HAVING 人数 > 2;

4. 分页查询

1. 语法:limit 开始的索引,每页查询的条数;
    2. 公式:开始的索引 = (当前的页码 - 1) * 每页显示的条数
        -- 每页显示3条记录 

        SELECT * FROM student LIMIT 0,3; -- 第1页

        SELECT * FROM student LIMIT 3,3; -- 第2页

        SELECT * FROM student LIMIT 6,3; -- 第3页

    3. limit 是一个MySQL"方言"

约束

* 概念: 对表中的数据进行限定,保证数据的正确性、有效性和完整性。    
* 分类:
    1. 主键约束:primary key
    2. 非空约束:not null
    3. 唯一约束:unique
    4. 外键约束:foreign key

非空约束:not null,值不能为null

1. 创建表时添加约束
    CREATE TABLE stu(
        id INT,
        NAME VARCHAR(20) NOT NULL -- name为非空
    );
2. 创建表完后,添加非空约束
    ALTER TABLE stu MODIFY NAME VARCHAR(20) NOT NULL;

3. 删除name的非空约束
    ALTER TABLE stu MODIFY NAME VARCHAR(20);

唯一约束:unique,值不能重复

1. 创建表时,添加唯一约束
    CREATE TABLE stu(
        id INT,
        phone_number VARCHAR(20) UNIQUE -- 添加了唯一约束

    );
    * 注意mysql中,唯一约束限定的列的值可以有多个null


​ 2. 删除唯一约束

​ ALTER TABLE stu DROP INDEX phone_number;

​ 3. 在创建表后,添加唯一约束
​ ALTER TABLE stu MODIFY phone_number VARCHAR(20) UNIQUE;

主键约束:primary key。

1. 注意:
    1. 含义:非空且唯一
    2. 一张表只能有一个字段为主键
    3. 主键就是表中记录的唯一标识

2. 在创建表时,添加主键约束
    create table stu(
        id int primary key,-- 给id添加主键约束
        name varchar(20)
    );

3. 删除主键
    -- 错误 alter table stu modify id int ;
    ALTER TABLE stu DROP PRIMARY KEY;

4. 创建完表后,添加主键
    ALTER TABLE stu MODIFY id INT PRIMARY KEY;

5. 自动增长:
    1.  概念:如果某一列是数值类型的,使用 auto_increment 可以来完成值得自动增长

    2. 在创建表时,添加主键约束,并且完成主键自增长
    create table stu(
        id int primary key auto_increment,-- 给id添加主键约束
        name varchar(20)
    );


​ 3. 删除自动增长
​ ALTER TABLE stu MODIFY id INT;
​ 4. 添加自动增长
​ ALTER TABLE stu MODIFY id INT AUTO_INCREMENT;

外键约束:foreign key,让表于表产生关系,从而保证数据的正确性。

1. 在创建表时,可以添加外键
    * 语法:
        create table 表名(
            ....
            外键列
            constraint 外键名称 foreign key (外键列名称) references 主表名称(主表列名称)
        );

2. 删除外键
    ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;

3. 创建表之后,添加外键
    ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称);


​ 4. 级联操作
​ 1. 添加级联操作
​ 语法:ALTER TABLE 表名 ADD CONSTRAINT 外键名称
​ FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称) ON UPDATE CASCADE ON DELETE CASCADE ;
​ 2. 分类:
​ 1. 级联更新:ON UPDATE CASCADE
​ 2. 级联删除:ON DELETE CASCADE

数据库的设计

1. 多表之间的关系

1. 分类:
    1. 一对一(了解):
        * 如:人和身份证
        * 分析:一个人只有一个身份证,一个身份证只能对应一个人
    2. 一对多(多对一):
        * 如:部门和员工
        * 分析:一个部门有多个员工,一个员工只能对应一个部门
    3. 多对多:
        * 如:学生和课程
        * 分析:一个学生可以选择很多门课程,一个课程也可以被很多学生选择

2. 实现关系:

1. 一对多(多对一):
    * 如:部门和员工
    * 实现方式:在多的一方建立外键,指向一的一方的主键。
2. 多对多:
    * 如:学生和课程
    * 实现方式:多对多关系实现需要借助第三张中间表。中间表至少包含两个字段,这两个字段作为第三张表的外键,分别指向两张表的主键
3. 一对一(了解):
    * 如:人和身份证
    * 实现方式:一对一关系实现,可以在任意一方添加唯一外键指向另一方的主键。

3. 案例

-- 创建旅游线路分类表 tab_category
-- cid 旅游线路分类主键,自动增长
-- cname 旅游线路分类名称非空,唯一,字符串 100
CREATE TABLE tab_category (
    cid INT PRIMARY KEY AUTO_INCREMENT,
    cname VARCHAR(100) NOT NULL UNIQUE
);

-- 创建旅游线路表 tab_route
/*
rid 旅游线路主键,自动增长
rname 旅游线路名称非空,唯一,字符串 100
price 价格
rdate 上架时间,日期类型
cid 外键,所属分类
*/
CREATE TABLE tab_route(
    rid INT PRIMARY KEY AUTO_INCREMENT,
    rname VARCHAR(100) NOT NULL UNIQUE,
    price DOUBLE,
    rdate DATE,
    cid INT,
    FOREIGN KEY (cid) REFERENCES tab_category(cid)
);

/*创建用户表 tab_user
uid 用户主键,自增长
username 用户名长度 100,唯一,非空
password 密码长度 30,非空
name 真实姓名长度 100
birthday 生日
sex 性别,定长字符串 1
telephone 手机号,字符串 11
email 邮箱,字符串长度 100
*/
CREATE TABLE tab_user (
    uid INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(100) UNIQUE NOT NULL,
    PASSWORD VARCHAR(30) NOT NULL,
    NAME VARCHAR(100),
    birthday DATE,
    sex CHAR(1) DEFAULT '男',
    telephone VARCHAR(11),
    email VARCHAR(100)
);

/*
创建收藏表 tab_favorite
rid 旅游线路 id,外键
date 收藏时间
uid 用户 id,外键
rid 和 uid 不能重复,设置复合主键,同一个用户不能收藏同一个线路两次
*/
CREATE TABLE tab_favorite (
    rid INT, -- 线路id
    DATE DATETIME,
    uid INT, -- 用户id
    -- 创建复合主键
    PRIMARY KEY(rid,uid), -- 联合主键
    FOREIGN KEY (rid) REFERENCES tab_route(rid),
    FOREIGN KEY(uid) REFERENCES tab_user(uid)
);

数据库设计的范式

* 概念:设计数据库时,需要遵循的一些规范。要遵循后边的范式要求,必须先遵循前边的所有范式要求

    设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小。
    目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。

* 分类:
    1. 第一范式(1NF):每一列都是不可分割的原子数据项
    2. 第二范式(2NF):在1NF的基础上,非码属性必须完全依赖于码(在1NF基础上消除非主属性对主码的部分函数依赖)
        * 几个概念:
            1. 函数依赖:A-->B,如果通过A属性(属性组)的值,可以确定唯一B属性的值。则称B依赖于A
                例如:学号-->姓名。  (学号,课程名称) --> 分数
            2. 完全函数依赖:A-->B, 如果A是一个属性组,则B属性值得确定需要依赖于A属性组中所有的属性值。
                例如:(学号,课程名称) --> 分数
            3. 部分函数依赖:A-->B, 如果A是一个属性组,则B属性值得确定只需要依赖于A属性组中某一些值即可。
                例如:(学号,课程名称) -- > 姓名
            4. 传递函数依赖:A-->B, B -- >C . 如果通过A属性(属性组)的值,可以确定唯一B属性的值,在通过B属性(属性组)的值可以确定唯一C属性的值,则称 C 传递函数依赖于A
                例如:学号-->系名,系名-->系主任
            5. 码:如果在一张表中,一个属性或属性组,被其他所有属性所完全依赖,则称这个属性(属性组)为该表的码
                例如:该表中码为:(学号,课程名称)
                * 主属性:码属性组中的所有属性
                * 非主属性:除过码属性组的属性

    3. 第三范式(3NF):在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)

数据库的备份和还原

1. 命令行:
    * 语法:
        * 备份: mysqldump -u用户名 -p密码 数据库名称 > 保存的路径
        * 还原:
            1. 登录数据库
            2. 创建数据库
            3. 使用数据库
            4. 执行文件。source 文件路径
2. 图形化工具:略

3.0 MySQL多表&事务课堂笔记

1. 多表查询

2. 事务

3. DCL

多表查询

查询语法

select
    列名列表
from
    表名列表
where....

准备sql

    # 创建部门表
    CREATE TABLE dept(
        id INT PRIMARY KEY AUTO_INCREMENT,
        NAME VARCHAR(20)
    );
    INSERT INTO dept (NAME) VALUES ('开发部'),('市场部'),('财务部');
    # 创建员工表
    CREATE TABLE emp (
        id INT PRIMARY KEY AUTO_INCREMENT,
        NAME VARCHAR(10),
        gender CHAR(1), -- 性别
        salary DOUBLE, -- 工资
        join_date DATE, -- 入职日期
        dept_id INT,
        FOREIGN KEY (dept_id) REFERENCES dept(id) -- 外键,关联部门表(部门表的主键)
    );
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('孙悟空','男',7200,'2013-02-24',1);
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('猪八戒','男',3600,'2010-12-02',2);
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('唐僧','男',9000,'2008-08-08',2);
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('白骨精','女',5000,'2015-10-07',3);
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('蜘蛛精','女',4500,'2011-03-14',1);
* 笛卡尔积:
    * 有两个集合A,B .取这两个集合的所有组成情况。
    * 要完成多表查询,需要消除无用的数据

多表查询的分类

1. 内连接查询

1. 隐式内连接:使用where条件消除无用数据
    * 例子:
    -- 查询所有员工信息和对应的部门信息

    SELECT * FROM emp,dept WHERE emp.`dept_id` = dept.`id`;

    -- 查询员工表的名称,性别。部门表的名称
    SELECT emp.name,emp.gender,dept.name FROM emp,dept WHERE emp.`dept_id` = dept.`id`;

    SELECT 
        t1.name, -- 员工表的姓名
        t1.gender,-- 员工表的性别
        t2.name -- 部门表的名称
    FROM
        emp t1,
        dept t2
    WHERE 
        t1.`dept_id` = t2.`id`;


​ 2. 显式内连接:
​ * 语法: select 字段列表 from 表名1 [inner] join 表名2 on 条件
​ * 例如:
​ * SELECT * FROM emp INNER JOIN dept ON emp.dept_id = dept.id;
​ * SELECT * FROM emp JOIN dept ON emp.dept_id = dept.id;

​ 3. 内连接查询:
​ 1. 从哪些表中查询数据
​ 2. 条件是什么
​ 3. 查询哪些字段

2. 外链接查询:

1. 左外连接:
    * 语法:select 字段列表 from 表1 left [outer] join 表2 on 条件;
    * 查询的是左表所有数据以及其交集部分。
    * 例子:
        -- 查询所有员工信息,如果员工有部门,则查询部门名称,没有部门,则不显示部门名称
        SELECT     t1.*,t2.`name` FROM emp t1 LEFT JOIN dept t2 ON t1.`dept_id` = t2.`id`;
2. 右外连接:
    * 语法:select 字段列表 from 表1 right [outer] join 表2 on 条件;
    * 查询的是右表所有数据以及其交集部分。
    * 例子:
        SELECT     * FROM dept t2 RIGHT JOIN emp t1 ON t1.`dept_id` = t2.`id`;

子查询:

* 概念:查询中嵌套查询,称嵌套查询为子查询。
    -- 查询工资最高的员工信息
    -- 1 查询最高的工资是多少 9000
    SELECT MAX(salary) FROM emp;

    -- 2 查询员工信息,并且工资等于9000的
    SELECT * FROM emp WHERE emp.`salary` = 9000;

    -- 一条sql就完成这个操作。子查询
    SELECT * FROM emp WHERE emp.`salary` = (SELECT MAX(salary) FROM emp);

子查询不同情况

1. 子查询的结果是单行单列的:
    * 子查询可以作为条件,使用运算符去判断。 运算符: > >= < <= =
    * 
    -- 查询员工工资小于平均工资的人
    SELECT * FROM emp WHERE emp.salary < (SELECT AVG(salary) FROM emp);
2. 子查询的结果是多行单列的:
    * 子查询可以作为条件,使用运算符in来判断
    -- 查询'财务部'和'市场部'所有的员工信息
    SELECT id FROM dept WHERE NAME = '财务部' OR NAME = '市场部';
    SELECT * FROM emp WHERE dept_id = 3 OR dept_id = 2;
    -- 子查询
    SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE NAME = '财务部' OR NAME = '市场部');

3. 子查询的结果是多行多列的:
    * 子查询可以作为一张虚拟表参与查询
    -- 查询员工入职日期是2011-11-11日之后的员工信息和部门信息
    -- 子查询
    SELECT * FROM dept t1 ,(SELECT * FROM emp WHERE emp.`join_date` > '2011-11-11') t2
    WHERE t1.id = t2.dept_id;

    -- 普通内连接
    SELECT * FROM emp t1,dept t2 WHERE t1.`dept_id` = t2.`id` AND t1.`join_date` >  '2011-11-11'

* 多表查询练习

-- 部门表
CREATE TABLE dept (
  id INT PRIMARY KEY PRIMARY KEY, -- 部门id
  dname VARCHAR(50), -- 部门名称
  loc VARCHAR(50) -- 部门所在地
);

-- 添加4个部门
INSERT INTO dept(id,dname,loc) VALUES 
(10,'教研部','北京'),
(20,'学工部','上海'),
(30,'销售部','广州'),
(40,'财务部','深圳');



​ – 职务表,职务名称,职务描述
​ CREATE TABLE job (
​ id INT PRIMARY KEY,
​ jname VARCHAR(20),
​ description VARCHAR(50)
​ );

​ – 添加4个职务
​ INSERT INTO job (id, jname, description) VALUES
​ (1, ‘董事长’, ‘管理整个公司,接单’),
​ (2, ‘经理’, ‘管理部门员工’),
​ (3, ‘销售员’, ‘向客人推销产品’),
​ (4, ‘文员’, ‘使用办公软件’);



​ – 员工表
​ CREATE TABLE emp (
​ id INT PRIMARY KEY, – 员工id
​ ename VARCHAR(50), – 员工姓名
​ job_id INT, – 职务id
​ mgr INT , – 上级领导
​ joindate DATE, – 入职日期
​ salary DECIMAL(7,2), – 工资
​ bonus DECIMAL(7,2), – 奖金
​ dept_id INT, – 所在部门编号
​ CONSTRAINT emp_jobid_ref_job_id_fk FOREIGN KEY (job_id) REFERENCES job (id),
​ CONSTRAINT emp_deptid_ref_dept_id_fk FOREIGN KEY (dept_id) REFERENCES dept (id)
​ );

​ – 添加员工
​ INSERT INTO emp(id,ename,job_id,mgr,joindate,salary,bonus,dept_id) VALUES
​ (1001,’孙悟空’,4,1004,’2000-12-17’,’8000.00’,NULL,20),
​ (1002,’卢俊义’,3,1006,’2001-02-20’,’16000.00’,’3000.00’,30),
​ (1003,’林冲’,3,1006,’2001-02-22’,’12500.00’,’5000.00’,30),
​ (1004,’唐僧’,2,1009,’2001-04-02’,’29750.00’,NULL,20),
​ (1005,’李逵’,4,1006,’2001-09-28’,’12500.00’,’14000.00’,30),
​ (1006,’宋江’,2,1009,’2001-05-01’,’28500.00’,NULL,30),
​ (1007,’刘备’,2,1009,’2001-09-01’,’24500.00’,NULL,10),
​ (1008,’猪八戒’,4,1004,’2007-04-19’,’30000.00’,NULL,20),
​ (1009,’罗贯中’,1,NULL,’2001-11-17’,’50000.00’,NULL,10),
​ (1010,’吴用’,3,1006,’2001-09-08’,’15000.00’,’0.00’,30),
​ (1011,’沙僧’,4,1004,’2007-05-23’,’11000.00’,NULL,20),
​ (1012,’李逵’,4,1006,’2001-12-03’,’9500.00’,NULL,30),
​ (1013,’小白龙’,4,1004,’2001-12-03’,’30000.00’,NULL,20),
​ (1014,’关羽’,4,1007,’2002-01-23’,’13000.00’,NULL,10);



​ – 工资等级表
​ CREATE TABLE salarygrade (
​ grade INT PRIMARY KEY, – 级别
​ losalary INT, – 最低工资
​ hisalary INT – 最高工资
​ );

​ – 添加5个工资等级
​ INSERT INTO salarygrade(grade,losalary,hisalary) VALUES
​ (1,7000,12000),
​ (2,12010,14000),
​ (3,14010,20000),
​ (4,20010,30000),
​ (5,30010,99990);

​ – 需求:

​ – 1.查询所有员工信息。查询员工编号,员工姓名,工资,职务名称,职务描述
​ /*
​ 分析:
​ 1.员工编号,员工姓名,工资,需要查询emp表 职务名称,职务描述 需要查询job表
​ 2.查询条件 emp.job_id = job.id

*/
SELECT
t1.id, – 员工编号
t1.ename, – 员工姓名
t1.salary,– 工资
t2.jname, – 职务名称
t2.description – 职务描述
FROM
emp t1, job t2
WHERE
t1.job_id = t2.id;



​ – 2.查询员工编号,员工姓名,工资,职务名称,职务描述,部门名称,部门位置
​ /*
​ 分析:
​ 1. 员工编号,员工姓名,工资 emp 职务名称,职务描述 job 部门名称,部门位置 dept
​ 2. 条件: emp.job_id = job.id and emp.dept_id = dept.id
/

​ SELECT
​ t1.id, – 员工编号
​ t1.ename, – 员工姓名
​ t1.salary,– 工资
​ t2.jname, – 职务名称
​ t2.description, – 职务描述
​ t3.dname, – 部门名称
​ t3.loc – 部门位置
​ FROM
​ emp t1, job t2,dept t3
​ WHERE
​ t1.job_id = t2.id AND t1.dept_id = t3.id;

​ – 3.查询员工姓名,工资,工资等级
​ /

​ 分析:
​ 1.员工姓名,工资 emp 工资等级 salarygrade
​ 2.条件 emp.salary >= salarygrade.losalary and emp.salary <= salarygrade.hisalary
​ emp.salary BETWEEN salarygrade.losalary and salarygrade.hisalary
/
​ SELECT
​ t1.ename ,
​ t1.salary,
​ t2.

​ FROM emp t1, salarygrade t2
​ WHERE t1.salary BETWEEN t2.losalary AND t2.hisalary;



​ – 4.查询员工姓名,工资,职务名称,职务描述,部门名称,部门位置,工资等级
​ /*
​ 分析:
​ 1. 员工姓名,工资 emp , 职务名称,职务描述 job 部门名称,部门位置,dept 工资等级 salarygrade
​ 2. 条件: emp.job_id = job.id and emp.dept_id = dept.id and emp.salary BETWEEN salarygrade.losalary and salarygrade.hisalary

​ */
​ SELECT
​ t1.ename,
​ t1.salary,
​ t2.jname,
​ t2.description,
​ t3.dname,
​ t3.loc,
​ t4.grade
​ FROM
​ emp t1,job t2,dept t3,salarygrade t4
​ WHERE
​ t1.job_id = t2.id
​ AND t1.dept_id = t3.id
​ AND t1.salary BETWEEN t4.losalary AND t4.hisalary;



​ – 5.查询出部门编号、部门名称、部门位置、部门人数

​ /*
​ 分析:
​ 1.部门编号、部门名称、部门位置 dept 表。 部门人数 emp表
​ 2.使用分组查询。按照emp.dept_id完成分组,查询count(id)
​ 3.使用子查询将第2步的查询结果和dept表进行关联查询

​ */
​ SELECT
​ t1.id,t1.dname,t1.loc , t2.total
​ FROM
​ dept t1,
​ (SELECT
​ dept_id,COUNT(id) total
​ FROM
​ emp
​ GROUP BY dept_id) t2
​ WHERE t1.id = t2.dept_id;


​ – 6.查询所有员工的姓名及其直接上级的姓名,没有领导的员工也需要查询

​ /*
​ 分析:
​ 1.姓名 emp, 直接上级的姓名 emp
​ * emp表的id 和 mgr 是自关联
​ 2.条件 emp.id = emp.mgr
​ 3.查询左表的所有数据,和 交集数据
​ * 使用左外连接查询

/
​ /

​ select
​ t1.ename,
​ t1.mgr,
​ t2.id,
​ t2.ename
​ from emp t1, emp t2
​ where t1.mgr = t2.id;

​ */

​ SELECT
​ t1.ename,
​ t1.mgr,
​ t2.id,
​ t2.ename
​ FROM emp t1
​ LEFT JOIN emp t2
​ ON t1.mgr = t2.id;

事务

1. 事务的基本介绍

1. 概念:
    *  如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败。

2. 操作:
    1. 开启事务: start transaction;
    2. 回滚:rollback;
    3. 提交:commit;
3. 例子:
    CREATE TABLE account (
        id INT PRIMARY KEY AUTO_INCREMENT,
        NAME VARCHAR(10),
        balance DOUBLE
    );
    -- 添加数据
    INSERT INTO account (NAME, balance) VALUES ('zhangsan', 1000), ('lisi', 1000);


​ SELECT * FROM account;
​ UPDATE account SET balance = 1000;
​ – 张三给李四转账 500 元

​ – 0. 开启事务
​ START TRANSACTION;
​ – 1. 张三账户 -500

​ UPDATE account SET balance = balance - 500 WHERE NAME = ‘zhangsan’;
​ – 2. 李四账户 +500
​ – 出错了…
​ UPDATE account SET balance = balance + 500 WHERE NAME = ‘lisi’;

​ – 发现执行没有问题,提交事务
​ COMMIT;

– 发现出问题了,回滚事务
ROLLBACK;
4. MySQL数据库中事务默认自动提交

* 事务提交的两种方式:
    * 自动提交:
        * mysql就是自动提交的
        * 一条DML(增删改)语句会自动提交一次事务。
    * 手动提交:
        * Oracle 数据库默认是手动提交事务
        * 需要先开启事务,再提交
* 修改事务的默认提交方式:
    * 查看事务的默认提交方式:SELECT @@autocommit; -- 1 代表自动提交  0 代表手动提交
    * 修改默认提交方式: set @@autocommit = 0;

事务的四大特征:

1. 原子性:是不可分割的最小操作单位,要么同时成功,要么同时失败。
2. 持久性:当事务提交或回滚后,数据库会持久化的保存数据。
3. 隔离性:多个事务之间。相互独立。
4. 一致性:事务操作前后,数据总量不变

事务的隔离级别(了解)

* 概念:多个事务之间隔离的,相互独立的。但是如果多个事务操作同一批数据,则会引发一些问题,设置不同的隔离级别就可以解决这些问题。
* 存在问题:
    1. 脏读:一个事务,读取到另一个事务中没有提交的数据
    2. 不可重复读(虚读):在同一个事务中,两次读取到的数据不一样。
    3. 幻读:一个事务操作(DML)数据表中所有记录,另一个事务添加了一条数据,则第一个事务查询不到自己的修改。
* 隔离级别:
    1. read uncommitted:读未提交
        * 产生的问题:脏读、不可重复读、幻读
    2. read committed:读已提交 (Oracle)
        * 产生的问题:不可重复读、幻读
    3. repeatable read:可重复读 (MySQL默认)
        * 产生的问题:幻读
    4. serializable:串行化
        * 可以解决所有的问题

    * 注意:隔离级别从小到大安全性越来越高,但是效率越来越低
    * 数据库查询隔离级别:
        * select @@tx_isolation;
    * 数据库设置隔离级别:
        * set global transaction isolation level  级别字符串;

* 演示:
    set global transaction isolation level read uncommitted;
    start transaction;
    -- 转账操作
    update account set balance = balance - 500 where id = 1;
    update account set balance = balance + 500 where id = 2;

DCL

* SQL分类:
    1. DDL:操作数据库和表
    2. DML:增删改表中数据
    3. DQL:查询表中数据
    4. DCL:管理用户,授权

* DBA:数据库管理员

* DCL:管理用户,授权

管理用户

1. 添加用户:
    * 语法:CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';
2. 删除用户:
    * 语法:DROP USER '用户名'@'主机名';
3. 修改用户密码:

    UPDATE USER SET PASSWORD = PASSWORD('新密码') WHERE USER = '用户名';
    UPDATE USER SET PASSWORD = PASSWORD('abc') WHERE USER = 'lisi';

    SET PASSWORD FOR '用户名'@'主机名' = PASSWORD('新密码');
    SET PASSWORD FOR 'root'@'localhost' = PASSWORD('123');

    * mysql中忘记了root用户的密码?
        1. cmd -- > net stop mysql 停止mysql服务
            * 需要管理员运行该cmd

        2. 使用无验证方式启动mysql服务: mysqld --skip-grant-tables
        3. 打开新的cmd窗口,直接输入mysql命令,敲回车。就可以登录成功
        4. use mysql;
        5. update user set password = password('你的新密码') where user = 'root';
        6. 关闭两个窗口
        7. 打开任务管理器,手动结束mysqld.exe 的进程
        8. 启动mysql服务
        9. 使用新密码登录。
4. 查询用户:
    -- 1. 切换到mysql数据库
    USE myql;
    -- 2. 查询user表
    SELECT * FROM USER;

    * 通配符: % 表示可以在任意主机使用用户登录数据库

权限管理:

1. 查询权限:
    -- 查询权限
    SHOW GRANTS FOR '用户名'@'主机名';
    SHOW GRANTS FOR 'lisi'@'%';

2. 授予权限:
    -- 授予权限
    grant 权限列表 on 数据库名.表名 to '用户名'@'主机名';
    -- 给张三用户授予所有权限,在任意数据库任意表上

    GRANT ALL ON *.* TO 'zhangsan'@'localhost';
3. 撤销权限:
    -- 撤销权限:
    revoke 权限列表 on 数据库名.表名 from '用户名'@'主机名';
    REVOKE UPDATE ON db3.`account` FROM 'lisi'@'%';

Git使用教程-总结

Git是目前世界上最先进的分布式版本控制系统(没有之一)。

Git有什么特点?简单来说就是:高端大气上档次!

那什么是版本控制系统?

如果你用Microsoft Word写过长篇大论,那你一定有这样的经历:

想删除一个段落,又怕将来想恢复找不回来怎么办?有办法,先把当前文件“另存为……”一个新的Word文件,再接着改,改到一定程度,再“另存为……”一个新文件

Git安装

image-20200113214320080

image-20200113214334154

image-20200113214351540

image-20200113214409218

image-20200113214420785

image-20200113214444113

image-20200113214455035

image-20200113214507046

image-20200113214531226

Git命令行操作

1.本地初始化

命令:git init   //注意:.git 目录中存放的是本地库相关的子目录和文件,不要删除,也不要胡 乱修改。

image-20200113214830300

2.设置签名

项目级别/仓库级别

仅在当前本地库范围内有效
形式:

用户名:tom Email 
地址:[email protected]

作用:区分不同开发人员的身份,这里设置的签名和登录远程库(代码托管中心)的账号、密码没有任何关系

git config user.name Time
git config user.email [email protected] //信息保存位置:./.git/config 文件

[更多:系统级别 用户级别]

2.基本操作

状态查看:

git status        //查看工作区,暂存区状态

添加

git add [file name]        //将工作区的"新建/修改" 添加到暂存区

提交

git commit -m"commit message" [file name]    //将暂存区的内容提交到本地仓库

查看历史记录

git log     //多屏显示控制方式: 空格向下翻页 b 向上翻页 q 退出

image-20200113220619968

git log --pretty=oneline    //只显示当前之前的版本

image-20200113220801001

git log--oneline    //同上

image-20200113220852979

git reflog

image-20200113220919262

前进后退

image-20200113221216275

  1. 基于索引值操作[推荐]:

    gitreset–hard[局部索引值] : gitreset–harda6ace91

  2. 使用^符号:只能后退 :

    gitreset–hardHEAD^ 注:一个^表示后退一步,n 个表示后退 n 步

  3. 使用~符号:只能后退:

    gitreset–hardHEAD~n 注:表示后退 n 步

reset 命令的三个参数对比
–soft 参数 : 仅仅在本地库移动 HEAD 指针
–mixed 参数 : 在本地库移动 HEAD 指针 重置暂存区
–hard 参数 : 在本地库移动 HEAD 指针 重置暂存区 重置工作区

比较文件差异

git diff[文件名]    // 将工作区中的文件和暂存区进行比较 

git diff[本地库中历史版本][文件名] // 将工作区中的文件和本地库历史记录比较  不带文件名比较多个文件

删除文件并找回

前提:删除前,文件存在时的状态提交到了本地库。
操作:git reset --hard [指针位置] 
    删除操作已经提交到本地库:指针位置指向历史记录 
    删除操作已经提交到本地库:指针位置指向历史记录 

比较文件差异

➢git diff [文件名] 
将工作区中的文件和暂存区进行比较
➢git diff [本地库中历史版本] [文件名]
将工作区中的文件和本地库历史记录比较
➢不带文件名比较多 个文件

分支管理

创建分支

git branch [分支名]

查看分支

git branch -V

切换分支

git checkout[分支名]

合并分支

第一步:切换到接受修改的分支(被合并,增加新内容)上
git checkout [被合并分支名]

第二步:执行merge命令
git merge [有新内容分支名]

合并冲突

image-20200113223124166

冲突的解决:

◆第一步:编辑文件,删除特殊符号
◆第二步:把文件修改到满意的程度,保存退出
◆第三步:git add[文件名]
◆第四步:gitcommit-m"日志信息"
    ●注意:此时commit一定不能带具体文件名

GitHub

创建远程库地址别名

git remote -v 查看当前所有远程地址别名 

git remote add [别名] [远程地址]

推送

git push [别名] [分支名]

image-20200113224732934

克隆

命令:git origin [远程地址]

image-20200113224935853

效果: 完整的把远程库下载到本地
创建 origin 远程地址别名
初始化本地库


拉取

pull=fetch+merge
git fetch [远程库地址别名][远程分支名] 
git merge [远程库地址别名/远程分支名] 
git pull [远程库地址别名][远程分支名]

### 解决冲突
如果不是基于 GitHub 远程库的最新版所做的修改,不能推送,必须先拉 取。
拉取下来后如果进入冲突状态,则按照“分支冲突解决”操作解决即可。

SSH 登录

➢进入当前用户的家 目录
$ cd~
➢删除.ssh 目录
$ rm -rvf .ssh.
➢运行命令生成.ssh 密钥目录
$ ssh -keygen -t rsa-C [Email]
[注意:这里-C这个参数是大写的C]
➢进入.ssh 目录查看文件列表
$ cd.ssh
$Is-I1F
➢查看id_rsa.pub 文件内容
➢复制id__rsa.pub文件内容,登录GitHub,点击用户头像→Settings→SSH and GPG 
keys
➢New SSH Key
➢输入复制的密钥信息
➢回到Gitbash创建远程地址别名

git remote add origin_ssh [email protected]:atguigu2018ybuq/huashan.git

团队成员邀请

//百度即可(不想写了)

跨团队协作

//百度即可(不想写了)

小站的艰难诞生

这个博客的诞生耗费了我大量的时间这本该是一件很简单的事然而还是贫穷限制想想
这篇文章将记录小站的”艰难”诞生~
先简单描述一下这个过程

服务器>域名>绕过域名备案>9-typecho>选模板>购买主机>(回到解放前)11-Hexo>配置
用了整整五天时间!!

前段时间刚好买了个服务器,也不知道为什么买,可能是因为便宜~
2020-1-4 花了几个小时的时间来选域名~
打算搞个博客,这应该是个半个小时能解决的问题,然而 第一个问题来了.
服务器买的是国内的所以,忘了备案这个事,花了半天时间通过一些手段绕过了备案
备案的问题解决了,选博客程序typecho 花了大量时间配置,和选主题~(强迫症)

本该就结束了,当一切搞定,发现网站访问速度….因为绕过代理的实现原理的特殊
可能打开你需要看着他转圈圈,这个虽然这个博客给自己看的,但是~强迫症犯了

之后花了一天左右找新的无需备案的服务器(因为贫穷)试了淘宝上的一个,那个垃圾一言难尽啊

转来转去又回到了Hexo 也是现在这个,静态托管在Conding ,这个过程也是无比的艰难
结果不知道几天的解决BUG终于能够正常访问,还是要有,选模板花了大量时间~

配置花了大半时间~包括插件的安装

直到1/13号算是完成了~中途一堆问题的细节就不写了-

一言难尽~

基础篇

通用语法及分类

  • DDL: 数据定义语言,用来定义数据库对象(数据库、表、字段)
  • DML: 数据操作语言,用来对数据库表中的数据进行增删改
  • DQL: 数据查询语言,用来查询数据库中表的记录
  • DCL: 数据控制语言,用来创建数据库用户、控制数据库的控制权限

DDL(数据定义语言)

数据定义语言

数据库操作

查询所有数据库:
SHOW DATABASES;
查询当前数据库:
SELECT DATABASE();
创建数据库:
CREATE DATABASE [ IF NOT EXISTS ] 数据库名 [ DEFAULT CHARSET 字符集] [COLLATE 排序规则 ];
删除数据库:
DROP DATABASE [ IF EXISTS ] 数据库名;
使用数据库:
USE 数据库名;

注意事项
  • UTF8字符集长度为3字节,有些符号占4字节,所以推荐用utf8mb4字符集

表操作

查询当前数据库所有表:
SHOW TABLES;
查询表结构:
DESC 表名;
查询指定表的建表语句:
SHOW CREATE TABLE 表名;

创建表:

1
2
3
4
5
6
7
CREATE TABLE 表名(
字段1 字段1类型 [COMMENT 字段1注释],
字段2 字段2类型 [COMMENT 字段2注释],
字段3 字段3类型 [COMMENT 字段3注释],
...
字段n 字段n类型 [COMMENT 字段n注释]
)[ COMMENT 表注释 ];

最后一个字段后面没有逗号

添加字段:
ALTER TABLE 表名 ADD 字段名 类型(长度) [COMMENT 注释] [约束];
例:ALTER TABLE emp ADD nickname varchar(20) COMMENT '昵称';

修改数据类型:
ALTER TABLE 表名 MODIFY 字段名 新数据类型(长度);
修改字段名和字段类型:
ALTER TABLE 表名 CHANGE 旧字段名 新字段名 类型(长度) [COMMENT 注释] [约束];
例:将emp表的nickname字段修改为username,类型为varchar(30)
ALTER TABLE emp CHANGE nickname username varchar(30) COMMENT '昵称';

删除字段:
ALTER TABLE 表名 DROP 字段名;

修改表名:
ALTER TABLE 表名 RENAME TO 新表名

删除表:
DROP TABLE [IF EXISTS] 表名;
删除表,并重新创建该表:
TRUNCATE TABLE 表名;

DML(数据操作语言)

添加数据

指定字段:
INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (值1, 值2, ...);
全部字段:
INSERT INTO 表名 VALUES (值1, 值2, ...);

批量添加数据:
INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (值1, 值2, ...), (值1, 值2, ...), (值1, 值2, ...);
INSERT INTO 表名 VALUES (值1, 值2, ...), (值1, 值2, ...), (值1, 值2, ...);

注意事项
  • 字符串和日期类型数据应该包含在引号中
  • 插入的数据大小应该在字段的规定范围内

更新和删除数据

修改数据:
UPDATE 表名 SET 字段名1 = 值1, 字段名2 = 值2, ... [ WHERE 条件 ];
例:
UPDATE emp SET name = 'Jack' WHERE id = 1;

删除数据:
DELETE FROM 表名 [ WHERE 条件 ];

DQL(数据查询语言)

语法:

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

基础查询

查询多个字段:
SELECT 字段1, 字段2, 字段3, ... FROM 表名;
SELECT * FROM 表名;

设置别名:
SELECT 字段1 [ AS 别名1 ], 字段2 [ AS 别名2 ], 字段3 [ AS 别名3 ], ... FROM 表名;
SELECT 字段1 [ 别名1 ], 字段2 [ 别名2 ], 字段3 [ 别名3 ], ... FROM 表名;

去除重复记录:
SELECT DISTINCT 字段列表 FROM 表名;

转义:
SELECT * FROM 表名 WHERE name LIKE '/_张三' ESCAPE '/'
/ 之后的_不作为通配符

条件查询

语法:
SELECT 字段列表 FROM 表名 WHERE 条件列表;

条件:

比较运算符 功能
> 大于
>= 大于等于
< 小于
<= 小于等于
= 等于
<> 或 != 不等于
BETWEEN … AND … 在某个范围内(含最小、最大值)
IN(…) 在in之后的列表中的值,多选一
LIKE 占位符 模糊匹配(_匹配单个字符,%匹配任意个字符)
IS NULL 是NULL
逻辑运算符 功能
AND 或 && 并且(多个条件同时成立)
OR 或 || 或者(多个条件任意一个成立)
NOT 或 ! 非,不是

例子:

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
-- 年龄等于30
select * from employee where age = 30;
-- 年龄小于30
select * from employee where age < 30;
-- 小于等于
select * from employee where age <= 30;
-- 没有身份证
select * from employee where idcard is null or idcard = '';
-- 有身份证
select * from employee where idcard;
select * from employee where idcard is not null;
-- 不等于
select * from employee where age != 30;
-- 年龄在20到30之间
select * from employee where age between 20 and 30;
select * from employee where age >= 20 and age <= 30;
-- 下面语句不报错,但查不到任何信息
select * from employee where age between 30 and 20;
-- 性别为女且年龄小于30
select * from employee where age < 30 and gender = '女';
-- 年龄等于25或30或35
select * from employee where age = 25 or age = 30 or age = 35;
select * from employee where age in (25, 30, 35);
-- 姓名为两个字
select * from employee where name like '__';
-- 身份证最后为X
select * from employee where idcard like '%X';

聚合查询(聚合函数)

常见聚合函数:

函数 功能
count 统计数量
max 最大值
min 最小值
avg 平均值
sum 求和

语法:
SELECT 聚合函数(字段列表) FROM 表名;
例:
SELECT count(id) from employee where workaddress = "广东省";

分组查询

语法:
SELECT 字段列表 FROM 表名 [ WHERE 条件 ] GROUP BY 分组字段名 [ HAVING 分组后的过滤条件 ];

where 和 having 的区别:

  • 执行时机不同:where是分组之前进行过滤,不满足where条件不参与分组;having是分组后对结果进行过滤。
  • 判断条件不同:where不能对聚合函数进行判断,而having可以。

例子:

1
2
3
4
5
6
7
8
9
10
-- 根据性别分组,统计男性和女性数量(只显示分组数量,不显示哪个是男哪个是女)
select count(*) from employee group by gender;
-- 根据性别分组,统计男性和女性数量
select gender, count(*) from employee group by gender;
-- 根据性别分组,统计男性和女性的平均年龄
select gender, avg(age) from employee group by gender;
-- 年龄小于45,并根据工作地址分组
select workaddress, count(*) from employee where age < 45 group by workaddress;
-- 年龄小于45,并根据工作地址分组,获取员工数量大于等于3的工作地址
select workaddress, count(*) address_count from employee where age < 45 group by workaddress having address_count >= 3;
注意事项
  • 执行顺序:where > 聚合函数 > having
  • 分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段无任何意义

排序查询

语法:
SELECT 字段列表 FROM 表名 ORDER BY 字段1 排序方式1, 字段2 排序方式2;

排序方式:

  • ASC: 升序(默认)
  • DESC: 降序

例子:

1
2
3
4
5
-- 根据年龄升序排序
SELECT * FROM employee ORDER BY age ASC;
SELECT * FROM employee ORDER BY age;
-- 两字段排序,根据年龄升序排序,入职时间降序排序
SELECT * FROM employee ORDER BY age ASC, entrydate DESC;
注意事项

如果是多字段排序,当第一个字段值相同时,才会根据第二个字段进行排序

分页查询

语法:
SELECT 字段列表 FROM 表名 LIMIT 起始索引, 查询记录数;

例子:

1
2
3
4
-- 查询第一页数据,展示10条
SELECT * FROM employee LIMIT 0, 10;
-- 查询第二页
SELECT * FROM employee LIMIT 10, 10;
注意事项
  • 起始索引从0开始,起始索引 = (查询页码 - 1) * 每页显示记录数
  • 分页查询是数据库的方言,不同数据库有不同实现,MySQL是LIMIT
  • 如果查询的是第一页数据,起始索引可以省略,直接简写 LIMIT 10

DQL执行顺序

FROM -> WHERE -> GROUP BY -> SELECT -> ORDER BY -> LIMIT

DCL

管理用户

查询用户:

1
2
USER mysql;
SELECT * FROM user;

创建用户:
CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';

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

删除用户:
DROP USER '用户名'@'主机名';

例子:

1
2
3
4
5
6
7
8
9
-- 创建用户test,只能在当前主机localhost访问
create user 'test'@'localhost' identified by '123456';
-- 创建用户test,能在任意主机访问
create user 'test'@'%' identified by '123456';
create user 'test' identified by '123456';
-- 修改密码
alter user 'test'@'localhost' identified with mysql_native_password by '1234';
-- 删除用户
drop user 'test'@'localhost';
注意事项
  • 主机名可以使用 % 通配

权限控制

常用权限:

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

更多权限请看权限一览表

查询权限:
SHOW GRANTS FOR '用户名'@'主机名';

授予权限:
GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机名';

撤销权限:
REVOKE 权限列表 ON 数据库名.表名 FROM '用户名'@'主机名';

注意事项
  • 多个权限用逗号分隔
  • 授权时,数据库名和表名可以用 * 进行通配,代表所有

函数

  • 字符串函数
  • 数值函数
  • 日期函数
  • 流程函数

字符串函数

常用函数:

函数 功能
CONCAT(s1, s2, …, sn) 字符串拼接,将s1, s2, …, sn拼接成一个字符串
LOWER(str) 将字符串全部转为小写
UPPER(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
2
3
4
5
6
7
8
9
10
11
12
13
14
-- 拼接
SELECT CONCAT('Hello', 'World');
-- 小写
SELECT LOWER('Hello');
-- 大写
SELECT UPPER('Hello');
-- 左填充
SELECT LPAD('01', 5, '-');
-- 右填充
SELECT RPAD('01', 5, '-');
-- 去除空格
SELECT TRIM(' Hello World ');
-- 切片(起始索引为1)
SELECT SUBSTRING('Hello World', 1, 5);

数值函数

常见函数:

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

日期函数

常用函数:

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

例子:

1
2
-- DATE_ADD
SELECT DATE_ADD(NOW(), INTERVAL 70 YEAR);

流程函数

常用函数:

函数 功能
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默认值

例子:

1
2
3
4
5
6
7
8
select
name,
(case when age > 30 then '中年' else '青年' end)
from employee;
select
name,
(case workaddress when '北京市' then '一线城市' when '上海市' then '一线城市' else '二线城市' end) as '工作地址'
from employee;

约束

分类:

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

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

常用约束

约束条件 关键字
主键 PRIMARY KEY
自动增长 AUTO_INCREMENT
不为空 NOT NULL
唯一 UNIQUE
逻辑条件 CHECK
默认值 DEFAULT

例子:

1
2
3
4
5
6
7
create table user(
id int primary key auto_increment,
name varchar(10) not null unique,
age int check(age > 0 and age < 120),
status char(1) default '1',
gender char(1)
);

外键约束

添加外键:

1
2
3
4
5
6
7
8
9
CREATE TABLE 表名(
字段名 字段类型,
...
[CONSTRAINT] [外键名称] FOREIGN KEY(外键字段名) REFERENCES 主表(主表列名)
);
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名) REFERENCES 主表(主表列名);

-- 例子
alter table emp add constraint fk_emp_dept_id foreign key(dept_id) references dept(id);

删除外键:
ALTER TABLE 表名 DROP FOREIGN KEY 外键名;

删除/更新行为

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

更改删除/更新行为:
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段) REFERENCES 主表名(主表字段名) ON UPDATE 行为 ON DELETE 行为;

多表查询

多表关系

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

一对多

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

多对多

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

一对一

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

查询

合并查询(笛卡尔积,会展示所有组合结果):
select * from employee, dept;

笛卡尔积:两个集合A集合和B集合的所有组合情况(在多表查询时,需要消除无效的笛卡尔积)

消除无效笛卡尔积:
select * from employee, dept where employee.dept = dept.id;

内连接查询

内连接查询的是两张表交集的部分

隐式内连接:
SELECT 字段列表 FROM 表1, 表2 WHERE 条件 ...;

显式内连接:
SELECT 字段列表 FROM 表1 [ INNER ] JOIN 表2 ON 连接条件 ...;

显式性能比隐式高

例子:

1
2
3
4
5
-- 查询员工姓名,及关联的部门的名称
-- 隐式
select e.name, d.name from employee as e, dept as d where e.dept = d.id;
-- 显式
select e.name, d.name from employee as e inner join dept as d on e.dept = d.id;

外连接查询

左外连接:
查询左表所有数据,以及两张表交集部分数据
SELECT 字段列表 FROM 表1 LEFT [ OUTER ] JOIN 表2 ON 条件 ...;
相当于查询表1的所有数据,包含表1和表2交集部分数据

右外连接:
查询右表所有数据,以及两张表交集部分数据
SELECT 字段列表 FROM 表1 RIGHT [ OUTER ] JOIN 表2 ON 条件 ...;

例子:

1
2
3
4
5
-- 左
select e.*, d.name from employee as e left outer join dept as d on e.dept = d.id;
select d.name, e.* from dept d left outer join emp e on e.dept = d.id; -- 这条语句与下面的语句效果一样
-- 右
select d.name, e.* from employee as e right outer join dept as d on e.dept = d.id;

左连接可以查询到没有dept的employee,右连接可以查询到没有employee的dept

自连接查询

当前表与自身的连接查询,自连接必须使用表别名

语法:
SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件 ...;

自连接查询,可以是内连接查询,也可以是外连接查询

例子:

1
2
3
4
-- 查询员工及其所属领导的名字
select a.name, b.name from employee a, employee b where a.manager = b.id;
-- 没有领导的也查询出来
select a.name, b.name from employee a left join employee b on a.manager = b.id;

联合查询 union, union all

把多次查询的结果合并,形成一个新的查询集

语法:

1
2
3
SELECT 字段列表 FROM 表A ...
UNION [ALL]
SELECT 字段列表 FROM 表B ...

注意事项

  • UNION ALL 会有重复结果,UNION 不会
  • 联合查询比使用or效率高,不会使索引失效

子查询

SQL语句中嵌套SELECT语句,称谓嵌套查询,又称子查询。
SELECT * FROM t1 WHERE column1 = ( SELECT column1 FROM t2);
子查询外部的语句可以是 INSERT / UPDATE / DELETE / SELECT 的任何一个

根据子查询结果可以分为:

  • 标量子查询(子查询结果为单个值)
  • 列子查询(子查询结果为一列)
  • 行子查询(子查询结果为一行)
  • 表子查询(子查询结果为多行多列)

根据子查询位置可分为:

  • WHERE 之后
  • FROM 之后
  • SELECT 之后

标量子查询

子查询返回的结果是单个值(数字、字符串、日期等)。
常用操作符:- < > > >= < <=

例子:

1
2
3
4
5
6
7
8
9
-- 查询销售部所有员工
select id from dept where name = '销售部';
-- 根据销售部部门ID,查询员工信息
select * from employee where dept = 4;
-- 合并(子查询)
select * from employee where dept = (select id from dept where name = '销售部');

-- 查询xxx入职之后的员工信息
select * from employee where entrydate > (select entrydate from employee where name = 'xxx');

列子查询

返回的结果是一列(可以是多行)。

常用操作符:

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

例子:

1
2
3
4
5
6
-- 查询销售部和市场部的所有员工信息
select * from employee where dept in (select id from dept where name = '销售部' or name = '市场部');
-- 查询比财务部所有人工资都高的员工信息
select * from employee where salary > all(select salary from employee where dept = (select id from dept where name = '财务部'));
-- 查询比研发部任意一人工资高的员工信息
select * from employee where salary > any (select salary from employee where dept = (select id from dept where name = '研发部'));

行子查询

返回的结果是一行(可以是多列)。
常用操作符:=, <, >, IN, NOT IN

例子:

1
2
3
-- 查询与xxx的薪资及直属领导相同的员工信息
select * from employee where (salary, manager) = (12500, 1);
select * from employee where (salary, manager) = (select salary, manager from employee where name = 'xxx');

表子查询

返回的结果是多行多列
常用操作符:IN

例子:

1
2
3
4
-- 查询与xxx1,xxx2的职位和薪资相同的员工
select * from employee where (job, salary) in (select job, salary from employee where name = 'xxx1' or name = 'xxx2');
-- 查询入职日期是2006-01-01之后的员工,及其部门信息
select e.*, d.* from (select * from employee where entrydate > '2006-01-01') as e left join dept as d on e.dept = d.id;

事务

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

基本操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
-- 1. 查询张三账户余额
select * from account where name = '张三';
-- 2. 将张三账户余额-1000
update account set money = money - 1000 where name = '张三';
-- 此语句出错后张三钱减少但是李四钱没有增加
模拟sql语句错误
-- 3. 将李四账户余额+1000
update account set money = money + 1000 where name = '李四';

-- 查看事务提交方式
SELECT @@AUTOCOMMIT;
-- 设置事务提交方式,1为自动提交,0为手动提交,该设置只对当前会话有效
SET @@AUTOCOMMIT = 0;
-- 提交事务
COMMIT;
-- 回滚事务
ROLLBACK;

-- 设置手动提交后上面代码改为:
select * from account where name = '张三';
update account set money = money - 1000 where name = '张三';
update account set money = money + 1000 where name = '李四';
commit;

操作方式二:

开启事务:
START TRANSACTION 或 BEGIN TRANSACTION;
提交事务:
COMMIT;
回滚事务:
ROLLBACK;

操作实例:

1
2
3
4
5
start transaction;
select * from account where name = '张三';
update account set money = money - 1000 where name = '张三';
update account set money = money + 1000 where name = '李四';
commit;

四大特性ACID

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

并发事务

问题 描述
脏读 一个事务读到另一个事务还没提交的数据
不可重复读 一个事务先后读取同一条记录,但两次读取的数据不同
幻读 一个事务按照条件查询数据时,没有对应的数据行,但是再插入数据时,又发现这行数据已经存在

这三个问题的详细演示:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=55cd

并发事务隔离级别:

隔离级别 脏读 不可重复读 幻读
Read uncommitted
Read committed ×
Repeatable Read(默认) × ×
Serializable × × ×
  • √表示在当前隔离级别下该问题会出现
  • Serializable 性能最低;Read uncommitted 性能最高,数据安全性最差

查看事务隔离级别:
SELECT @@TRANSACTION_ISOLATION;
设置事务隔离级别:
SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL {READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE };
SESSION 是会话级别,表示只针对当前会话有效,GLOBAL 表示对所有会话有效

进阶篇

存储引擎

MySQL体系结构:

结构图
层级描述

存储引擎就是存储数据、建立索引、更新/查询数据等技术的实现方式。存储引擎是基于表而不是基于库的,所以存储引擎也可以被称为表引擎。
默认存储引擎是InnoDB。

相关操作:

1
2
3
4
5
6
7
8
-- 查询建表语句
show create table account;
-- 建表时指定存储引擎
CREATE TABLE 表名(
...
) ENGINE=INNODB;
-- 查看当前数据库支持的存储引擎
show engines;

InnoDB

InnoDB 是一种兼顾高可靠性和高性能的通用存储引擎,在 MySQL 5.5 之后,InnoDB 是默认的 MySQL 引擎。

特点:

  • DML 操作遵循 ACID 模型,支持事务
  • 行级锁,提高并发访问性能
  • 支持外键约束,保证数据的完整性和正确性

文件:

  • xxx.ibd: xxx代表表名,InnoDB 引擎的每张表都会对应这样一个表空间文件,存储该表的表结构(frm、sdi)、数据和索引。

参数:innodb_file_per_table,决定多张表共享一个表空间还是每张表对应一个表空间

知识点:

查看 Mysql 变量:
show variables like 'innodb_file_per_table';

从idb文件提取表结构数据:
(在cmd运行)
ibd2sdi xxx.ibd

InnoDB 逻辑存储结构:
InnoDB逻辑存储结构

MyISAM

MyISAM 是 MySQL 早期的默认存储引擎。

特点:

  • 不支持事务,不支持外键
  • 支持表锁,不支持行锁
  • 访问速度快

文件:

  • xxx.sdi: 存储表结构信息
  • xxx.MYD: 存储数据
  • xxx.MYI: 存储索引

Memory

Memory 引擎的表数据是存储在内存中的,受硬件问题、断电问题的影响,只能将这些表作为临时表或缓存使用。

特点:

  • 存放在内存中,速度快
  • hash索引(默认)

文件:

  • xxx.sdi: 存储表结构信息

存储引擎特点

特点 InnoDB MyISAM Memory
存储限制 64TB
事务安全 支持 - -
锁机制 行锁 表锁 表锁
B+tree索引 支持 支持 支持
Hash索引 - - 支持
全文索引 支持(5.6版本之后) 支持 -
空间使用 N/A
内存使用 中等
批量插入速度
支持外键 支持 - -

存储引擎的选择

在选择存储引擎时,应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统,还可以根据实际情况选择多种存储引擎进行组合。

  • InnoDB: 如果应用对事物的完整性有比较高的要求,在并发条件下要求数据的一致性,数据操作除了插入和查询之外,还包含很多的更新、删除操作,则 InnoDB 是比较合适的选择
  • MyISAM: 如果应用是以读操作和插入操作为主,只有很少的更新和删除操作,并且对事务的完整性、并发性要求不高,那这个存储引擎是非常合适的。
  • Memory: 将所有数据保存在内存中,访问速度快,通常用于临时表及缓存。Memory 的缺陷是对表的大小有限制,太大的表无法缓存在内存中,而且无法保障数据的安全性

电商中的足迹和评论适合使用 MyISAM 引擎,缓存适合使用 Memory 引擎。

性能分析

查看执行频次

查看当前数据库的 INSERT, UPDATE, DELETE, SELECT 访问频次:
SHOW GLOBAL STATUS LIKE 'Com_______'; 或者 SHOW SESSION STATUS LIKE 'Com_______';
例:show global status like 'Com_______'

慢查询日志

慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒)的所有SQL语句的日志。
MySQL的慢查询日志默认没有开启,需要在MySQL的配置文件(/etc/my.cnf)中配置如下信息:
# 开启慢查询日志开关
slow_query_log=1
# 设置慢查询日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志
long_query_time=2
更改后记得重启MySQL服务,日志文件位置:/var/lib/mysql/localhost-slow.log

查看慢查询日志开关状态:
show variables like 'slow_query_log';

profile

show profile 能在做SQL优化时帮我们了解时间都耗费在哪里。通过 have_profiling 参数,能看到当前 MySQL 是否支持 profile 操作:
SELECT @@have_profiling;
profiling 默认关闭,可以通过set语句在session/global级别开启 profiling:
SET profiling = 1;
查看所有语句的耗时:
show profiles;
查看指定query_id的SQL语句各个阶段的耗时:
show profile for query query_id;
查看指定query_id的SQL语句CPU的使用情况
show profile cpu for query query_id;

explain

EXPLAIN 或者 DESC 命令获取 MySQL 如何执行 SELECT 语句的信息,包括在 SELECT 语句执行过程中表如何连接和连接的顺序。
语法:
# 直接在select语句之前加上关键字 explain / desc
EXPLAIN SELECT 字段列表 FROM 表名 HWERE 条件;

EXPLAIN 各字段含义:

  • id:select 查询的序列号,表示查询中执行 select 子句或者操作表的顺序(id相同,执行顺序从上到下;id不同,值越大越先执行)
  • select_type:表示 SELECT 的类型,常见取值有 SIMPLE(简单表,即不适用表连接或者子查询)、PRIMARY(主查询,即外层的查询)、UNION(UNION中的第二个或者后面的查询语句)、SUBQUERY(SELECT/WHERE之后包含了子查询)等
  • type:表示连接类型,性能由好到差的连接类型为 NULL、system、const、eq_ref、ref、range、index、all
  • possible_key:可能应用在这张表上的索引,一个或多个
  • Key:实际使用的索引,如果为 NULL,则没有使用索引
  • Key_len:表示索引中使用的字节数,该值为索引字段最大可能长度,并非实际使用长度,在不损失精确性的前提下,长度越短越好
  • rows:MySQL认为必须要执行的行数,在InnoDB引擎的表中,是一个估计值,可能并不总是准确的
  • filtered:表示返回结果的行数占需读取行数的百分比,filtered的值越大越好

索引

img

索引是帮助 MySQL 高效获取数据数据结构(有序)。在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查询算法,这种数据结构就是索引。

优缺点:

优点:

  • 提高数据检索效率,降低数据库的IO成本
  • 通过索引列对数据进行排序,降低数据排序的成本,降低CPU的消耗

缺点:

  • 索引列也是要占用空间的
  • 索引大大提高了查询效率,但降低了更新的速度,比如 INSERT、UPDATE、DELETE

索引结构

索引结构 描述
B+Tree 最常见的索引类型,大部分引擎都支持B+树索引
Hash 底层数据结构是用哈希表实现,只有精确匹配索引列的查询才有效,不支持范围查询
R-Tree(空间索引) 空间索引是 MyISAM 引擎的一个特殊索引类型,主要用于地理空间数据类型,通常使用较少
Full-Text(全文索引) 是一种通过建立倒排索引,快速匹配文档的方式,类似于 Lucene, Solr, ES
索引 InnoDB MyISAM Memory
B+Tree索引 支持 支持 支持
Hash索引 不支持 不支持 支持
R-Tree索引 不支持 支持 不支持
Full-text 5.6版本后支持 支持 不支持

B-Tree

二叉树

二叉树的缺点可以用红黑树来解决:
红黑树
红黑树也存在大数据量情况下,层级较深,检索速度慢的问题。

为了解决上述问题,可以使用 B-Tree 结构。
B-Tree (多路平衡查找树) 以一棵最大度数(max-degree,指一个节点的子节点个数)为5(5阶)的 b-tree 为例(每个节点最多存储4个key,5个指针)

B-Tree结构

B-Tree 的数据插入过程动画参照:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=68
演示地址:https://www.cs.usfca.edu/~galles/visualization/BTree.html

B+Tree

结构图:

B+Tree结构图

演示地址:https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html

与 B-Tree 的区别:

  • 所有的数据都会出现在叶子节点
  • 叶子节点形成一个单向链表

MySQL 索引数据结构对经典的 B+Tree 进行了优化。在原 B+Tree 的基础上,增加一个指向相邻叶子节点的链表指针,就形成了带有顺序指针的 B+Tree,提高区间访问的性能。

MySQL B+Tree 结构图

Hash

哈希索引就是采用一定的hash算法,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。
如果两个(或多个)键值,映射到一个相同的槽位上,他们就产生了hash冲突(也称为hash碰撞),可以通过链表来解决。

Hash索引原理图

特点:

  • Hash索引只能用于对等比较(=、in),不支持范围查询(betwwn、>、<、…)
  • 无法利用索引完成排序操作
  • 查询效率高,通常只需要一次检索就可以了,效率通常要高于 B+Tree 索引

存储引擎支持:

  • Memory
  • InnoDB: 具有自适应hash功能,hash索引是存储引擎根据 B+Tree 索引在指定条件下自动构建的

面试题

  1. 为什么 InnoDB 存储引擎选择使用 B+Tree 索引结构?
  • 相对于二叉树,层级更少,搜索效率高
  • 对于 B-Tree,无论是叶子节点还是非叶子节点,都会保存数据,这样导致一页中存储的键值减少,指针也跟着减少,要同样保存大量数据,只能增加树的高度,导致性能降低
  • 相对于 Hash 索引,B+Tree 支持范围匹配及排序操作

索引分类

分类 含义 特点 关键字
主键索引 针对于表中主键创建的索引 默认自动创建,只能有一个 PRIMARY
唯一索引 避免同一个表中某数据列中的值重复 可以有多个 UNIQUE
常规索引 快速定位特定数据 可以有多个
全文索引 全文索引查找的是文本中的关键词,而不是比较索引中的值 可以有多个 FULLTEXT

在 InnoDB 存储引擎中,根据索引的存储形式,又可以分为以下两种:

分类 含义 特点
聚集索引(Clustered Index) 将数据存储与索引放一块,索引结构的叶子节点保存了行数据 必须有,而且只有一个
二级索引(Secondary Index) 将数据与索引分开存储,索引结构的叶子节点关联的是对应的主键 可以存在多个

演示图:

大致原理
演示图

聚集索引选取规则:

  • 如果存在主键,主键索引就是聚集索引
  • 如果不存在主键,将使用第一个唯一(UNIQUE)索引作为聚集索引
  • 如果表没有主键或没有合适的唯一索引,则 InnoDB 会自动生成一个 rowid 作为隐藏的聚集索引

image-20220525105014706

思考题

1. 以下 SQL 语句,哪个执行效率高?为什么?

1
2
3
select * from user where id = 10;
select * from user where name = 'Arm';
-- 备注:id为主键,name字段创建的有索引

答:第一条语句,因为第二条需要回表查询,相当于两个步骤。

2. InnoDB 主键索引的 B+Tree 高度为多少?

答:假设一行数据大小为1k,一页中可以存储16行这样的数据。InnoDB 的指针占用6个字节的空间,主键假设为bigint,占用字节数为8.
可得公式:n * 8 + (n + 1) * 6 = 16 * 1024,其中 8 表示 bigint 占用的字节数,n 表示当前节点存储的key的数量,(n + 1) 表示指针数量(比key多一个)。算出n约为1170。

如果树的高度为2,那么他能存储的数据量大概为:1171 * 16 = 18736
如果树的高度为3,那么他能存储的数据量大概为:1171 * 1171 * 16 = 21939856

另外,如果有成千上万的数据,那么就要考虑分表,涉及运维篇知识。

语法

创建索引:
CREATE [ UNIQUE | FULLTEXT ] INDEX index_name ON table_name (index_col_name, ...);
如果不加 CREATE 后面不加索引类型参数,则创建的是常规索引

查看索引:
SHOW INDEX FROM table_name;

删除索引:
DROP INDEX index_name ON table_name;

案例:

1
2
3
4
5
6
7
8
9
10
11
-- name字段为姓名字段,该字段的值可能会重复,为该字段创建索引
create index idx_user_name on tb_user(name);
-- phone手机号字段的值非空,且唯一,为该字段创建唯一索引
create unique index idx_user_phone on tb_user (phone);
-- 为profession, age, status创建联合索引
create index idx_user_pro_age_stat on tb_user(profession, age, status);
-- 为email建立合适的索引来提升查询效率
create index idx_user_email on tb_user(email);

-- 删除索引
drop index idx_user_email on tb_user;

使用规则

最左前缀法则

如果索引关联了多列(联合索引),要遵守最左前缀法则,最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引中的列。
如果跳跃某一列,索引将部分失效(后面的字段索引失效)。

联合索引中,出现范围查询(<, >),范围查询右侧的列索引失效。可以用>=或者<=来规避索引失效问题。

索引失效情况

  1. 在索引列上进行运算操作,索引将失效。如:explain select * from tb_user where substring(phone, 10, 2) = '15';
  2. 字符串类型字段使用时,不加引号,索引将失效。如:explain select * from tb_user where phone = 17799990015;,此处phone的值没有加引号
  3. 模糊查询中,如果仅仅是尾部模糊匹配,索引不会是失效;如果是头部模糊匹配,索引失效。如:explain select * from tb_user where profession like '%工程';,前后都有 % 也会失效。
  4. 用 or 分割开的条件,如果 or 其中一个条件的列没有索引,那么涉及的索引都不会被用到。
  5. 如果 MySQL 评估使用索引比全表更慢,则不使用索引。

SQL 提示

是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些人为的提示来达到优化操作的目的。

例如,使用索引:
explain select * from tb_user use index(idx_user_pro) where profession="软件工程";
不使用哪个索引:
explain select * from tb_user ignore index(idx_user_pro) where profession="软件工程";
必须使用哪个索引:
explain select * from tb_user force index(idx_user_pro) where profession="软件工程";

use 是建议,实际使用哪个索引 MySQL 还会自己权衡运行速度去更改,force就是无论如何都强制使用该索引。

覆盖索引&回表查询

尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能找到),减少 select *。

explain 中 extra 字段含义:
using index condition:查找使用了索引,但是需要回表查询数据
using where; using index;:查找使用了索引,但是需要的数据都在索引列中能找到,所以不需要回表查询

如果在聚集索引中直接能找到对应的行,则直接返回行数据,只需要一次查询,哪怕是select *;如果在辅助索引中找聚集索引,如select id, name from xxx where name='xxx';,也只需要通过辅助索引(name)查找到对应的id,返回name和name索引对应的id即可,只需要一次查询;如果是通过辅助索引查找其他字段,则需要回表查询,如select id, name, gender from xxx where name='xxx';

所以尽量不要用select *,容易出现回表查询,降低效率,除非有联合索引包含了所有字段

面试题:一张表,有四个字段(id, username, password, status),由于数据量大,需要对以下SQL语句进行优化,该如何进行才是最优方案:
select id, username, password from tb_user where username='itcast';

解:给username和password字段建立联合索引,则不需要回表查询,直接覆盖索引

前缀索引

当字段类型为字符串(varchar, text等)时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO,影响查询效率,此时可以只降字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。

语法:create index idx_xxxx on table_name(columnn(n));
前缀长度:可以根据索引的选择性来决定,而选择性是指不重复的索引值(基数)和数据表的记录总数的比值,索引选择性越高则查询效率越高,唯一索引的选择性是1,这是最好的索引选择性,性能也是最好的。
求选择性公式:

1
2
select count(distinct email) / count(*) from tb_user;
select count(distinct substring(email, 1, 5)) / count(*) from tb_user;

show index 里面的sub_part可以看到接取的长度

单列索引&联合索引

单列索引:即一个索引只包含单个列
联合索引:即一个索引包含了多个列
在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引,而非单列索引。

单列索引情况:
explain select id, phone, name from tb_user where phone = '17799990010' and name = '韩信';
这句只会用到phone索引字段

注意事项
  • 多条件联合查询时,MySQL优化器会评估哪个字段的索引效率更高,会选择该索引完成本次查询

设计原则

  1. 针对于数据量较大,且查询比较频繁的表建立索引
  2. 针对于常作为查询条件(where)、排序(order by)、分组(group by)操作的字段建立索引
  3. 尽量选择区分度高的列作为索引,尽量建立唯一索引,区分度越高,使用索引的效率越高
  4. 如果是字符串类型的字段,字段长度较长,可以针对于字段的特点,建立前缀索引
  5. 尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率
  6. 要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价就越大,会影响增删改的效率
  7. 如果索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询

SQL 优化

插入数据

普通插入:

  1. 采用批量插入(一次插入的数据不建议超过1000条)
  2. 手动提交事务
  3. 主键顺序插入

大批量插入:
如果一次性需要插入大批量数据,使用insert语句插入性能较低,此时可以使用MySQL数据库提供的load指令插入。

1
2
3
4
5
6
7
# 客户端连接服务端时,加上参数 --local-infile(这一行在bash/cmd界面输入)
mysql --local-infile -u root -p
# 设置全局参数local_infile为1,开启从本地加载文件导入数据的开关
set global local_infile = 1;
select @@local_infile;
# 执行load指令将准备好的数据,加载到表结构中
load data local infile '/root/sql1.log' into table 'tb_user' fields terminated by ',' lines terminated by '\n';

主键优化

数据组织方式:在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的,这种存储方式的表称为索引组织表(Index organized table, IOT)

页分裂:页可以为空,也可以填充一般,也可以填充100%,每个页包含了2-N行数据(如果一行数据过大,会行溢出),根据主键排列。
页合并:当删除一行记录时,实际上记录并没有被物理删除,只是记录被标记(flaged)为删除并且它的空间变得允许被其他记录声明使用。当页中删除的记录到达 MERGE_THRESHOLD(默认为页的50%),InnoDB会开始寻找最靠近的页(前后)看看是否可以将这两个页合并以优化空间使用。

MERGE_THRESHOLD:合并页的阈值,可以自己设置,在创建表或创建索引时指定

文字说明不够清晰明了,具体可以看视频里的PPT演示过程:https://www.bilibili.com/video/BV1Kr4y1i7ru?p=90

主键设计原则:

  • 满足业务需求的情况下,尽量降低主键的长度
  • 插入数据时,尽量选择顺序插入,选择使用 AUTO_INCREMENT 自增主键
  • 尽量不要使用 UUID 做主键或者是其他的自然主键,如身份证号
  • 业务操作时,避免对主键的修改

order by优化

  1. Using filesort:通过表的索引或全表扫描,读取满足条件的数据行,然后在排序缓冲区 sort buffer 中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序
  2. Using index:通过有序索引顺序扫描直接返回有序数据,这种情况即为 using index,不需要额外排序,操作效率高

如果order by字段全部使用升序排序或者降序排序,则都会走索引,但是如果一个字段升序排序,另一个字段降序排序,则不会走索引,explain的extra信息显示的是Using index, Using filesort,如果要优化掉Using filesort,则需要另外再创建一个索引,如:create index idx_user_age_phone_ad on tb_user(age asc, phone desc);,此时使用select id, age, phone from tb_user order by age asc, phone desc;会全部走索引

总结:

  • 根据排序字段建立合适的索引,多字段排序时,也遵循最左前缀法则
  • 尽量使用覆盖索引
  • 多字段排序,一个升序一个降序,此时需要注意联合索引在创建时的规则(ASC/DESC)
  • 如果不可避免出现filesort,大数据量排序时,可以适当增大排序缓冲区大小 sort_buffer_size(默认256k)

group by优化

  • 在分组操作时,可以通过索引来提高效率
  • 分组操作时,索引的使用也是满足最左前缀法则的

如索引为idx_user_pro_age_stat,则句式可以是select ... where profession order by age,这样也符合最左前缀法则

limit优化

常见的问题如limit 2000000, 10,此时需要 MySQL 排序前2000000条记录,但仅仅返回2000000 - 2000010的记录,其他记录丢弃,查询排序的代价非常大。
优化方案:一般分页查询时,通过创建覆盖索引能够比较好地提高性能,可以通过覆盖索引加子查询形式进行优化

例如:

1
2
3
4
5
6
7
8
-- 此语句耗时很长
select * from tb_sku limit 9000000, 10;
-- 通过覆盖索引加快速度,直接通过主键索引进行排序及查询
select id from tb_sku order by id limit 9000000, 10;
-- 下面的语句是错误的,因为 MySQL 不支持 in 里面使用 limit
-- select * from tb_sku where id in (select id from tb_sku order by id limit 9000000, 10);
-- 通过连表查询即可实现第一句的效果,并且能达到第二句的速度
select * from tb_sku as s, (select id from tb_sku order by id limit 9000000, 10) as a where s.id = a.id;

count优化

MyISAM 引擎把一个表的总行数存在了磁盘上,因此执行 count(*) 的时候会直接返回这个数,效率很高(前提是不适用where);
InnoDB 在执行 count(*) 时,需要把数据一行一行地从引擎里面读出来,然后累计计数。
优化方案:自己计数,如创建key-value表存储在内存或硬盘,或者是用redis

count的几种用法:

  • 如果count函数的参数(count里面写的那个字段)不是NULL(字段值不为NULL),累计值就加一,最后返回累计值
  • 用法:count(*)、count(主键)、count(字段)、count(1)
  • count(主键)跟count(*)一样,因为主键不能为空;count(字段)只计算字段值不为NULL的行;count(1)引擎会为每行添加一个1,然后就count这个1,返回结果也跟count(*)一样;count(null)返回0

各种用法的性能:

  • count(主键):InnoDB引擎会遍历整张表,把每行的主键id值都取出来,返回给服务层,服务层拿到主键后,直接按行进行累加(主键不可能为空)
  • count(字段):没有not null约束的话,InnoDB引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,服务层判断是否为null,不为null,计数累加;有not null约束的话,InnoDB引擎会遍历整张表把每一行的字段值都取出来,返回给服务层,直接按行进行累加
  • count(1):InnoDB 引擎遍历整张表,但不取值。服务层对于返回的每一层,放一个数字 1 进去,直接按行进行累加
  • count(*):InnoDB 引擎并不会把全部字段取出来,而是专门做了优化,不取值,服务层直接按行进行累加

按效率排序:count(字段) < count(主键) < count(1) < count(*),所以尽量使用 count(*)

update优化(避免行锁升级为表锁)

InnoDB 的行锁是针对索引加的锁,不是针对记录加的锁,并且该索引不能失效,否则会从行锁升级为表锁。

如以下两条语句:
update student set no = '123' where id = 1;,这句由于id有主键索引,所以只会锁这一行;
update student set no = '123' where name = 'test';,这句由于name没有索引,所以会把整张表都锁住进行数据更新,解决方法是给name字段添加索引

总结

image-20220525114408704

数据类型

整型

类型名称 取值范围 大小
TINYINT -128〜127 1个字节
SMALLINT -32768〜32767 2个宇节
MEDIUMINT -8388608〜8388607 3个字节
INT (INTEGHR) -2147483648〜2147483647 4个字节
BIGINT -9223372036854775808〜9223372036854775807 8个字节

无符号在数据类型后加 unsigned 关键字。

浮点型

类型名称 说明 存储需求
FLOAT 单精度浮点数 4 个字节
DOUBLE 双精度浮点数 8 个字节
DECIMAL (M, D),DEC 压缩的“严格”定点数 M+2 个字节

日期和时间

类型名称 日期格式 日期范围 存储需求
YEAR YYYY 1901 ~ 2155 1 个字节
TIME HH:MM:SS -838:59:59 ~ 838:59:59 3 个字节
DATE YYYY-MM-DD 1000-01-01 ~ 9999-12-3 3 个字节
DATETIME YYYY-MM-DD HH:MM:SS 1000-01-01 00:00:00 ~ 9999-12-31 23:59:59 8 个字节
TIMESTAMP YYYY-MM-DD HH:MM:SS 1980-01-01 00:00:01 UTC ~ 2040-01-19 03:14:07 UTC 4 个字节

字符串

类型名称 说明 存储需求
CHAR(M) 固定长度非二进制字符串 M 字节,1<=M<=255
VARCHAR(M) 变长非二进制字符串 L+1字节,在此,L< = M和 1<=M<=255
TINYTEXT 非常小的非二进制字符串 L+1字节,在此,L<2^8
TEXT 小的非二进制字符串 L+2字节,在此,L<2^16
MEDIUMTEXT 中等大小的非二进制字符串 L+3字节,在此,L<2^24
LONGTEXT 大的非二进制字符串 L+4字节,在此,L<2^32
ENUM 枚举类型,只能有一个枚举字符串值 1或2个字节,取决于枚举值的数目 (最大值为65535)
SET 一个设置,字符串对象可以有零个或 多个SET成员 1、2、3、4或8个字节,取决于集合 成员的数量(最多64个成员)

二进制类型

类型名称 说明 存储需求
BIT(M) 位字段类型 大约 (M+7)/8 字节
BINARY(M) 固定长度二进制字符串 M 字节
VARBINARY (M) 可变长度二进制字符串 M+1 字节
TINYBLOB (M) 非常小的BLOB L+1 字节,在此,L<2^8
BLOB (M) 小 BLOB L+2 字节,在此,L<2^16
MEDIUMBLOB (M) 中等大小的BLOB L+3 字节,在此,L<2^24
LONGBLOB (M) 非常大的BLOB L+4 字节,在此,L<2^32

权限一览表

具体权限的作用详见官方文档

GRANT 和 REVOKE 允许的静态权限

Privilege Grant Table Column Context
ALL [PRIVILEGES] Synonym for “all privileges” Server administration
ALTER Alter_priv Tables
ALTER ROUTINE Alter_routine_priv Stored routines
CREATE Create_priv Databases, tables, or indexes
CREATE ROLE Create_role_priv Server administration
CREATE ROUTINE Create_routine_priv Stored routines
CREATE TABLESPACE Create_tablespace_priv Server administration
CREATE TEMPORARY TABLES Create_tmp_table_priv Tables
CREATE USER Create_user_priv Server administration
CREATE VIEW Create_view_priv Views
DELETE Delete_priv Tables
DROP Drop_priv Databases, tables, or views
DROP ROLE Drop_role_priv Server administration
EVENT Event_priv Databases
EXECUTE Execute_priv Stored routines
FILE File_priv File access on server host
GRANT OPTION Grant_priv Databases, tables, or stored routines
INDEX Index_priv Tables
INSERT Insert_priv Tables or columns
LOCK TABLES Lock_tables_priv Databases
PROCESS Process_priv Server administration
PROXY See proxies_priv table Server administration
REFERENCES References_priv Databases or tables
RELOAD Reload_priv Server administration
REPLICATION CLIENT Repl_client_priv Server administration
REPLICATION SLAVE Repl_slave_priv Server administration
SELECT Select_priv Tables or columns
SHOW DATABASES Show_db_priv Server administration
SHOW VIEW Show_view_priv Views
SHUTDOWN Shutdown_priv Server administration
SUPER Super_priv Server administration
TRIGGER Trigger_priv Tables
UPDATE Update_priv Tables or columns
USAGE Synonym for “no privileges” Server administration

GRANT 和 REVOKE 允许的动态权限

Privilege Context
APPLICATION_PASSWORD_ADMIN Dual password administration
AUDIT_ABORT_EXEMPT Allow queries blocked by audit log filter
AUDIT_ADMIN Audit log administration
AUTHENTICATION_POLICY_ADMIN Authentication administration
BACKUP_ADMIN Backup administration
BINLOG_ADMIN Backup and Replication administration
BINLOG_ENCRYPTION_ADMIN Backup and Replication administration
CLONE_ADMIN Clone administration
CONNECTION_ADMIN Server administration
ENCRYPTION_KEY_ADMIN Server administration
FIREWALL_ADMIN Firewall administration
FIREWALL_EXEMPT Firewall administration
FIREWALL_USER Firewall administration
FLUSH_OPTIMIZER_COSTS Server administration
FLUSH_STATUS Server administration
FLUSH_TABLES Server administration
FLUSH_USER_RESOURCES Server administration
GROUP_REPLICATION_ADMIN Replication administration
GROUP_REPLICATION_STREAM Replication administration
INNODB_REDO_LOG_ARCHIVE Redo log archiving administration
NDB_STORED_USER NDB Cluster
PASSWORDLESS_USER_ADMIN Authentication administration
PERSIST_RO_VARIABLES_ADMIN Server administration
REPLICATION_APPLIER PRIVILEGE_CHECKS_USER for a replication channel
REPLICATION_SLAVE_ADMIN Replication administration
RESOURCE_GROUP_ADMIN Resource group administration
RESOURCE_GROUP_USER Resource group administration
ROLE_ADMIN Server administration
SESSION_VARIABLES_ADMIN Server administration
SET_USER_ID Server administration
SHOW_ROUTINE Server administration
SYSTEM_USER Server administration
SYSTEM_VARIABLES_ADMIN Server administration
TABLE_ENCRYPTION_ADMIN Server administration
VERSION_TOKEN_ADMIN Server administration
XA_RECOVER_ADMIN Server administration

图形化界面工具

安装

小技巧

  1. 在SQL语句之后加上\G会将结果的表格形式转换成行文本形式
  2. 查看Mysql数据库占用空间:
    1
    2
    3
    4
    SELECT table_schema "Database Name"
    , SUM(data_length + index_length) / (1024 * 1024) "Database Size in MB"
    FROM information_schema.TABLES
    GROUP BY table_schema;

常用算法复杂度速查表 - SegmentFault 思否

绪论

算法:是为了解决某类问题而规定的一个有限长的操作序列。

五个特性:有穷性、确定性、可行性、输入、输出

评价算法优劣的基本标准(4个):

正确性、可读性、健壮性、高效性及低存储量

​ 常见的算法时间复杂度由小到大依次为:image-20220316143749407

img

常见时间复杂度

image-20220322201328442
image-20220322201337562

image-20220322201401167

image-20220322201522419

image-20220429151146516)、image-20220429151349939

栈和队列

前,中,后缀表达式

添加到第(N+1)个,移动平均:(0+1+2+……+N)/(N+1)=N/2

删除第N个,移动平均:[0+1+……+(N-1)]/N=(N-1)/2

环形队列

1、front:指向队列的第一个元素,即array[front]就是队列的第一个元素,front的初始值是0;

2、rear:指向队列的最后一个元素的后一个位置,空出一个空间做为约定(实际存储数据会比最大容量小1),rear的初始值是0;

3、maxSize:队列的最大容量;

4、队列满的条件是:(rear+1)%maxSize == front ;

5、队列为空的条件:rear == front;

6、队列中的有效数据个数:(rear + maxSize - front)% maxSize

数组和广义表

在这里插入图片描述

image-20220406173800225

image-20220324201019506

矩阵(稀疏矩阵)压缩存储(3种方式) (biancheng.net)

十字链表

这里写图片描述

子串长度:n(n+1)/2 + 1

树和二叉树

树的转换

image-20220325180611435

二叉树转换为树:

image-20220325180626855

森林转换为二叉树

image-20220325180705550

二叉树转换为森林

image-20220325180720205

二叉树的性质

image-20220325180202645

性质1:树中的结点数等于所有结点的度数加1

每个结点的度数分别对应结点的子结点,所有结点的度数为b,因此结点数n = b +根节点,即n = b + 1.

性质2:度为m的树中第i层上至多有m的i-1次方个结点(i>=1)

本性质采用数学归纳法进行证明:
1)当树为第一层时,第一层至多有m0=1个结点
2)当树为第i-1层时,由该性质值第i-1层至多有mi-2个结点
3)当树为第i层时,由于树的度为m,可知每个第i-1层的每个结点至多有m个子结点,因此第i层至多有mi-2*m=mi-1个结点。
因此性质得证。

性质3:高度为h的m叉树至多有(m^h-1)/(m-1)个结点

由性质2可知,度为m的数第i层至多有mi-1个结点,因此高度为h的m叉树至多有n = m0+m1+…+mh-1个结点,用等比数列公式求和,可以得到n= (mh-1)/(m-1).

性质4:具有n个结点的m叉树的最小高度为⌈logm(n(m-1)+1)⌉

性质4是由性质3逆推得到,由得到最小高度可知树的每一层都具有最多结点。由于高度为整数,且多余结点也是一层,因此树的高度需要向上取整。

其他总结

二叉树中n0=n2+1

满二叉树n,高度为 log2(n+1)

n节点,空指针域n+1

n节点,分支树,或所有节点度之和为n-1

哈夫曼树

哈夫曼树中单节点分支为0,m为叶子结点数,节点总数为 2m-1

哈夫曼树权值计算

结点的带权路径长度:结点到树根之间的路径长度与该结点上权的乘积

image-20220325185844619

线索二叉树

image-20220329195904314

image-20220329195959739

image-20220329200326095

有向完全图:有n(n-1)条边的图,就是图中任意两点均有边相连的图。

完全图:有n(n-1)/2条边的图,就是图中任意两点均有边相连的图。

弧尾:边的始点称弧尾。

弧头:边的终点称弧头,即箭头的一端。

出度:顶点v的出度即是以该顶点为始点的边的数目。

入度:顶点v的入度即是以该顶点为终点的边的数目。

度:无向图中顶点的度就是关联于该顶点的边的数目。

强连通分量:有向图的极大强连通子图称为强连通分量。

强连通图:有向图中,若图中任意两个不同顶点都存在路径,则称该图为强连通图。

连通分量:无向图的极大连通子图称为它的连通分量。

图的遍历:从图中某一顶点出发,沿着一些边访遍图中所有的顶点,且使每个顶点仅被访问一次,就叫做图的遍历。

生成树:生成树是连通图的极小连通子图。所谓极小是指:若在树中任意增加一条边,则将出现一个回路;若去掉一条边,将会使之变成非连通图。

生成树的权:生成树各边的权值总和称为生成树的权。

最小生成树:权最小的生成树称为最小生成树。

有向无环图:一个无环的有向图称做有向无环图。

AOV网络:用顶点表示活动,用有向边<Vi,Vj>表示活动Vi必须先于活动Vj进行,即用弧表示活动间的优先关系。这种有向图叫做顶点表示活动的AOV网络。

AOE网:AOE网是一个带权的有向无环图,其中,顶点表示事件,弧表示活动,权表示活动持续的时间。

关键路径:从源点到汇点的最长路径长度叫做关键路径。

关键活动:关键路径上的所有活动都是关键活动。

最短路径:即求两个顶点间长度最短的路径,但不是指路径上边数的总和,而是指路径上各边权值的总和。

存储结构

img

常见算法时间复杂度

DFS

深度优先搜索(Depth_Fisrst Search)遍历类似于树的先根遍历,是树的先根遍历的推广。

实质上是对每个顶点查找其邻接点的过程,其耗费的时间取决于所采用结构。

显然,这是一个递归的过程。为了在遍历过程中便于区分顶点是否已被访问,需附设访问标志数组visited[0:n-1], ,其初值为FALSE ,一旦某个顶点被访问,则其相应的分量置为TRUE。

从图的某一点v 出发,递归地进行深度优先遍历的过程如下:

1
2
3
4
5
6
7
void DFS(Graph G,int v )
{ /*从第v 个顶点出发递归地深度优先遍历图G*/
visited[v]=TRUE;VisitFunc(v); /*访问第v 个顶点*/
for(w=FisrAdjVex(G,v);w; w=NextAdjVex(G,v,w))
if (!visited[w])
DFS(G,w); /*对v 的尚未访问的邻接顶点w 递归调用DFS*/
}

邻接表表示时,查找所有顶点的邻接点所需时间为O(E),访问顶点的邻接点所花时间为O(V),此时,总的时间复杂度O(V+E)

邻接矩阵表示时,查找每个顶点的邻接点所需时间为O(V),要查找整个矩阵,故总的时间度为O(V^2)

v为图的顶点数,E为边数

BFS

遍历顶点的每个相邻边的时间复杂度称为O(N),其中N是相邻边的数量。因此,对于V个顶点,时间复杂度变为O(V*N)=
O(E),其中E是图形中边的总数。由于是从Queue中删除顶点或向Queue中添加顶点O(1),因此为什么将顶点添加到BFS的整体时间复杂度中O(V+E)

图的基本术语

最小生成树算法Prim,Kruskal

Prim算法的时间复杂度为O(n^2)

Kruskal的算法复杂度为O(eloge)

image-20220326152812589

image-20220326152937138

image-20220326153042843

普里姆Prim

image-20220326153313684

image-20220326153941317

克鲁斯卡尔Kruskal

image-20220326153606844

image-20220326154033035

最短路径Dijkstra,Flody

image-20220326154215555

Dijkstra

​ 时间复杂度为:O(n^2)

image-20220326155346561

image-20220326155544368

Flody

拓扑排序AOV网

​ n个顶点,e条边的有向无环图过不排序时间复杂度为 O(n+e)

image-20220303143113175

image-20220303143023873

  1. 找到无入度的点
  2. 任选其中一点加入排序队列

关键路径AOE网

image-20220326152608882

活动余量为0则为关键

在这里插入图片描述

查找

时间复杂度

  1. 顺序查找法的平均查找长度为:(n+1)/2

image-20220410161254189

关于ASL(平均查找长度)的简单总结

B树

对于关键字为n,高度为h,阶数为m的B数,最小高度为:

image-20220315164829954

image-20220315165936276

image-20220311151158567

image-20220311153112186

B+树

image-20220311155504952

image-20220311155901265

二叉排序树BFS

image-20220315143446548

image-20220315143732507

二叉排序树删除节点的几种方法

1:删除节点左子树的最右边的元素替代之,相当于用前继节点替代

2:删除节点右子树的最左边的元素替代之,相当于用后继节点替代

以上两种都不改变中序遍历二叉树所得的顺序

3:设要删除的节点是B,节点B是节点A的左子树。删除节点B以后,令B的左子树为A的左子树,B的右子树加到B的左子树的最右边。

4:http://www.cppblog.com/guogangj/archive/2009/10/26/99502.html,假设要删除节点A,则(考虑节点A的左子树,找出左子树中最大的节点并与A替换,若此时A为叶子节点,则直接删除,否则重复括号内的过程)。

image-20220315150720753

二叉平衡树AVL

image-20220315151154407

image-20220503150212253

散列

二次探测

如何用二次探测法处理散列冲突

排序

排序算法

创建二叉排序树的时间复杂度是O(nlog2n)

image-20220308113351939

各种常用排序算法
类别 排序方法 时间复杂度 空间复杂度 稳定性 复杂性 特点
最好 平均 最坏 辅助存储 简单
插入排序 直接插入 O(N) O(N2) O(N2) O(1) 稳定 简单
希尔排序 O(N) O(N1.3) O(N2) O(1) 不稳定 复杂
选择排序 直接选择 O(N) O(N2) O(N2) O(1) 不稳定
堆排序 O(N*log2N) O(N*log2N) O(N*log2N) O(1) 不稳定 复杂
交换排序 冒泡排序 O(N) O(N2) O(N2) O(1) 稳定 简单 1、冒泡排序是一种用时间换空间的排序方法,n小时好2、最坏情况是把顺序的排列变成逆序,或者把逆序的数列变成顺序,最差时间复杂度O(N^2)只是表示其操作次数的数量级3、最好的情况是数据本来就有序,复杂度为O(n)
快速排序 O(N*log2N) O(N*log2N) O(N2) O(log2n)~O(n) 不稳定 复杂 1、n大时好,快速排序比较占用内存,内存随n的增大而增大,但却是效率高不稳定的排序算法。2、划分之后一边是一个,一边是n-1个,这种极端情况的时间复杂度就是O(N^2)3、最好的情况是每次都能均匀的划分序列,O(N*log2N)
归并排序 O(N*log2N) O(N*log2N) O(N*log2N) O(n) 稳定 复杂 1、n大时好,归并比较占用内存,内存随n的增大而增大,但却是效率高且稳定的排序算法。
基数排序 O(d(r+n)) O(d(r+n)) O(d(r+n)) O(rd+n) 稳定 复杂
注:r代表关键字基数,d代表长度,n代表关键字个数

image-20220312112056055

img

堆排序

image-20220314170311713

img

当下沉的子节点值想等时,往左边下沉

错题总结

下标运算

循环队列对头尾不熟悉,

数据结构的多种实现

查漏补缺

  • 排序算法
  • AVL树
  • 二叉树和森林转换
  • 根据遍历结果还原树
  • 多存储结构的实现
  • 哈夫曼树权值计算
  • 常见算法实现和时间复杂度
  • 图的一些概念
  • 有序链表倒置算法
  • AOE网
  • 图遍历
  • 概念知识
  • 查找算法实现细节
  • 折半查找插入
  • 树的兄弟链存储遍历
  • 前中后缀表达式
  • 算法分析
  • 十字链表
  • 排序算法的实现
  • 快速排序、堆排序、归并排序和基数排序实现细节
  • 拓扑排序细节
  • 简答题
  • 算法题
  • 链式基数排序
  • B树

错题总结

选择题

image-20220419171919318

image-20220419173614764

image-20220419173800940

image-20220419173900928

image-20220419182416393

image-20220419182436191

image-20220419182500767

image-20220419183224000

image-20220419183347050

image-20220419183508163

image-20220419183524997

image-20220419183820495

image-20220420154554738

image-20220423155734018

image-20220424151023382

image-20220427170851058

image-20220428152547920

image-20220429151747367

image-20220429152438882

image-20220430152535881

image-20220430152543162

image-20220430152526461

image-20220501151321133

image-20220501151201526

image-20220501151216649

image-20220501151310233

image-20220501153946878

image-20220501154128087

image-20220502143045905

image-20220502143115045

image-20220507183353878

image-20220503141814188

image-20220503153447157

image-20220503155007210

image-20220503170245692

image-20220504153636450

image-20220508143710735

image-20220510151230840

image-20220510164314683

判断题

image-20220420161514256

image-20220420161521022

image-20220420172714974

image-20220426210425048

image-20220426210529001

image-20220430161052722

image-20220508145542178

填空题

image-20220419175030402

image-20220419175057666

image-20220419175110388

image-20220419175353242

image-20220419180206090

image-20220419180634952

image-20220419183021218

image-20220420163912980

image-20220423153014808

image-20220423153104625

image-20220423160756011

image-20220424153445884

image-20220424161617533

image-20220424161652516

image-20220424161953062

image-20220425151817333

image-20220425152517224

image-20220425153544391)image-20220425154520963)image-20220425154620266

image-20220426205821295

image-20220426205827791

image-20220426211024450

image-20220426211309692

image-20220426211603514

image-20220430152559107

image-20220502141422559

image-20220508152649702

image-20220508152657694

image-20220509163553255

image-20220510154329258

大题

image-20220505155210139

image-20220510155053510

【一棵度为2和一棵二叉树有什么区别】

1、度不同

度为2的树要求每个节点最多只能有两棵子树,并且至少有一个节点有两棵子树。二叉树的要求是度不超过2,节点最多有两个叉,可以是1或者0。

在任意一棵二叉树中,叶子结点总是比度为2的结点多一个。

2、分支不同

度为2的树有两个分支,但分支没有左右之分;
一棵二叉树也有两个分支,但有左右之分,左右子树的次序不能随意颠倒。

3、次序不同

度为2的树从形式上看与二叉树很相似,但它的子树是无序的,而二叉树是有序的。即,在一般树中若某结点只有一个孩子,就无需区分其左右次序,而在二叉树中即使是一个孩子也有左右之分。

  1. image-20220323180455658

image-20220319170015052

image-20220331145111010

image-20220331151804815

image-20220425153024587

算法题总结

image-20220408170943155

image-20220410144553917

image-20220410144912197

image-20220410144917819

image-20220410162632281

image-20220410162651135

image-20220411164317056

image-20220414154755866

image-20220419174928388

image-20220419183140950