MySQL

MySQL

基础

MySQL安装-windows

下载

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

安装

  • 双击官方下来的安装包文件
  • 根据安装提示进行安装

接下几步NEXT之后,如下设置

设置root密码

配置

安装好MySQL之后,还需要配置环境变量,这样才可以在任何目录下连接MySQL。

1). 在此电脑上,右键选择属性

2). 点击左侧的 "高级系统设置",选择环境变量

3). 找到 Path 系统变量, 点击 "编辑"

4). 选择 "新建" , 将MySQL Server的安装目录下的bin目录添加到环境变量

  • 连接数据库
mysql -u root -p

SQL通用语法

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

SQL分类

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

DDL

DDL-数据库操作
  • 查询
查询所用数据库
SHOW DATABASES;
查询当前数据库
SELECT DATABASE();
  • 创建
CREATE DATABASE [NOT EXISTS] 数据库名 [DEFAULT CHARSET 字符集] [COLLATA 排序规则];     #[]表示可选
  • 删除
DROP DATABASE [IF EXISTS] 数据库名;     #[]表示可选
  • 使用
USE 数据库名;   #在操作某数据库之前需要确定当前数据库(SELECT DATABASE();)是否是想要操作的数据库,可使用数据库切换9(use 数据库名)到另一个数据库;
DDL-表操作
查询
  • 查询当前数据库所有表
SHOW TABLAES;
  • 查询表结构
DESC 表名;
  • 查询指定表的建表语句
SHOW CREATE TABLE 表名;
创建
CREATE TABLE 表名(
    字段1 字段类型 [COMMENT 字段1注释],
    字段2 字段类型 [COMMENT 字段2注释],
    字段3 字段类型 [COMMENT 字段3注释],
    字段4 字段类型 [COMMENT 字段4注释]
) COMMENT 表注释;  #最后一个字段没有逗号

eg:
CREATE TABLE tb_user(
    id int comment '编号',
    name varchar(50) comment '姓名',
    age int comment '年龄',
    gender avrchar(1) comment '性别'
) comment '用户信息';
数据类型

MySQL中的数据类型主要分为三类:数值类型、字符串类型、日期时间类型。

  • 数值类型
类型 大小 有符号范围(signed) 无符号范围(unsigned) 描述
TINYINT 1 bytes (-128,127) (0,255) 小整数值
SMALLINT 2 (-32768,32767) (0,65535) 大整数值
MEDIUMINT 3 (-8388608,8388607) (0,16777215) 大整数值
INT 或 INTEGER 4 (-2147483648, 2147483647) (0,16777215) 大整数值
BIGINT 8 (-2^63, 2^63 -1) (0, 2^64-1) 极大整数值
FLOAT 4 (-3. 403..., 3. 4028..) 0和(1.175.., 3. 4028...) 单精度浮点数值
DOUBLE 8 (-1.797...,1.7976...) 0和(2.225....,1.7976....) 单精度浮点数值
DECIMAL 依赖于M(精度,位数)和D(标度,小数点数)的值 依赖于M(精度,位数)和D(标度,小数点后位数)的值 小数值(精确定点数)
  • 字符串类型
CHAR 0-255 bytes 定长字符串
VARCHAR 0-65535 bytes 变长字符串
TINYBLOB 0-255 bytes 不超过255个字符的二进制数据
TINYTEXT 0-255 bytes 短文本字符串
BLOB 0-65535 bytes 二进制形式的长文本数据
TEXT 0-65535 bytes 长文本数据
MEDIUMBLOB 0-16777215 bytes 二进制形式的中等长度文本数据
MEDIUMTEXT 0-16 777 215 bytes 中等长度文本数据
LONGBLOB 0-4 294 967 295 bytes 二进制形式的极大文本数据
LONGTEXT 0-4 294 967 295 byte 极大文本数据
  • 日期类型
DATE 3 1000-01-01 至 9999-12-31 YYYY-MM-DD 时间值
TIME 3 -838:59:59 至838:59:59 HH:MM: SS 时间值或持续时间
YEAR 1 1901至2155 YYYY 年份值
DATETIME 8 1000-01-01 00:00:00 至9999-12-31 23:59:59 YYYY-MM-DD HH:MM:SS 混合日期和时间值
TIMESTAMP 4 1970-01-01 00:00:01 至2038-01-19 03:14:07 YYYY-MM-DD HH:MM:SS 混合日期和时间值,时间戳
修改
  • 添加字段

    ALTER TABLE 表名 ADD 字段名 类型(长度) [COMMENT 注释] [约束];
    
    eg:
    ALTER TABLE emp ADD nickname varchar(20) COMMENT '昵称'; 
    
  • 修改数据类型

ALTER TABLE 表名 MODIFY 字段名  新数据类型(长度);

修改字段名和字段类型
ALTER TABLE 表名 CHANGE 旧字段名 新字段名  类型(长度) [COMMENT 注释] [约束];

eg:
将emp表的nickname字段修改为username,类型为varchar(30)
ALTER TABLE emp CHANGE nickname username varchar(30) COMMENT '用户名';

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

eg:
将emp表的字段username删除
ALTER TABLE emp DROP username;

修改表名
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, ...);
INSERT INTO 表名 VALUES 
(值1,值2, ...),
(值1,值2, ...),
(值1,值2, ...),
(值1,值2, ...),
(值1,值2, ...),
(值1,值2, ...);

#插入数据时,指定的字段顺序需要与顺序是一一对应的。
#字符串和日期型数据应该包含在引号中。
#插入的数据大小,应该在字段的规定范围内。
修改数据
UPDATE 表名 SET 字段名1 = 值1, 字段名2 = 值2, ...[WHERE 条件];  #修改语句的条件可有可无,如果没有条件,则会修改整张表的所有数据
删除数据
DELETE FROM 表名 [WHERE 条件]; 
#delete 语句的条件可有可无,如果没有则删除整张表的数据
#delete 语句不能删除某一个字段的值(可以用updata修改为null)

DQL

  • DQL英文全称是Data Query Language(数据查询语言),数据查询语言,用来查询数据库中表的记录。
  • 查询关键字:SELECT
基本查询
  • 查询多个字段
SELECT 字段1,字段2, ... FROM 表名;

eg:
#查询指定字段,name,workon,age 
select name,workon,age from emp;

SELECT * FROM 表名;

eg:
#查询所有字段
select id,workon,name,gender,age,idcard,wordaddress,entrydate from emp;

select * from emp;
  • 设置别名
SELECT 字段1 [AS 别名1] , 字段2 [AS 别名2] ... FROM 表名;

eg:
#查询所有员工的工作地址,起别名
select workaddress as '工作地址' from emp;
select workaddress '工作地址' from emp;
  • 去除重复记录
SELECT DISTINCT 字段列表 FROM 表名;

eg:
select distinct workaddress '工作地址' from emp;
条件查询
  • 语法
SELECT 字段列表 FROM 表名 WHERE 条件列表;
  • 条件
比较运算符 功能
> 大于
>= 大于等于
< 小于
<= 小于等于
= 等于
<> 或 != 不等于
BETWEEN ... AND ... 在某个范围之内(含最小,最大值)
IN( ... ) 在in之后的列表中的值,多选一
LIKE 占位符 模糊匹配(_ 匹配单个字符,%匹配任意个字符)
IS NULL 是 null
AND 或 && 并且(多个条件同时成立)
OR 或 || 或者(多个条件中的任意一个成立)
NOT 或 ! 非,不是
#查询年龄等于 88 的员工
select * from emp where age = 88;

#查询年龄小于 20 的员工
select * from emp where age < 20;

#查询年龄小于等于 20 的员工
select * from emp where age <= 20;

#查询没有身份证号的员工信息
select * from emp where idcard is null;

#查询有身份证号的员工信息
select * from emp where idcard is not null;

#查询年龄不等于 88 的员工信息
select * from emp where age != 88;
selecrt * from emp where age <> 88;

#查询年龄在15岁(包含)到20岁(包含)之间的员工信息
select * from emp where age >= 15 && age <= 20;
select * from emp where age >= 15 and age <= 20;
select * from emp where age between 15 and 20;

#查询性别为 女 且年龄小于 25 岁的员工信息

select *  from emp where gender = '女' and age < 25;

#查询年龄等于18 或 20 或 40 的员工信息
select * from emp where age = 18 or age = 20 or age 40;
select * from emp where age in (18,20,40);

#查询姓名为两个字的员工信息
select * from emp where name like '__';

#查询身份证号最后一位为X的员工信息
select * from emp where name like '%X';

聚合函数
  • 常见聚合函数
函数 功能
count 统计数量
max 最大值
min 最小值
avg 平均值
sum 求和
  • 语法
SELECT 聚合函数(字段列表) FROM 表名;

eg:
#统计该企业员工数量
select count(*) from emp;    # null值不计算在内

#统计该企业员工的平均年龄
select avg(age) from emp;

#统计该企业员工的最大年龄
select max(age) from emp;

#统计该企业员工的最小年龄
select min(age) from emp;

#统计西安地区员工的年龄之和
select sum(age) from emp where workaddress = '西安';
分组查询
  • 语法
SELECT 字段列表 FROM 表名 [WHERE 条件] GROUP BY 分组字段名 [HAVING 分组后过滤条件];

eg:
#根据性别分组,统计男性员工和女性员工的数量
select gender,count(*) from emp group by gender;

#根据性别分组,统计男性员工和女性员工的平均年龄
select gender,avg(age) from emp group by gender;

#查询年龄小于45的员工,并根据工作地址分组,获取员工数量大于等于3的工作地址
select workaddress,count(*)  adddress_count from emp where age < 45 group by workaddress having address_count >= 3;


#执行顺序:where > 聚合函数 > having
#分组之后,查询的字段一般为聚合函数和分组字段,查询其他字段无意义
  • where 与having区别
    • 执行时机不同:where是分组之前进行过滤,不满足where条件,不参与分组;而having是分组之后对结果进行过滤。
    • 判断条件不同:where不能对聚合函数进行判断,而having可以。
排序查询
  • 语法
SELECT 字段列表 FROM 表名 ORDER BY 字段1 排序方式1,字段2 排序方式2;

eg:
#1.根据年龄对公司的员工进行升序排序
select * from emp order by age asc;  #升序
select * from emp order by age desc;  #降序

select * from emp order by age;   #默认升序,可省略

#2.根据入职时间, 对员工进行降序排序
select * from emp order by entrydate desc;

#3.根据年龄对公司的员工进行升序排序,年龄相同,再按照入职时间进行降序排序
select * from emp order by age asc, entrydate desc;

  • 排序方式
    • ASC:升序(默认值)
    • DESC:降序 #如果是多字段排序,当第一个字段值相同时,才会根据第二字段进行排序
分页查询
  • 语法
SELECT 字段列表 FROM 表名 LIMIT 起始索引,查询记录数;

#查询第 1 页员工数据,每页展示10条记录--------> 起始索引 = (2-1)* 10
select * from emp limit 10,10;
/*起始索引从0开始,起始索引 = (查询页码 - 1) * 每页显示记录数.
分页查询是数据的方言,不同的数据库有不同的实现,MySQL中是LIMIT.
如果查询的是第一页数据,起始索引可以省略,直接简写成 limit 10.*/
案例
# 1.查询年龄为20,21, 22,23岁的女性员工信息。
select * from emp where gender = '女' and age in (20,21,22,23);

#2.查询性别为男,并且年龄在20-40 岁(含)以内的姓名为三个字的员工
select * from emp where gender = '男' and age between 20 and 40 and name like '___';

#3.统计员工表中,年龄小于60岁的,男性员工和女性员工的人数。
select gender count(*) from emp where age < 60 group by gender;

#4.查询所有年龄小于等于35岁员工的姓名和年龄,并对查询结果按年龄升序排序,如果年龄相同按入职时间降序排序。
select name,age from emp where age <= 35 order by age asc , entrydate desc;

#5.查询性别为男,且年龄在20-40 岁(含)以内的前5个员工信息,对查询的结果按年龄升序排序,年龄相同按入职时间升序排序。
select * from emp where gender = '男' and age between 20-40 order by age asc , entrydate asc limit 5;  #limit 一般在最后

执行顺序

from --> where --> group by --> having --> select --> order by --> limit 

DCL

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

管理用户
  • 查询用户
USE mysql;
SELECT * FROM user;
  • 创建用户
CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码';

eg:
#创建用户itcast ,只能够在当前主机Localhost访问,密码123456;
create user 'itcast'@'localhost' identified by '123456';

#创建用户heya ,可以在任意主机访问该数据库,密码123456 ;
create user 'heya'@'%' identified by '123456';
  • 修改用户密码
ALTER USER '用户名'@'主机名' IDENTIFIED WITH mysql_native_password BY '新密码'

eg:
#修改用户heya 的访问密码为1234 ;
alter user 'heya'@'%' identified with mysql_native_password by '1234';
  • 删除用户
DROP USER '用户名'@'主机名';

eg:
#删除itcast@LocaLhost用户
drop user 'itcast'@'localhost';

/*主机名可以使用%通配。
这类SQL开发人员操作的比较少,主要是DBA ( Database Administrator 数据库管理员)使用。*/
权限控制
权限 说明
ALL, ALL PRIVILEGES 所有权限
SELECT 查询数据
INSERT 插入数据
UPDATE 修改数据
DELETE 删除数据
ALTER 修改表
DROP 删除数据库/表/视图
CREATE 创建数据库/表
  • 查询权限
SHOW GRANTS FOR '用户名'@'主机名';

eg:
#查询权限
show grants for 'heya'@'%';
  • 授予权限
GRANT 权限列表 ON 数据库名.表名 TO '用户名'@'主机名';

eg:
#授予权限
grant all on itcast.* to 'heya'@'%';
  • 撤销权限
REVOKE 权限列表 ON 数据库名.表名 from '用户名'@'主机名';

eg:
#撤销权限
revoke all on itcast.* from 'heya'@'%';


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

函数

是指一段可以直接被另一段程序调用的程序或代码

字符串函数

MySQL中内置了很多字符串函数,常用的几个如下:

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

#concat ---->字符串拼接
select concat('Hello',' MYSQL');
#结果: Hello MYSQL

#lower ---->将字符串str全部转为小写
select lower('Hello');
#结果:hello

#upper ---->将字符串str全部转为大写
select upper('Hello');
#结果:HELLO

#lpad ---->左填充,用字符串pad对str的左边进行填充,达到n个字符串长度
select lpad('01', 5, '-');
#结果:---01

#rpad ---->右填充,用字符串pad对str的右边进行填充,达到n个字符串长度
select rpad('01', 5, '-');
#结果:01---

#trim ---->去掉字符串头部和尾部的空格
select trim('    Hello MYSQL   ');
#结果:Hello MySQL

#substring ---->返回从字符串str从start位置起的len个长度的字符串
select substring('Hello MySQL',1,5);
#结果:Hello


case:
#由于业务需求变更,企业员工的工号,统一为5位数, 目前不足5位数的全部在前面补0。比如: 1 号员工的工号应该为00001。
update emp set workon = lpad(workon,5,'0');

数值函数

常见的数值函数如下:

函数 功能
CEIL(X) 向上取整
FLOOR(X) 向下取整
MOD(X,Y) 返回x/y的模
RAND() 返回0~1内的随机数
ROUND(X,Y) 求参数x的四舍五入的值,保留y为小数
#ceil   向上取整
select ceil(1.1);
#结果:2

#floor  向下取整
select floor(1.9);
#结果:1

#mod   7/4的模  取余
select mod(7,4);
#结果:3

#rand   0~1内的随机数
select rand();
#结果:0.1564654

#round   参数2.344的四舍五入的值,保留2为小数
select round(2.344,2);
#结果:2.34

case:
#通过数据库的函数,生成一个六位数的随机验证码。
select rpad(round(rand()*1000000,0),6,'0');
/*
1.rand产生0~1内的随机数,乘以1000000得到整数部分为六位数
2.round将小数四舍五入转换为整数
3.rpad将随机产生的随机数(如0.019234)四舍五入不足六位(19234)的补足

日期函数

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

#curtime   返回当前时间
select curtime();

#now     返回当前日期和时间
select now();

#year    获取目前的年份
select year(now());    

#month   获取目前的月份
select month(now());

#day   获取目前的日期
select day(now());

#date_add   从目前加上8年后的日期时间
select date_add(now(),interval 8 year);

#datediff    两日期相隔天数   第一个日期减第二个
select datediff(now(),'2021-12-31');


case:
#查询所有员工的入职天数,并根据入职天数倒序排序。
select name,datediff(curdate(),entrydate) as 'entrydays' from emp order by entrydays desc;

流程函数

流程函数可在SQL语句中实现条件筛选,从而提高语句的效率

函数 功能
IF(value1,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默认值
#if   如果value为true,则返回t,否则返回f
select if(true,'ok','error');
#结果:ok

#ifnull  如果value1不为空,返回value1, 否则返回value2
select ifnull('ok','default');
#结果:ok

select ifnull('','default');
#结果:

select ifnull('null','default');
#结果:null

select ifnull(null,'default');
#结果:default

#case when then else end  如果expr的值   等于  val1,返回res1, .. 否则返回default默认值
#需求:查询emp表的员工姓名和工作地址(北京、上海 ——————>一线城市,其他 -------> 二线城市)
select
	name,
	(case workaddress when '北京' then '一线城市' when '上海' then '一线城市' else '二线城市' end) as '工作地址' from emp;
	
#case:统计班级各个学员的成绩,展示的规则如下:   >= 85 展示优秀, >= 60 展示及格, 否则展示不及格  (这里表示范围不能用  CASE [expr]  WHEN [val1] THEN [res1] ... ELSE [default] END 匹配)
select 
	id,
	name,
	(case when math >= 85 then '优秀' when math >= 60 then '及格' else '不及格' end) '数学',
	(case when english >= 85 then '优秀' when english >= 60 then '及格' else '不及格' end) '英语',
	(case when Chinese >= 85 then '优秀' when Chinese >= 60 then '及格' else '不及格' end) '语文'
from score;

约束

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

约束演示

  • 根据需求,完成表结构的创建
字段 字段含义 字段类型 约束条件 约束关键字
id id 唯一标识 int 主键,并且自动增长 PRIMARY KEY,AUTO_INCREMENT
name 姓名 varchar(10) 不为空,并且唯一 NOT NULL,UNIQUE
age 年龄 int 大于0,并且小于等于120 CHECK
status 状态 char(1) 如果没有指定该值,默认为1 DEFAULT
gender 性别 char(1)
create table user(
	id int primary key auto_increment comment '主键',
	name varchar(10) not null unique comment '年龄',
	age int check(age > 0 && age <= 120) comment '年龄',
	status char(1) default '1' comment '状态',
	gender char(1) comment '性别'
)comment '用户表';

insert into user(name,age,status,gender) values   #id自动增长,可不用设置
('tom1',18,'1','男'),
('tom2',19,'1','男');

外键约束

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

  • 语法

    • 添加外键
    CREATE TABLE 表名(
        字段名 数据类型,
        ...
        [CONSTRAINT] 外键名称 FOREIGN KEY(外键字段名) REFERENCES 主表(主表列名)
    );
    
    ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY(外键字段名) REFERENCES 主表(主列名);
    
    eg:
    alter table emp add constraint fk_emp_dept_id foreign key (dept_id) references dept(id);
    
    • 删除外键
    alter table 表名 drop foreign key 外键名称;
    
    eg:
    alter table emp drop foreign key fk_emp_dept_id;
    
  • 删除更新行为

行为 说明
NOT ACTION 当在父表中 删除/更新 对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新。(与 RESTRICT一致)
RESTRICT 当在父表中 删除/更新 对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除/更新。(与 NOT ACTION一致)
CASCADE 当在父表中 删除/更新 对应记录时,首先检查该记录是否有对应外键,如果有,则 也删除/更新外键在子表中的记录。
SET NULL 当在父表中删除对应记录时,首先检查该记录是否有对应外键,如果有则设置 子表中该外键值为 null (这就要求该外键允许取null)。
SET DEFAULT 父表有变更时,子表将外键列设置成-个默认的值(Innodb不支持)
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段) REFERENCES 主表名(主表字段名) ON UPDATE CASCADE ON DELETE CASCADE;

ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段) REFERENCES 主表名(主表字段名) ON UPDATE SET NULL ON DELETE SET NULL;

eg:
alter table add constraint fk_emp_dept_id foreign key (dept_id) references dept(id) on update cascade on delete cascade;

alter table add constraint fk_emp_dept_id foreign key (dept_id) references dept(id) on update cascade on delete cascade;

多表查询

多表关系

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

一对多(多对一)
  • 部门和员工的关系
  • 一个部门对应多个员工,一个员工对应一个部门
  • 实现:在多的一方建立外键,指向一的一方的主键
多对多
  • 学生与课程的关系
  • 一个学生可以选修多门课程,一门课程也可以供多个学生选择
  • 建立第三张中间表,中间表至少包含两个外键,分别关联两方主键
create table student(
    id int auto_increment primary key comment '主键',
    name varchar(10) comment '姓名',
    no varchar(10) comment '学号'
) comment '学生表' ;

insert into student values (null,'黛绮丝','2000100101'),(null,'易天真','2000100102'),(null,'张无忌','2000100103'),(null,'谢逊','2000100104'),(null,'周芷若','2000100105');

create table course(
    id int auto_increment primary key comment '主键ID',
    name varchar(10) comment '课程名称'
)  comment '课程表';

insert into course values (null,'Java'),(null,'PHP'),(null,'PYTHON'),(null,'MYSQL');

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

insert into student_course values (null,1,1),(null,1,2),(null,1,3),(null,2,2),(null,2,3),(null,3,4);


一对一
  • 用户与用户详情的关系
  • 一对一关系,多用于单表拆分,将一张表的基础字段放在一张表中,其他详情字段放在另一张表中,以提升操作效率
  • 实现:在任意一方加入外键,关联另一方的主键,并且设置外键为唯一的(UNIQUE)
create table tb_user(
    id int auto_increment primary key comment '主键ID',
    name varchar(10) comment '姓名',
    age int comment '年龄',
    gender char(1) comment '1: 男,2:女',
    phone char(11) comment '手机号'
) comment '用户基本信息表';


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

insert into tb_user(id, name, age, gender, phone) values
    (null,'黄渤', 45, '1', '1880001111'),
    (null, '冰冰', 35,'2', '1800002222'),
    (nulL, '码云', 55, '1', ' 18800008888'),
    (null, '李彦宏', 50,'1', '18800009999');
		
		
insert into tb_user_edu(id, degree, major, primaryschool, middLeschool, university, userid) values
	(null, '本科','舞蹈', '静安区第一小学','静安区第-中学','北京舞蹈学院' ,1),
	(null, '硕士','表演','朝阳区第 -小学','朝阳区第 一中学','北京电影学院',2),
	(null, '本科','英语','杭州市第一小学','杭州市第一中学','杭州师范大学' ,3),
	(nuLL, '本科','应用数学','阳泉第一小学','阳泉区第一中学','清华大学',4);

多表查询

  • 笛卡尔积是指在数学中,两个集合X和Y的笛卡尓积(Cartesian product),又称直积,表示为X × Y,第一个对象是X的成员而第二个对象是Y的所有可能有序对的其中一个成员。笛卡尔积又叫笛卡尔乘积,是一个叫笛卡尔的人提出来的。 简单的说就是两个集合相乘的结果。两个集合A集合和B集合的所有组合情况。(在多表查询时,需要消除无效的笛
    卡尔积),假设集合A={a, b},集合B={0, 1, 2},则两个集合的笛卡尔积为{(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}。
  • 消除笛卡尔积
SELECT  列名1, 列名2  FORM 表名1,表名2 WHERE 表名1.列名1=表名2.列名2

eg:
select * from emp,dept where emp.dept_id = dept.id;
分类
  • 连接查询

    • 内连接:相当于查询A、B交集部分数据

    • 外连接

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

  • 子查询

内连接
  • 语法 (内连接是交集部分)

    • 隐式内连接
    SELECT 字段列表 FROM 表1,表2 WHERE 条件 ...;
    
    eg:
    /*
    查询每个员工的姓名,及关联的部门的名称(隐式内连接实现)。
    表结构:emp,dept。 
    连接条件:emp.dept_id = dept.id
    */
    
    select emp.name,dept.name from emp,dept where emp.dept_id = dept.id;
    
    select e.name,d.neme from emp e,dept d where e.dept_id = d.id;
    
    • 显式内连接
    SELECT 字段列表 FROM 表1 [INNER] JOIN 表2 ON 连接条件 ...;
    
    eg:
    /*
    查询每个员工的姓名,及关联的部门的名称(显式内连接实现)。 ----- INNER JOIN ... ON ...
    表结构:emp,dept。 
    连接条件:emp.dept_id = dept.id
    */
    
    select emp.name, dept.name from emp inner join dept on emp.dept_id = dept.id;
    
    select e.name, d.name from emp e join dept d on e.dept_id = d.id;  #inner 可省略
    
外连接
  • 语法

    • 左外连接
    #相当于查询表1(左表)的所有数据 包含 表1和表2交集部分的数据
    SELECT 字段类别 FROM 表1 LEFT [OUTER] JOIN 表2 ON 条件 ...;
    
    eg:
    /*
    查询emp表的所有数据,和对应的部门信息(左外连接)
    表结构:emp,dept
    连接条件:emp.dept_id = dept.id
    */
    
    select e.*,d.name from emp e left outer join dept d on e.dept_id = d.id;
    
    select e.*,d.name from emp e left join dept d on e.dept_id = d.id; #outer 可省略
    
    
    • 右外连接
    #相当于查询表2(右表)的所有数据 包含 表1和表2交集部分的数据
    SELECT 字段类别 FROM 表1 RIGHT [OUTER] JOIN 表2 ON 条件 ...;
    
    eg:
    /*
    查询dept表的所有数据,和对应的员工信息(右外连接)
    表结构:emp,dept。 
    连接条件:emp.dept_id = dept.id
    */
    
    select d.*, e.* from emp e right join dept d on e.dept_id =  d.id;
    
    select d.*, e.* from  dept d left join emp e on e.dept_id =  d.id;
    
自连接
  • 语法
#自连接查询,可以是内连接,也可以是外连接查询

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

eg:
#查询员工 及其 所属领导的名字。表结构:emp

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

select a.name,b.name from emp a ioin  emp b on  a.managerid = b.id;

#查询所有员工 emp 及其领导的名字 emp ,如果员工没有领导,也需要查询出来,表结构:emp

select a.name '员工', b.name '领导' from emp a left join emp b on a.managerid = b.id;
联合查询

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

SELECT 字段列表 FROM 表A ...
UNION [all]
SELECT 字段列表 FROM 表B ...;

/*
对于联合查询的多张表的 列数 必须保持一致, 字段 类型 也需要保持一致。
union all会将全部的数据直接合并在一起,union 会对合并之后的数据去重。
*/

eg:
#将薪资低于 5000 的员工,和 年龄大于50岁的员工全部查询出来;(注意不是并且的关系)

select * from emp where salary < 5000   #union all会将全部的数据直接合并在一起
union all 
select * from emp where age > 50;

select * from emp where salary < 5000   #union 会对合并之后的数据去重。
union 
select * from emp where age > 50;

子查询

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

#子查询外部的语句可以是INSERT / UPDATE / DELETE / SELECT 的任何-一个。
SELECT * FROM t1 WHERE column1 = (SELECT column1 FROM t2);

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

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

根据子查询位置,分为: WHERE之后、FROM之后、SELECT 之后。

  • 标量子查询

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

#查询 '销售部' 的所有员工信息
-- a. 查询‘销售部’ 部门 的ID
select  id from dept where name = '销售部';

-- b. 根据销售部门ID,查询员工信息
select * from emp where dept_id = 4;

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

#查询 '房东白' 入职之后的员工信息
-- a. 查询'房东白' 的入职日期
select  entrydate from emp where name = '房东白';

-- b. 查询指定入职日期之后入职的员工信息
select * from emp where entrydate > (select  entrydate from emp where name = '房东白');
  • 列子查询

子查询返回的结果是一列(可以是多行) ,这种子查询称为列子查询。
常用的操作符:IN、NOT IN、ANY、SOME、ALL

操作符 描述
IN 在指定的集合范围之内,多选一
NOT IN 不在指定的集合范围之内
ANY 子查询返回列表中,有任意一个满足即可
SOME 与ANY等同,使用some的地方都可以使用any
ALL 子查询返回列表的所有值都必须满足
#查询"销售部”和“市场部”的所有员工信息
-- a. 查询‘销售部’ 和 ‘市场部’ 的所有员工信息
select id from dept where name = '销售部' or name '市场部';

-- b. 根据部门ID,查询员工信息
select * from emp where dept_id in (select id from dept where name = '销售部' or name '市场部');

#查询比财务部 所有 人工资都高的员工信息
-- a.查询所有财务部人员工资
select id from dept where name = '财务部';

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

-- b. 比财务部所有人工资都高的员工信息
select * from emp where salary > all (select salary from emp where dept_id = (select id from dept where name = '财务部'));

#查询比研发部其中 任意 一人工资高的员工信息
-- a.查询所有研发部人员工资
select id from dept where name = '研发部';

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

-- b. 比研发部所有人工资都高的员工信息
select * from emp where salary > any (select salary from emp where dept_id = (select id from dept where name = '研发部'));

select * from emp where salary > some (select salary from emp where dept_id = (select id from dept where name = '研发部'));

  • 行子查询

    子查询返回的结果是一行(可以是多列) ,这种子查询称为行子查询。
    常用的操作符:=、<>、IN、NOT IN

    # 查询与 ‘张无忌’的薪资及直属领导相同的员工信息
    -- a. 查询 ‘张无忌’的薪资及直属领导
    select salary,managerid from emp where name = '张无忌';
    
    -- b. 查询与 ‘张无忌’的薪资及直属领导相同的员工信息
    select * from emp where (salary,managerid) = (select salary,managerid from emp where name = '张无忌');
    
  • 表子查询

    子查询返回的结果是多行多列,这种子查询称为表子查询。
    常用的操作符: IN

    #1.查询与"鹿杖客”,"宋远桥”的职位和薪资相同的员工信息
    -- a.查询"鹿杖客","宋远桥”的职位和薪资
    select job,salary from emp where name = '鹿杖客' or '宋远桥';
    
    -- b.查询与“鹿杖客”,“宋远桥”的职位和薪资相同的员工信息
    select * from emp where (job,salary) in (select job,salary from emp where name = '鹿杖客' or '宋远桥');
    
    #2.查询入职日期是"2006-01-01" 之后的员工信息,及 其部门信息
    -- a.查询入职日期是"2006-01-01" 之后的员工信息
    select * from emp where entrydate > '2006-01-01';
    
    -- b. 查询这部分员工,对应的部门信息
    select e.*,d.* from (select * from emp where entrydate > '2006-01-01') e left join dept d on e.dept_id = d.id;
    

案例

#涉及 emp、dept、salgrade(工资等级表)
#1.查询员工的姓名、年龄、职位、部门信息。(隐式内连接)
-- 表:emp,dept
-- 连接条件: emp.dept_id = dept.id
select e.name, e.age, e.job, d.name from emp e. dept d where e.dept_id = d.id;

#2.查询年龄小于30岁的员工姓名、 年龄、职位、部门信息。(显式内连接)
-- 表:emp,dept
-- 连接条件: emp.dept_id = dept.id
select e.name, e.age, e.job, d.name from emp e inner join dept d on e.dept_id = d.id where e.age < 30;

#3.查询拥有 员工的部门ID、部门名称。
-- 表:emp,dept
-- 连接条件: emp.dept_id = dept.id
select distinct d.id, d.name from emp e, dept d where e.dept_id = d.id;

#4.查询所有年龄 大于40岁 的员工,及其归属的部门名称;如果员工没有分配部门,也需要展示出来。
-- 表:emp,dept
-- 连接条件: emp.dept_id = dept.id
-- 外连接
select e.* , d.name from emp e left join  dept d on e.dept_id = d.id where e.age > 40;

#5.查询所有 员工的工资等级。
-- 表:emp,salgrade(工资等级表)
-- 连接条件: emp.salary >= salgrade.losal and  emp.salary <= salgrade.hisal;
select e.*, s.grade, s.losal, s.hisal from emp e ,salgrade s where  e.salary >= s.losal and  e.salary <= s.hisal;

select e.*, s.grade, s.losal, s.hisal from emp e ,salgrade s where   e.salary between s.losal and  s.hisal;

#6. 查询"研发部"所有员工的信息及 工资等级。
-- 表:emp, salgrade, dept 
-- 连接条件: emp.salgrade between salgrade.losal and salgrade.hisal, emp.dept_id = dept.id;
-- dept.name = '研发部';
select e.* ,s.grade from emp e, dept d, salgrade s, where e.dept_id d.id and (e.salary between s.losal and s.hisal ) and d.name = '研发部';

#7. 查询"研发部"员工的平均工资。
-- 表:emp, salgrade, dept 
-- 连接条件:emp.dept_id = dept.id
select avg(e.salary) from emp e ,dept d where e.dept_id = d.id and d.name = '研发部'; 

#8.查询工资比 "灭绝"高的员工信息。
--  a.查询"灭绝"的薪资
select salary from emp  where name = '灭绝';

-- b.查询工资比 "灭绝"高的员工信息
select * from emp where salary > (select salary from emp  where name = '灭绝');
#9. 查询比平均薪资高的员工信息。
-- a.查询员工的平均薪资
select avg(salary) from emp;

-- b. 查询比平均薪资高的员工信息
select * from emp where salary > (select avg(salary) from emp);

#10.查询低于本部门平均工资的员工信息。
-- a. 查询指定部门平均薪资
select avg(e1.salary) from emp e1 where e1.dept_id = 1;
select avg(e1.salary) from emp e1 where e1.dept_id = 2;

-- b. 查询低于本部门平均工资的员工信息
select * from emp e2 where e2.salary < (select avg(e1.salary) from emp e1 where e1.dept_id = e2.dept_id);

#11.查询所有的部门信息,并统计部门的员工人数。
-- a.查询所有的部门信息
select id,name from dept;

-- b.统计部门的员工人数
select count(*) from emp where dept_id = 1;

-- c.查询所有的部门信息,并统计部门的员工人数。
select d.id,d.name (select count(*) from emp e where e.dept_id = d.id) '人数' from dept d;

#12.查询所有学生的选课情况,展示出学生名称,学号,课程名称
--  表:studen ,course ,student_course
-- 连接条件: student.id = student_course.studentid ,course.id = student_course.courseid

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

事务

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

事务操作

# 数据准备
create table account(
id int auto_increment primary key comment' 主键ID',
name varchar(10) comment '姓名',
money int comment '余额'
) comment '账户表' ;
insert into account(id, name, money) VALUES (null, '张三' , 2000), (null, '李四', 2000);


-- 转账操作(张三给李四转账1000)
-- 1.查询张三账户余额
select * from account where name = '张三';

-- 2.将张三账户余额- 1000
update account set money = money - 1000 where name = '张三';

-- 如果事务在此发生异常,则在此之前的事务会完成,之后的事务无法完成,转账操作就会发生异常!!!

-- 3.将李四账户余额+1000
update account set money = money + 1000 where name = '李四';

-- 恢复数据
update account set money = 2000 where name = '张三' or name = '李四' ;


  • 查看/设置事务提交方式
#方式1
SELECT @@autocommit;   #查看提交方式, 1 为自动提交,0 为手动提交;事务操作默认是自动提交的
SET @@autocommit = 0;   #将事务改为手动提交

#方式二
START TRANSACTION 或 BEGIN;  #开启事务,不需将提交方式改为手动提交   提交 回滚不变
  • 提交事务
COMMIT;  #如果事务执行成功,则提交事务
  • 回滚事务
ROLLBACK;  #当事务发生异常时,可回滚事务

#将事务改为手动提交,在事务操作过程中出现了异常,不进行提交(COMMIT),则进行回滚(ROLLBACK)。

事务四大特性

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

并发事务问题

  • 脏读:一个事务读到另一个事务还没有提交的数据。比如:事务A在进行查询又进行更新数据,同时事务B又对事务A进行查询,而事务A又还没有提交,这事务B进行的读取到的是事务A更新后的数据。
  • 不可重复读:一个事务先后读取同一条记录,但两次读取的数据不同,称之为不可重复读。比如:事务A第一次查询的记录之后,而事务B对事务A查询记录进行更新,第二次查询的记录与第一次的不同
  • 幻读:一个事务按照条件查询数据时,没有对应的数据行,但是在插入数据时,又发现这行数据已经存在。比如:事务A对记录1查询时没有, 之后事务B对记录1进行插入,当事务A对记录1插入时,则会显示数据已存在。

事务隔离级别

隔离级别 脏读 不可重复读 幻读
Read uncommitted(读,未提交) 1(会出现的问题) 1 1
Read commited(读,已提交) 0 1 1
Repeatable Read(默认) 0 0 1
Serializable 0 0 0
-- 查看事务隔离级别
SELECT @@TRANSACTION_ISOLATION

-- 设置事务隔离级别
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL [READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE]


eg:
select @@transaction_isolation;   #查看隔离级别

set session transaction isolation level read uncommitted; #设置隔离级别为:读,未提交

进阶

存储引擎

MySQL体系结构

  • 连接层
    最上层是一些客户端和链接服务,主要完成一些类似于连接处理、授权认证、及相关的安全方案。服务器也会为安全接入的每个客户端验证它所具有的操作权限。
  • 服务层
    第二层架构主要完成大多数的核心服务功能,如SQL接口,并完成缓存的查询,SQL的分析和优化,部分内置函数的执行。所有跨存储引擎的功能也在这一层实现, 如过程、函数等。
  • 引擎层
    存储引擎真正的负责了MySQL中数据的存储和提取,服务器通过API和存储引擎进行通信。不同的存储引擎具有不同的功能,这样我们可以根据自己的需要,来选取合适的存储引擎。
  • 存储层
    主要是将数据存储在文件系统之上,并完成与存储引擎的交互。

存储引擎简介

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

#1.在创建表时, 指定存储引擎
CREATE TABLE表名(
字段1 字段1类型 [COMMENT 字段1注释],
......
字段n 字段n类型 [COMMENT 字段n注释]
) ENGINE = INNODB [COMMENT表注释];

#2.查看 当前数据库支持的存储引擎
SHOW ENGINES ;


-- 查询建 表语句    ENGINE 存储引擎
SHOW CREATE TABLE 表名;

-- result
CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT ' 主键ID',
  `name` varchar(10) COLLATE utf8_unicode_ci DEFAULT NULL COMMENT '姓名',
  `money` int(11) DEFAULT NULL COMMENT '余额',
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=3 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='账户表'

-- 查询当前数据库支持的存储引擎
show engines;

-- 创建表my_myisam ,并指定MyISAM存储引擎
create table my_myisam(
    id int,
    name varchar(10)
)engine = MyISAM;

-- 创建表my_memory,指定Memory存储引擎
create table my_memory(
    id int,
    name varchar(10)
)engine = Memory;

存储引擎特点

  • InnoDB

    • InnoDB是一种兼顾可靠性和高性能的通用存储引擎,在MySQL5.5之后,InnoDB是默认的MySQL存储引擎。
    • 特点

    DML操作遵循ACID模型,支持事务;

    行级锁,提高并发访问性能;

    支持外键FOREIGN KEY 约束,保证数据的完整性和正确性;

    • 文件

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

    参数:innodb_file_per_table

-- 查看ibdb表结构
ibd2sdi 表名.ibd   #会返回一个json文件,表结构

  • MyISAM

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

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

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

      xxx.MYD:存储数据

      XXX.MYI:存储索引

  • Memory

    • 介绍
      Memory引擎的表数据时存储在内存中的,由于受到硬件问题、或断电问题的影响,只能将这些表作为临时表或缓存使用。
    • 特点
      内存存放
      hash索引(默认)
    • 文件
      xxx.sdi:存储表结构信息 #是有一个sdi文件,因为数据都存储在内存当中
特点 InnoDB MyISAM Memory
存储限制 64TB
事务安全 支持 - -
锁机制 行锁 表锁 表锁
B+tree索引 支持 支持 支持
Hash索引 - - 支持
全文索引 支持(5.6版本之后) 支持 -
空间使用 -
内存使用 中等
批量插入速度
支持外键 支持 - -

存储引擎选择

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

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

MySQL安装-Linux

准备一台Linux服务器

云服务器或者虚拟机都可以;

Linux的版本为 CentOS7;

下载Linux版MySQL安装包

https://downloads.mysql.com/archives/community/

上传MySQL安装包

创建目录,并解压

mkdir mysql

tar -xvf mysql-8.0.26-1.el7.x86_64.rpm-bundle.tar -C mysql

安装mysql的安装包

cd mysql 

#按照执行循序来

rpm -ivh mysql-community-common-8.0.26-1.el7.x86_64.rpm 

rpm -ivh mysql-community-client-plugins-8.0.26-1.el7.x86_64.rpm 

rpm -ivh mysql-community-libs-8.0.26-1.el7.x86_64.rpm 

/*
报错:
warning: mysql-community-libs-8.0.26-1.el7.x86_64.rpm: Header V3 DSA/SHA256 Signature, 
error: Failed dependencies:
	mariadb-libs is obsoleted by mysql-community-libs-8.0.26-1.el7.x86_64
	
解决:   清除之前安装过的依赖即可
yum remove mysql-libs    
*/

rpm -ivh mysql-community-libs-compat-8.0.26-1.el7.x86_64.rpm


yum install openssl-devel #执行下一条指令报错缺少openssl,安装即可

rpm -ivh  mysql-community-devel-8.0.26-1.el7.x86_64.rpm

rpm -ivh mysql-community-client-8.0.26-1.el7.x86_64.rpm

rpm -ivh  mysql-community-server-8.0.26-1.el7.x86_64.rpm

/*
报错:   安装服务端时报错
解决办法:
yum install -y perl-Module-Install.noarch
如果缺 类似 net-tools 的依赖包,安装即可
yum install net-tools
*/

启动MySQL服务

systemctl start mysqld   #启动MySQL
systemctl restart mysqld    #重启MySQL
systemctl stop mysqld     #停止MySQL

查询自动生成的root用户密码

grep 'temporary password' /var/log/mysqld.log
或
cat /var/log/mysqld.log

查看完临时密码之后,进入数据库

mysql -u root -p

然后输入上述查询到的自动生成的密码, 完成登录 .

修改root用户密码

登录到MySQL之后,需要将自动生成的不便记忆的密码修改了,修改成自己熟悉的便于记忆的密码。

ALTER  USER  'root'@'localhost'  IDENTIFIED BY '1234';

​ 执行上述的SQL会报错,原因是因为设置的密码太简单,密码复杂度不够。我们可以设置密码的复杂度为简单类型,密码长度为4。

set global validate_password.policy = 0;  #调整等级
set global validate_password.length = 4;   #密码长度

降低密码的校验规则之后,再次执行上述修改密码的指令。

创建用户

默认的root用户只能当前节点localhost访问,是无法远程访问的,我们还需要创建一个root账户,用户远程访问

create user 'root'@'%' IDENTIFIED WITH mysql_native_password BY '1234';

给root用户分配权限

grant all on *.* to 'root'@'%';  #给用户分配所有权限

重新远程连接MySQL

mysql -u root -p

然后输入密码,连接不了就把防火墙关了

systemctl stop firewalld.service

systemctl disable firewalld.service

索引

索引概述

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

  • 优势

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

  • 劣势

索引列也是要占用空间的。索引大大提高了查询效率,同时却也降低更新表的速度,如对表进行INSERT、UPDATE、 DELETE时,效率降低。

索引结构

MySQL的索引是在存储引擎层实现的,不同的存储引擎有不同的结构,主要包含以下几种:

索引结构 描述
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(多路平衡查找数)

以一颗最大度数(max-degree )为5(5阶)的b-tree为例(每个节点最多存储4个key, 5个指针(节点分支))。

m阶B-Tree满足以下条件:

1、每个节点最多拥有m个子树

2、根节点至少有2个子树

3、分支节点至少拥有m/2颗子树(除根节点和叶子节点外都是分支节点)

4、所有叶子节点都在同一层、每个节点最多可以有m-1个key,并且以升序排列

​ 所以B-Tree的性能总是等价于二分查找(与M值无关),也就没有B树平衡的问题;由于M/2的限制,在插入结点时,如果结点已满,需要将结点分裂为两个各占M/2的结点;删除结点时,需将两个不足M/2的兄弟结点合并;

  • B+Tree

相对于B-Tree区别:
①.所有的数据都会出现在叶子节点
②.叶子节点形成一个 单向链表

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

  • Hash
  • 哈希索引就是采用一定的hash算法 ,将键值换算成新的hash值,映射到对应的槽位上,然后存储在hash表中。
    如果两个(或多个)键值,映射到-一个相同的槽位.上,他们就产生了hash冲突( 也称为hash碰撞),可以通过链表来解决。
    • Hash索引特点
  1. Hash索引只能用于对等比较(=, in), 不支持范围查询(between, >,<,... )
  2. 无法利用索引完成排序操作
  3. 查询效率高,通常只需要一次检索就可以了,效率通常要高于B+tree索引

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

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

索引分类

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

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

分类 含义 特点
聚集索引(Clustered Index) 将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据 必须有,而且只有一一个
二级索引(Secondary Index) 将 数据与索引分开存储,索引结构的叶子节点关联的是对应的主键 可以存在多个
  • 聚集索引选取规则:
    • 如果存在主键,主键索引就是聚集索引。
    • 如果不存在主键,将使用第一个唯一 (UNIQUE) 索引作为聚集索引。
    • 如果表没有主键, 或没有合适的唯一索引, 则nnoDB会自动生成一-个rowid作为隐 藏的聚集索引。

  • 思考

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

    #索引结构是B+Tree,所有节点及数据在叶子节点中都能找到,聚集索引:叶子节点存放id及对应的行数据。二级索引:叶子节点存放非主键索引及对应的主键id。
    
    select  from user where id= 10 ;
    #分析:按照id主键查询是聚集索引,只需一遍扫描即可查询
    
    select * from user where mame= 'Arm';
    #分析:首先要到二级索引查找主键id值,然后根据id值才能查到到行数据
    
    #备注: id为主键,name字段创建的有索引。
    

    2.InnoDB主键索引的B+tree高度为多高呢?
    假设:
    一行数据大小为1k,一页(16k)中可以存储16行这样的数据。InnoDB的指针占用6个字节的空间,主键即使为bigint,占用字节数为8。
    高度为2: (n为key值个数)

    n * 8 + (n + 1) * 6 = 16 * 1024 ,算出n约为1170
    1171 * 16= 18736

    高度为3:

    1171 * 1171 * 16= 21939856

索引语法

  • 创建索引

    CREATE [UNIQUE | FULLTEXT] INDEX index_name ON table_name ( index_col_name, ... );
    
  • 查看索引

    SHOW INDEX FROM table_name;  #show index from tb_user\G;  列太多可将数据显示为行
    
  • 删除索引

    DROP INDEX index_name ON table_name;
    

    case:

    create table tb_user(
    	id int primary key auto_increment comment '主键',
    	name varchar(50) not null comment '用户名',
    	phone varchar(11) not null comment '手机号',
    	email varchar(100) comment '邮箱',
    	profession varchar(11) comment '专业',
    	age tinyint unsigned comment '年龄',
    	gender char(1) comment '性别 , 1: 男, 2: 女',
    	status char(1) comment '状态',
    	createtime datetime comment '创建时间'
    ) comment '系统用户表';
    
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('吕布', '17799990000', 'lvbu666@163.com', '软件工程', 23, '1', '6', '2001-02-02 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('曹操', '17799990001', 'caocao666@qq.com', '通讯工程', 33, '1', '0', '2001-03-05 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('赵云', '17799990002', '17799990@139.com', '英语', 34, '1', '2', '2002-03-02 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('孙悟空', '17799990003', '17799990@sina.com', '工程造价', 54, '1', '0', '2001-07-02 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('花木兰', '17799990004', '19980729@sina.com', '软件工程', 23, '2', '1', '2001-04-22 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('大乔', '17799990005', 'daqiao666@sina.com', '舞蹈', 22, '2', '0', '2001-02-07 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('露娜', '17799990006', 'luna_love@sina.com', '应用数学', 24, '2', '0', '2001-02-08 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('程咬金', '17799990007', 'chengyaojin@163.com', '化工', 38, '1', '5', '2001-05-23 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('项羽', '17799990008', 'xiaoyu666@qq.com', '金属材料', 43, '1', '0', '2001-09-18 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('白起', '17799990009', 'baiqi666@sina.com', '机械工程及其自动化', 27, '1', '2', '2001-08-16 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('韩信', '17799990010', 'hanxin520@163.com', '无机非金属材料工程', 27, '1', '0', '2001-06-12 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('荆轲', '17799990011', 'jingke123@163.com', '会计', 29, '1', '0', '2001-05-11 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('兰陵王', '17799990012', 'lanlinwang666@126.com', '工程造价', 44, '1', '1', '2001-04-09 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('狂铁', '17799990013', 'kuangtie@sina.com', '应用数学', 43, '1', '2', '2001-04-10 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('貂蝉', '17799990014', '84958948374@qq.com', '软件工程', 40, '2', '3', '2001-02-12 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('妲己', '17799990015', '2783238293@qq.com', '软件工程', 31, '2', '0', '2001-01-30 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('芈月', '17799990016', 'xiaomin2001@sina.com', '工业经济', 35, '2', '0', '2000-05-03 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('嬴政', '17799990017', '8839434342@qq.com', '化工', 38, '1', '1', '2001-08-08 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('狄仁杰', '17799990018', 'jujiamlm8166@163.com', '国际贸易', 30, '1', '0', '2007-03-12 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('安琪拉', '17799990019', 'jdodm1h@126.com', '城市规划', 51, '2', '0', '2001-08-15 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('典韦', '17799990020', 'ycaunanjian@163.com', '城市规划', 52, '1', '2', '2000-04-12 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('廉颇', '17799990021', 'lianpo321@126.com', '土木工程', 19, '1', '3', '2002-07-18 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('后羿', '17799990022', 'altycj2000@139.com', '城市园林', 20, '1', '0', '2002-03-10 00:00:00');
    INSERT INTO itcast.tb_user (name, phone, email, profession, age, gender, status, createtime) VALUES ('姜子牙', '17799990023', '37483844@qq.com', '工程造价', 29, '1', '4', '2003-05-26 00:00:00');
    
    
    # 1. name字段为姓名字段, 该字段的值可能会重复,为该字段创建索引。
    create index idx_user_name on tb_user(name);
    
    # 2. phone李机号字段的值, 是非空,且唯一的,为该字段创建唯一索引。
    create unique index idx_user_phone on tb_user(phone);
    
    # 3.为profession、 age、status创建 联合索引。
    create index idx_user_pro_age_sta on tb_user(profession,age,status);
    
    # 4.为email建 立合适的索引来提升查询效率。
    create index idx_user_email on tb_user(email);
    

SQL性能分析

  • SQL执行频率
    MySQL客户端连接成功后,通过show [session l global] status命令可以提供服务器状态信息。通过如下指令,可以查看当前数据库的INSERT、UPDATE、DELETE、SELECT的访问频次:

    show global status like 'Com_______';
    
  • 慢查询日志
    慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒, 默认10秒)的所有SQL语句的日志。MySQL的慢查询日志默认没有开启,需要在MySQL的配置文件(/etc/my.cnf) 中配置如下信息:

#开启MySQL慢日志查询开关
slow_query_log=1

#设置慢日志的时间为2秒,SQL语句执行时间超过2秒,就会视为慢查询,记录慢查询日志
long_query_time=2

#配置完毕之后,通过以下指令重新启动MySQL服务器进行测试,查看慢日志文件中记录的信息 /var/lib/mysql/localhost-slow.log。

#配置
vi /etc/my.cnf  #进入配置文件  在文末加上如下两条,保存退出
slow_query_log=1

long_query_time=2

#重启数据库
systemctl restart mysqld

#查看慢查询日志开关
show variables like 'slow_query_log';

#开启慢查询日志之后在 /var/lib/mysql/localhost-slow.log 下可查看日志

/* 导入100W 数据进入数据库表
1.进入数据库,执行 load data local infile '/home/mysql_data/tb_sku1.sql' into table `tb_sku` fields terminated by ',' lines terminated by '\n';
相关加载介绍移步:https://blog.csdn.net/liaowenxiong/article/details/120432417

注意!!!!
mysql使用文件导入数据是报错:

Loading local data is disabled; this must be enabled on both the client and server sides

临时解决办法:

命令行输入mysql登录命令:

mysql --local-infile=1 -u root -p 

查看是否开启:select @@local_infile;

设置变量:SET GLOBAL local_infile=1;

查看变量:show global variables like 'local_infile';

mysql> show global variables like 'local_infile';

导入文件没有问题,但是重启mysql服务,依然会提示以上错误、
永久解决办法:就是在mysql服务启动时自动加载该配置,修改mysql配置文件,参见:https://www.cnblogs.com/leinuo2016/p/15180560.html
随后进入指定数据库当中(use database)执行  
load data local infile '/home/mysql_data/tb_sku1.sql' into table `tb_sku` fields terminated by ',' lines terminated by '\n';
即可将数据加载到指定表中。
*/

select * from tb_sku limit 10;   # 可查询前10条数据试试是否导入成功

tail -f localhost-slow.log   #尾部慢查询实时查看,在MySQL中执行查询语句时,如果查询等时间超过2秒将会被检测到

  • profile详情(耗时)

show profiles 能够在做SQL优化时帮助我们了解时间都耗费到哪里去了。通过have_profiling参数, 能够看到当前MySQL是否支持profile操作:

SELECT @@have_profiling;

默认profiling是关闭的,可以通过set语句在session(会话)/ global(全局)级别开启profiling:

select @@profiling; #查看是否打开

SET profiling= 1;   #打开

#执行一系列的业务SQL 的操作,然后通过如下指令查看指令的执行耗时:

#查看每一条SQL的耗时基本情况
show profiles;

#查看指定query_id的SQL语句各个阶段的耗时情况  query_id 为 show profiles; 查询出的id
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 表名 WHERE 条件;

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 的值越大越好。

索引使用

  • 验证索引效率

在未简历索引之前,查看SQL的耗时

SELECT * FROM tb_sku where sn = '100000003145001';

针对字段创建索引

create index idx_sku_sn on tb_sku(sn);

然后再次执行相同的SQL语句,再次查看SQL的耗时,对比。

SELECT * FROM tb_sku where sn = '100000003145001';


#创建索引可加快查询速度
  • 最左前缀法则
    如果索引了多列(联合索引),要遵守最左前缀法则。最左前缀法则指的是查询从索引的最左列开始,并且不跳过索引|中的列。如果跳跃某一列,索引将部分失效(后面的字段索引失效)。
#最左端的索引字段必须存在,跟顺序无关
explain select* from tb_user where profession= '软件工程' and age= 31 and status= '0';
explain select * from tb_user where profession= '软件工程' and age =31;
explain select * from tb_user where profession= '软件工程';

explain select * from tb_user where age = 31 and status= '0'; #索引将部分失效

explain select * from tb_user where status= '0';

  • 范围查询
    联合索引中,出现范围查询(>,<),范围查询右侧的列索引失效

    explain select * from tb_user where profession = '软件工程'and age > 30 and status= '0'; #范围查询之后的索引会失效
    explain select * from tb_user where profession = '软件工程' and age >= 30 and status= '0'; #尽量使用 >= 这样的运算符
    
  • 索引列运算
    不要在索引列上进行运算操作,索引将失效。

    explain select * from tb_user where substring(phone,10,2) = '15'; #使用运算函数
    
    
  • 字符 串不加引号
    字符串 类型字段使用时,不加引号,索引将失效。

    explain select * from tb_user where profession = '软件工程' and age = 31 and status= 0;#字符串 类型字段不加引号,索引将失效
    explain select * from tb_user where phone = 17799990015;
    
  • 模糊查询
    如果仅仅是 尾部 模糊匹配,索引不会失效。如果是 头部 模糊匹配,索引失效。

    explain select * from tb_ user where profession like '软件%';  #尾部模糊查询,
    explain select * from tb_ user where profession like '%工程';   #头部模糊查询
    explain select * from tb_ user where profession like '%工%';  #头部模糊查询
    
  • or连接的条件
    用or分割开的条件,如果or前的条件中 的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。

    explain select * from tb_user where id = 10 or age = 23;  #age 没有建立索引,则or两侧的索引都会失效
    explain select * from tb_user where phone = '17799990017' or age = 23;
    
    #notes:由于age没有索引,所以即使id、phone有索引, 索引也会失效。所以需要针对于age也要建立索引。
    
  • 数据分布影响
    如果MySQL 评估 使用索引比全表更慢,则不使用索引。

    select * from tb_user where phone >= '17799990001'; # 查询的数据几乎是整张表,MySQL可能就会走全表扫描,而不走索引
    select * from tb_user where phone >= '17799990015'; 
    
  • SQL提示
    SQL提示,是优化数据库的一个重要手段,简单来说,就是在SQL语句中加入一些 人为的提示来达到优化操作的目的。
    use index:

    explain select * from tb_user use index(idx_user_pro) where profession = '软件工程';  #使用当前索引
    

    ignore index:

    explain select * from tb_user ignore index:(idx_user_pro) where profession = '软件工程';  #忽略当前索引
    

    force index:

    explain select * from tb_user force index(idx_user_pro) where profession = '软件工程;
    # 强制使用索引
    
  • 覆盖索引
    尽量使用覆盖索引(查询使用了索引,并且需要返回的列,在该索引中已经全部能够找到),减少select *。

    explain select id, profession from tb_user where profession = '软件工程' and age =31 and status = '0' ;
    #select *  会产生回表查询
    
    
  • 思考

    一张表, 有四个字段(id, username, password, status),由于数据量大,需要对以下SQL语句进行优化,该如何进
    行才是最优方案:

    select id,username,password from tb_user where username = 'itcast'; #建立username与password的联合索引,
    
  • 前缀索引
    当字段类型为字符串(varchar, text等 )时,有时候需要索引很长的字符串,这会让索引变得很大,查询时,浪费大量的磁盘IO,影响查询效率。此时可以只将字符串的一部分前缀,建立索引,这样可以大大节约索引空间,从而提高索引效率。

    • 语法

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

      select count(distinct email) / count(*) from tb_user ;
      select count(distinct substring(email,1,5))/ count(*) from tb_user ;
      
      create index idx_email_5 on tb_user(email(5));  #针对email取前五个做为前缀索引
      
  • 单列索引与联合索引
    单列索引:即一个索引只包含单个列。
    联合索引:即一个索引包含了多个列。
    在业务场景中,如果存在多个查询条件,考虑针对于查询字段建立索引时,建议建立联合索引。.

  • 索引设计原则

    1.针对于数据量较大, 且查询比较频繁的表建立索引。

    2.针对于常作为查询条件 (where) 、排序(order by)、分组(group by)操作的字段建立索引。

    3.尽量选择区分度高的列作为索引,尽量建立唯一 索引,区分度越高,使用索引的效率越高。
    4.如果是字符串类型的字段, 字段的长度较长,可以针对于字段的特点,建立前缀索引
    5.尽量使用联合索引,减少单列索引,查询时,联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率。

    6.要控制索引的数量,索引并不是多多益善,索引越多,维护索引结构的代价也就越大,会影响增删改的效率。
    7.如果 索引列不能存储NULL值,请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询。

SQL优化

insert 优化

  • 批量插入
Insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'erry');
  • 手动提交事务

    start transaction;   #开始事务
    insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'lerry');
    insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'lerry');
    insert into tb_test values(1,'Tom'),(2,'Cat'),(3,'lerry');
    commit;  #提交事务
    
  • 主键顺序插入

    主键乱序插入:8 1 9 21 88 54 46 3 54 69 87 32 78;
    
    主键顺序插入:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15;
    
  • 大批量插入数据

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

#客户端连接服务端时,加上参数 --local-infile
mysql --local-infile -u root -p

#设置全局参数local_infile为1,开启从本地加载文件导入数据的开关
set global local_infile = 1;

#执行load 指令将准备好的本地或服务器上的数据,加载到表结构中
load data local infile '/root/sql/tb_sku1.sql' into table `tb_sku` fields terminated by ',' lines terminated by '\n';

wc -l tb_sku1.sql  #查看数据有多少行 -l

head tb_sku1.sql  #查看前十条数据

#相关加载介绍移步:https://blog.csdn.net/liaowenxiong/article/details/120432417

主键优化

  • 数据组织方式

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

  • 页分裂

页可以为空,也可以填充一半, 也可以填充100%。每个页包含了2-N行数据(如果一行数据过大, 会行溢出),根据主键排列。主要是主键乱序插入时会产生页分裂

  • 页合并

当删除一行记录时,实际上记录并没有被物理删除,只是记录被标记(flaged) 为删除并且它的空间变得允许被其他记录声明使用。当页中删除的记录达到MERGE_THRESHOLD (默认为页的50%),InnoDB会开始寻 找最靠近的页(前或后)看看是否可以将两个页合并以优化空间使用。

  • 主键设计原则
    • 满足业务 需求的情况下,尽量降低主键的长度。如果主键长度过长,二级索引数据就很大,在搜索的时候就会耗费大量磁盘IO
    • 插入数据时, 尽量选择顺序插入,选择使用AUTO_ INCREMENT自增主键。主键乱序插入会造成页分裂
    • 尽量不要 使用UUID做主键或者是其他自然主键,如身份证号。主键过长会增加检索负担
    • 业务操作时, 避免对主键的修改。修改主键会相应的修改索引结构,代价大

order by 优化

posted @ 2022-02-20 12:46  Epiphany_Th  阅读(88)  评论(0)    收藏  举报