【MySQL】MySQL初级笔记

MySQL

1、初识MySQL(了解)

1.1、为什么学习数据库

1、岗位技能需求
2、现在的世界,得数据者得天下
3、存储数据的方法
4、程序,网站中,大量数据如何长久保存?
5、数据库是几乎软件体系中最核心的一个存在。

1.2、什么是数据库

数据库 ( DataBase , 简称DB )
概念 : 长期存放在计算机内,有组织,可共享的大量数据的集合,是一个数据 “仓库”
作用 : 保存,并能安全管理数据(如:增删改查等),减少冗余…
数据库总览 :

  • 关系型数据库 ( SQL )
    MySQL , Oracle , SQL Server , SQLite , DB2 , …
    关系型数据库通过外键关联来建立表与表之间的关系
  • 非关系型数据库 ( NOSQL )
    Redis , MongoDB , …
    非关系型数据库通常指数据以对象的形式存储在数据库中,而对象之间的关系通过每个对象自
    身的属性来决定

1.3、什么是DBMS

数据库管理系统 ( DataBase Management System)

数据库管理软件 , 科学组织和存储数据 , 高效地获取和维护数据

image-20221221234434053

1.4、MySQL简介

概念 : 是现在流行的开源的,免费的 关系型数据库
历史 : 由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品。
特点 :

  • 免费 , 开源数据库
  • 小巧 , 功能齐全
  • 使用便捷
  • 可运行于Windows或Linux操作系统
  • 可适用于中小型甚至大型网站应用

官网 : https://www.mysql.com/

MySQL不区分大小写

Java区分大小小

1.7 连接数据库

连接数据库语句 : mysql -h服务器主机地址 -P 端口号 -u 用户名 -p用户密码

注意 : -p后面不能加空格,否则会被当做密码的内容,导致登录失败 !

1.8 基本操作

update user set password=password('123456')where user='root'; 修改密码
flush privileges; 刷新数据库
show databases; 显示所有数据库
use dbname; 打开某个数据库
show tables; 显示数据库mysql中所有的表
describe user; 显示表mysql数据库中user表的列信息
create database name; 创建数据库
use databasename; 选择数据库
exit; 退出Mysql
? 命令关键词 : 寻求帮助
-- 单行注释
/* (多行注释)
aaaaa
bbbbb
cccccsda
*/

2、操作数据库

2.1、结构化查询语句分类

名称解释命令
DDL (数据定义语言)定义和管理数据对象,如数据库,数据表等CREATE、DROP、ALTER
DML (数据操作语言)用于操作数据库对象中所包含的数据INSERT、UPDATE、DELETE
DQL (数据查询语言)用于查询数据库数据SELECT
DCL (数据控制语言)用于管理数据库的语言,包括管理权限及数据更改GRANT、commit、rollback

2.2、数据库操作

创建数据库 : create database [if not exists] 数据库名;
删除数据库 : drop database [if exists] 数据库名;
查看数据库 : show databases;
使用数据库 : use 数据库名;

2.3、数据值和列类型

列类型:列存放的数据的数据类型

数值类型

image-20221222000857284

字符串类型

image-20221222002236993

日期和时间型数值类型

image-20221222002325136

NULL值

理解为 “没有值” 或 “未知值”
不要用NULL进行算术运算 , 结果仍为NULL

2.4、数据字段属性(重点)

image-20221222003044571

UnSigned

  • 无符号的
  • 声明该数据列不允许负数

ZEROFILL

  • 0填充
  • 不足位数的用0来填充 , 如int(3),5则为005 (长度共3位,前面用0填充)

image-20221222003707221

Auto_InCrement

  • 自动递增的 , 每添加一条数据 , 自动在上一个记录数上加 1(默认)
  • 通常用于设置主键 , 且为整数类型
  • 可定义起始值和步长
  • 当前表设置步长(AUTO_INCREMENT=100) : 只影响当前表
  • SET @@auto_increment_increment=5 ; 影响所有使用自增的表(全局)
  • 需要NOT NULL

NULL 和 NOT NULL

image-20221222003141410

  • NULL=>默认为NULL
  • NOT NULL=>该列必须有值,不能为NULL

DEFAULT

image-20221222003308566

  • 默认的
  • 用于设置默认值
  • 例如,性别字段,默认为"男" , 否则为 “女” ; 若无指定该列的值 , 则默认值为"男"的值

2.5、创建数据表(重要)

属于DDL的一种,语法:

create table [if not exists] `表名`(
    '字段名1' 列类型 [属性][索引][注释], //注释 - COMMENT
    '字段名2' 列类型 [属性][索引][注释],
    #...
    '字段名n' 列类型 [属性][索引][注释]
)[表类型][表字符集][注释];

说明 : 反引号用于区别MySQL保留字与普通字符而引入的 (键盘esc下面的键).

练习

-- 目标 : 创建一个school数据库
-- 创建学生表(列,字段)
-- 学号int 登录密码varchar(20) 姓名,性别varchar(2),出生日期(datatime),家庭住址,email

-- 创建表之前 , 一定要先选择数据库
-- 字符串使用单引号括起来
-- 记得加主键

CREATE DATABASE IF NOT EXISTS `SCHOOL`;
USE `SCHOOL`;
CREATE TABLE IF NOT EXISTS `STUDENT`(
	`ID` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
	`pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码',
	`name` varchar(30) NOT NULL DEFAULT '匿名' COMMENT '姓名',
	`sex` varchar(2) NOT NULL DEFAULT '男' COMMENT '性别',
	`birthday` datetime DEFAULT NULL COMMENT '生日',
	`address` varchar(100) DEFAULT NULL COMMENT '地址',
	`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
	PRIMARY KEY(`id`)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;


-- 查看数据库的定义
SHOW CREATE DATABASE school;
-- 查看数据表的定义
SHOW CREATE TABLE student;
-- 显示表结构
DESC student; -- 设置严格检查模式(不能容错了)SET sql_mode='STRICT_TRANS_TABLES';

2.6、 数据表的类型(引擎)

CREATE TABLE 表名(
    -- 省略一些代码
    -- Mysql注释
    -- 1. # 单行注释
    -- 2. /*...*/ 多行注释
)ENGINE = MyISAM (or InnoDB)

-- 查看mysql所支持的引擎类型 (表类型)
SHOW ENGINES;

MySQL的数据表的类型 :

MyISAM(早些年使用的) ,

InnoDB(默认) ,

HEAP , BOB , CSV等…

MyISAM 与 InnoDB 区别

名称MyISAMInnoDB
事务处理不支持支持
数据行锁定不支持 只支持表锁支持
外键约束不支持支持
全文索引支持不支持
表空间大小较小较大,约 2 倍!

经验 ( 适用场合 ) :

  • 适用 MyISAM : 节约空间 速度较快
  • 适用 InnoDB : 安全性 , 事务处理,多用户操作数据表

数据表的存储位置

  • MySQL数据表以文件方式存放在磁盘中,一个文件夹对应一个数据库

    • 包括表文件 , 数据文件 , 以及数据库的选项文件
    • 位置 : Mysql安装目录\data\下存放数据表 . 目录名对应数据库名 , 该目录下文件名对应数据表
  • 注意 :

    • InnoDB类型数据表只有一个 *.frm文件 , 以及上一级目录的ibdata1文件

    • MyISAM类型数据表对应三个文件 :

      • . frm – 表结构定义文件

      • . MYD – 数据文件 ( data )

      • . MYI – 索引文件 ( index )

image-20221222011243741

设置数据表字符集

我们可为数据库,数据表,数据列设定不同的字符集,设定方法 :

  • 创建时通过命令来设置 , 如 : CREATE TABLE 表名()CHARSET = utf8;
  • 如无设定 , 则根据MySQL数据库配置文件 my.ini 中的参数设定

2.7、修改数据库

修改表 ( ALTER TABLE )

  • 修改表名 : ALTER TABLE 旧表名 RENAME AS 新表名

  • 添加字段 : ALTER TABLE 表名 ADD 字段名 列属性[属性]

  • 修改字段 :

    • 修改约束 ALTER TABLE 表名 MODIFY 字段名 列类型[属性]
    • 重命名字段 ALTER TABLE 表名 CHANGE 旧字段名 新字段名 列属性[属性]
  • 删除字段 : ALTER TABLE 表名 DROP 字段名

删除表 (DROP TABLE)

语法: DROP TABLE [IF EXISTS] 表名

  • IF EXISTS 为可选 , 判断是否存在该数据表
  • 如删除不存在的数据表会抛出错误

3、MySQL数据管理

3.1、外键(了解)

以下都是物理外键,即数据库级别的外键,不建议使用
image-20221222205244177

外键概念

如果公共关键字在一个关系中是主关键字,那么这个公共关键字被称为另一个关系的外键。由此可见,
外键表示了两个关系之间的相关联系。以另一个关系的外键作主关键字的表被称为主表,具有此外键的
表被称为主表的从表。
在实际操作中,将一个表的值放入第二个表来表示关联,所使用的值是第一个表的主键值(在必要时可包
括复合主键值)。此时,第二个表中保存这些值的属性称为外键(foreign key)。
外键作用
保持数据一致性,完整性,主要目的是控制存储在外键表中的数据,约束。 使两张表形成关联,外键只能
引用外表中的列的值或使用空值。

创建外键

建表时指定外键约束

image-20221222204216699

建表后修改

image-20221222204444274

删除外键

操作:删除 grade 表,发现报错

image-20221222204539834

注意 : 删除具有主外键关系的表时 , 要先删子表 (学生), 后删主表(年级)

3.2、DML语言

数据操作语言

  • 用于操作数据库对象中所包含的数据
  • 包括 :
    • INSERT (添加数据语句)
    • UPDATE (更新数据语句)
    • DELETE (删除数据语句)

3.2.1、添加数据 INSERT

INSERT INTO 表名[(字段1,字段2,字段3,...)] VALUES('1 值1','值2','值3')

注意 :

  • 字段或值之间用英文逗号隔开 .
  • ’ 字段1,字段2…’ 该部分可省略 , 但添加的值务必与表结构,数据列,顺序相对应,且数量一致 .
  • 可同时插入多条数据 , values 后用英文逗号隔开 .
-- 使用语句如何增加语句?
-- 语法 : INSERT INTO 表名[(字段1,字段2,字段3,...)] VALUES('值1','值2','值3')
INSERT INTO grade(gradename) VALUES ('大一');

-- 主键自增,那能否省略呢?
INSERT INTO grade VALUES ('大二');

-- 查询:INSERT INTO grade VALUE ('大二')错误代码: 1136
Column count doesn`t match value count at row 1

-- 结论:'字段1,字段2...'该部分可省略 , 但添加的值务必与表结构,数据列,顺序相对应,且数量一.
-- 一次插入多条数据
INSERT INTO grade(gradename) VALUES ('大三'),('大四');

3.2.2、修改数据 UPDATE

UPDATE 表名 SET column_name=value [,column_name2=value2,...] [WHERE condition];

注意 :

  • column_name 为要更改的数据列
  • value 为修改后的数据 , 可以为变量 , 具体指 , 表达式或者嵌套的SELECT结果
  • condition 为筛选条件 , 如不指定则修改该表的所有列数据
-- 修改年级信息
UPDATE grade SET gradename = '高中' WHERE gradeid = 1;

3.2.3、删除数据 DELETE TRUNCATE

DELETE
DELETE FROM 表名 [WHERE condition];

注意:condition为筛选条件 , 如不指定则删除该表的所有列数据

-- 删除最后一个数据
DELETE FROM grade WHERE gradeid = 5;
TRUNCATE
TRUNCATE [TABLE] table_name;
-- 清空年级表
TRUNCATE grade;

注意:DELETE和TRUNCATE的区别

  • 相同 :
    • 都能删除数据 , 不删除表结构 , 但TRUNCATE速度更快
  • 不同 :
    • 使用TRUNCATE TABLE 重新设置AUTO_INCREMENT计数器
    • 使用TRUNCATE TABLE不会对事务有影响 (事务后面会说)
-- 创建一个测试表
CREATE TABLE `test` (
`id` INT(4) NOT NULL AUTO_INCREMENT,
`coll` VARCHAR(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

-- 插入几个测试数据
INSERT INTO test(coll) VALUES('row1'),('row2'),('row3');

-- 删除表数据(不带where条件的delete)
DELETE FROM test;

-- 结论:如不指定Where则删除该表的所有列数据,`自增当前值依然从原来基础上进行`,会记录日志.

-- 删除表数据(truncate)
TRUNCATE TABLE test;

-- 结论:truncate删除数据,自增当前值会恢复到初始值重新开始;不会记录日志.

-- 同样使用DELETE清空不同引擎的数据库表数据.重启数据库服务后
-- InnoDB : 自增列从初始值重新开始 (因为是存储在内存中,断电即失)
-- MyISAM : 自增列依然从上一个自增数据基础上开始 (存在文件中,不会丢失)

4、使用DQL查询数据

4.1、DQL语言

DQL( Data Query Language 数据查询语言 )

  • 查询数据库数据 , 如SELECT语句
  • 简单的单表查询或多表的复杂查询和嵌套查询
  • 是数据库语言中最核心,最重要的语句
  • 使用频率最高的语句

SELECT语法

SELECT [ALL | DISTINCT]
{* | table.* | [table.field1[as alias1][,table.field2[as alias2]][,...]]}
FROM table_name [as table_alias]
    [left | right | inner join table_name2] -- 联合查询
    [WHERE ...] -- 指定结果需满足的条件
    [GROUP BY ...] -- 指定结果按照哪几个字段来分组
    [HAVING] -- 过滤分组的记录必须满足的次要条件
    [ORDER BY ...] -- 指定查询记录按一个或多个条件排序
    [LIMIT {[offset,]row_count | row_countOFFSET offset}];
    -- 指定查询的记录从哪条至哪条

4.2、查询字段

-- 查询表中所有的数据列结果 , 采用 **" \* "** 符号; 但是效率低,不推荐 .
-- 查询所有学生信息
SELECT * FROM student;
-- 查询指定列(学号 , 姓名)
SELECT studentno,studentname FROM student;

AS 起别名

作用:

  • 可给数据列取一个新别名
  • 可给表取一个新别名
  • 可把经计算或总结的结果用另一个新名称来代替
-- 这里是为列取别名(当然as关键词可以省略)
SELECT studentno AS 学号,studentname AS 姓名 FROM student;
-- 使用as也可以为表取别名
SELECT studentno AS 学号,studentname AS 姓名 FROM student AS s;
-- 使用as,为查询结果取一个新名字
-- CONCAT()函数拼接字符串
SELECT CONCAT('姓名:',studentname) AS 新姓名 FROM student;

DISTINCT 去重

作用 : 去掉SELECT查询返回的记录结果中重复的记录 ( 返回所有列的值都相同 ) , 只返回一条

-- # 查看哪些同学参加了考试(学号) 去除重复项
SELECT * FROM result; -- 查看考试成绩
SELECT studentno FROM result; -- 查看哪些同学参加了考试
SELECT DISTINCT studentno FROM result; -- 了解:DISTINCT 去除重复项 , (默认是ALL)

使用表达式的列

数据库中的表达式 : 一般由文本值 , 列值 , NULL , 函数和操作符等组成
应用场景 :

  • SELECT语句返回结果列中使用
  • SELECT语句中的ORDER BY , HAVING等子句中使用
  • DML语句中的 where 条件语句中使用表达式
-- selcet查询中可以使用表达式
SELECT @@auto_increment_increment; -- 查询自增步长
SELECT VERSION(); -- 查询版本号
SELECT 100*3-1 AS 计算结果; -- 表达式
-- 学员考试成绩集体提分一分查看
SELECT studentno,StudentResult+1 AS '提分后' FROM result;
  • 避免SQL返回结果中包含 ’ . ’ , ’ * ’ 和括号等干扰开发语言程序.

4.3、where条件语句

作用:用于检索数据表中 符合条件 的记录
搜索条件可由一个或多个逻辑表达式组成 , 结果一般为真或假.

逻辑操作符

image-20221222211209248

-- 满足条件的查询(where)
SELECT Studentno,StudentResult FROM result;

-- 查询考试成绩在95-100之间的
SELECT Studentno,StudentResult
FROM result
WHERE StudentResult>=95 AND StudentResult<=100;

-- AND也可以写成 &&
SELECT Studentno,StudentResult
FROM result
WHERE StudentResult>=95 && StudentResult<=100;

-- 模糊查询(对应的词:精确查询)
SELECT Studentno,StudentResult
FROM result
WHERE StudentResult BETWEEN 95 AND 100;

-- 除了1000号同学,要其他同学的成绩
SELECT studentno,studentresult
FROM result
WHERE studentno!=1000;

-- 使用NOT
SELECT studentno,studentresult
FROM result
WHERE NOT studentno=1000;

模糊查询 : 比较操作符

image-20221222211445410

注意:

  • 数值数据类型的记录之间才能进行算术运算 ;
  • 相同数据类型的数据之间才能进行比较 ;
-- 模糊查询 between and \ like \ in \ null
-- =============================================
-- LIKE
-- =============================================
-- 查询姓刘的同学的学号及姓名
-- like结合使用的通配符 : % (代表0到任意个字符) _ (一个字符)
SELECT studentno,studentname FROM student
WHERE studentname LIKE '刘%';

-- 查询姓刘的同学,后面只有一个字的
SELECT studentno,studentname FROM student
WHERE studentname LIKE '刘_';

-- 查询姓刘的同学,后面只有两个字的
SELECT studentno,studentname FROM student
WHERE studentname LIKE '刘__';

-- 查询姓名中含有 嘉 字的
SELECT studentno,studentname FROM student
WHERE studentname LIKE '%嘉%';

-- 查询姓名中含有特殊字符的需要使用转义符号 '\'
-- 自定义转义符关键字: ESCAPE ':'
-- =============================================
-- IN
-- =============================================
-- 查询学号为1000,1001,1002的学生姓名
SELECT studentno,studentname FROM student
WHERE studentno IN (1000,1001,1002);

-- 查询地址在北京,南京,河南洛阳的学生
SELECT studentno,studentname,address FROM student
WHERE address IN ('北京','南京','河南洛阳');
-- =============================================
-- NULL 空
-- =============================================
-- 查询出生日期没有填写的同学
-- 不能直接写=NULL , 这是代表错误的 , 用 is null
SELECT studentname FROM student
WHERE BornDate IS NULL;

-- 查询出生日期填写非NULL的同学
SELECT studentname FROM student
WHERE BornDate IS NOT NULL;

-- 查询没有写家庭住址的同学(空字符串不等于null)
SELECT studentname FROM student
WHERE Address='' OR Address IS NULL;

4.4、连接查询

JOIN 对比

image-20221222211921081

image-20221222211927701

image-20221222211939253

/*
连接查询
如需要多张数据表的数据进行查询,则可通过连接运算符实现多个查询
内连接 inner join
查询两个表中的结果集中的交集
外连接 outer join
左外连接 left join
(以左表作为基准,右边表来一一匹配,匹配不上的,返回左表的记录,右表以NULL填充)
右外连接 right join
(以右表作为基准,左边表来一一匹配,匹配不上的,返回右表的记录,左表以NULL填充)
等值连接和非等值连接
自连接
*/

-- 查询参加了考试的同学信息(学号,学生姓名,科目编号,分数)
SELECT * FROM student;
SELECT * FROM result;
/*思路:
(1):分析需求,确定查询的列来源于两个类,student result,连接查询
(2):确定使用哪种连接查询?(内连接)
*/
SELECT s.studentno,studentname,subjectno,StudentResult
FROM student s
INNER JOIN result r
ON r.studentno = s.studentno

-- 右连接(也可实现)
SELECT s.studentno,studentname,subjectno,StudentResult
FROM student s
RIGHT JOIN result r
ON r.studentno = s.studentno

-- 等值连接
SELECT s.studentno,studentname,subjectno,StudentResult
FROM student s , result r
WHERE r.studentno = s.studentno

-- 左连接 (查询了所有同学,不考试的也会查出来)
SELECT s.studentno,studentname,subjectno,StudentResult
FROM student s
LEFT JOIN result r
ON r.studentno = s.studentno

-- 查一下缺考的同学(左连接应用场景)
SELECT s.studentno,studentname,subjectno,StudentResult
FROM student s
LEFT JOIN result r
ON r.studentno = s.studentno
WHERE StudentResult IS NULL

-- 思考题:查询参加了考试的同学信息(学号,学生姓名,科目名,分数)
SELECT s.studentno,studentname,subjectname,StudentResult
FROM student s
INNER JOIN result r
ON r.studentno = s.studentno
INNER JOIN `subject` sub
ON sub.subjectno = r.subjectno

自连接

/*
自连接
数据表与自身进行连接
需求:从一个包含栏目ID , 栏目名称和父栏目ID的表中
查询父栏目名称和其他子栏目名称
*/
-- 创建一个表
CREATE TABLE `category` (
`categoryid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主题id',
`pid` INT(10) NOT NULL COMMENT '父id',
`categoryName` VARCHAR(50) NOT NULL COMMENT '主题名字',
PRIMARY KEY (`categoryid`)
) ENGINE=INNODB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8
-- 插入数据
INSERT INTO `category` (`categoryid`, `pid`, `categoryName`)
VALUES('2','1','信息技术'),
('3','1','软件开发'),
('4','3','数据库'),
('5','1','美术设计'),
('6','3','web开发'),
('7','5','ps技术'),
('8','2','办公信息');
-- 编写SQL语句,将栏目的父子关系呈现出来 (父栏目名称,子栏目名称)

-- 核心思想:把一张表看成两张一模一样的表,然后将这两张表连接查询(自连接)
SELECT a.categoryName AS '父栏目',b.categoryName AS '子栏目'
FROM category AS a,category AS b
WHERE a.`categoryid`=b.`pid`

-- 思考题:查询参加了考试的同学信息(学号,学生姓名,科目名,分数)
SELECT s.studentno,studentname,subjectname,StudentResult
FROM student s
INNER JOIN result r
ON r.studentno = s.studentno
INNER JOIN `subject` sub
ON sub.subjectno = r.subjectno

-- 查询学员及所属的年级(学号,学生姓名,年级名)
SELECT studentno AS 学号,studentname AS 学生姓名,gradename AS 年级名称
FROM student s
INNER JOIN grade g
ON s.`GradeId` = g.`GradeID`

-- 查询科目及所属的年级(科目名称,年级名称)
SELECT subjectname AS 科目名称,gradename AS 年级名称
FROM SUBJECT sub
INNER JOIN grade g
ON sub.gradeid = g.gradeid

-- 查询 数据库结构-1 的所有考试结果(学号 学生姓名 科目名称 成绩)
SELECT s.studentno,studentname,subjectname,StudentResult
FROM student s
INNER JOIN result r
ON r.studentno = s.studentno
INNER JOIN `subject` sub
ON r.subjectno = sub.subjectno
WHERE subjectname='数据库结构-1'

4.5、排序和分页

/*============== 排序 ================
语法 : ORDER BY
ORDER BY 语句用于根据指定的列对结果集进行排序。
ORDER BY 语句默认按照ASC升序对记录进行排序。
如果您希望按照降序对记录进行排序,可以使用 DESC 关键字。
*/
-- 查询 数据库结构-1 的所有考试结果(学号 学生姓名 科目名称 成绩)
-- 按成绩降序排序
SELECT s.studentno,studentname,subjectname,StudentResult
FROM student s
INNER JOIN result r
ON r.studentno = s.studentno
INNER JOIN `subject` sub
ON r.subjectno = sub.subjectno
WHERE subjectname='数据库结构-1'
ORDER BY StudentResult DESC
/*============== 分页 ================
语法 : SELECT * FROM table LIMIT [offset,] rows | rows OFFSET offset
好处 : (用户体验,网络传输,查询压力)
推导:
第一页 : limit 0,5
第二页 : limit 5,5
第三页 : limit 10,5
......
第N页 : limit (pageNo-1)*pageSzie,pageSzie
[pageNo:页码,pageSize:单页面显示条数]
*/
-- 每页显示5条数据
SELECT s.studentno,studentname,subjectname,StudentResult
FROM student s
INNER JOIN result r
ON r.studentno = s.studentno
INNER JOIN `subject` sub
ON r.subjectno = sub.subjectno
WHERE subjectname='数据库结构-1'
ORDER BY StudentResult DESC , studentno
LIMIT 0,5
-- 查询 JAVA第一学年 课程成绩前10名并且分数大于80的学生信息(学号,姓名,课程名,分数)
SELECT s.studentno,studentname,subjectname,StudentResult
FROM student s
INNER JOIN result r
ON r.studentno = s.studentno
INNER JOIN `subject` sub
ON r.subjectno = sub.subjectno
WHERE subjectname='JAVA第一学年'
ORDER BY StudentResult DESC
LIMIT 0,10

4.6、子查询

/*============== 子查询 ================
什么是子查询?
在查询语句中的WHERE条件子句中,又嵌套了另一个查询语句
嵌套查询可由多个子查询组成,求解的方式是由里及外;
子查询返回的结果一般都是集合,故而建议使用IN关键字;
*/
-- 查询 数据库结构-1 的所有考试结果(学号,科目编号,成绩),并且成绩降序排列
-- 方法一:使用连接查询
SELECT studentno,r.subjectno,StudentResult
FROM result r
INNER JOIN `subject` sub
ON r.`SubjectNo`=sub.`SubjectNo`
WHERE subjectname = '数据库结构-1'
ORDER BY studentresult DESC;

-- 方法二:使用子查询(执行顺序:由里及外)
SELECT studentno,subjectno,StudentResult
FROM result
WHERE subjectno=(
SELECT subjectno FROM `subject`
WHERE subjectname = '数据库结构-1'
)
ORDER BY studentresult DESC;

-- 查询课程为 高等数学-2 且分数不小于80分的学生的学号和姓名
-- 方法一:使用连接查询
SELECT s.studentno,studentname
FROM student s
INNER JOIN result r
ON s.`StudentNo` = r.`StudentNo`
INNER JOIN `subject` sub
ON sub.`SubjectNo` = r.`SubjectNo`
WHERE subjectname = '高等数学-2' AND StudentResult>=80

-- 方法二:使用连接查询+子查询
-- 分数不小于80分的学生的学号和姓名
SELECT r.studentno,studentname FROM student s
INNER JOIN result r ON s.`StudentNo`=r.`StudentNo`
WHERE StudentResult>=80
-- 在上面SQL基础上,添加需求:课程为 高等数学-2
SELECT r.studentno,studentname FROM student s
INNER JOIN result r ON s.`StudentNo`=r.`StudentNo`
WHERE StudentResult>=80 AND subjectno=(
SELECT subjectno FROM `subject`
WHERE subjectname = '高等数学-2'
)
-- 方法三:使用子查询
-- 分步写简单sql语句,然后将其嵌套起来
SELECT studentno,studentname FROM student WHERE studentno IN(
SELECT studentno FROM result WHERE StudentResult>=80 AND subjectno=(
SELECT subjectno FROM `subject` WHERE subjectname = '高等数学-2'
)
)
/*
练习题目:
查 C语言-1 的前5名学生的成绩信息(学号,姓名,分数)
使用子查询,查询郭靖同学所在的年级名称
*/

5、MySQL函数

5.1、常用函数

数据函数

SELECT ABS(-8); /*绝对值*/
SELECT CEILING(9.4); /*向上取整*/
SELECT FLOOR(9.4); /*向下取整*/
SELECT RAND(); /*随机数,返回一个0-1之间的随机数*/
SELECT SIGN(0); /*符号函数: 负数返回-1,正数返回1,0返回0*/

字符串函数

SELECT CHAR_LENGTH('狂神说坚持就能成功'); /*返回字符串包含的字符数*/
SELECT CONCAT('我','爱','程序'); /*合并字符串,参数可以有多个*/
SELECT INSERT('我爱编程helloworld',1,2,'超级热爱'); /*替换字符串,从某个位置开始替换某个长度*/
SELECT LOWER('KuangShen'); /*小写*/
SELECT UPPER('KuangShen'); /*大写*/
SELECT LEFT('hello,world',5); /*从左边截取*/
SELECT RIGHT('hello,world',5); /*从右边截取*/
SELECT REPLACE('狂神说坚持就能成功','坚持','努力'); /*替换字符串*/
SELECT SUBSTR('狂神说坚持就能成功',4,6); /*截取字符串,开始和长度*/
SELECT REVERSE('狂神说坚持就能成功'); /*反转
-- 查询姓周的同学,改成邹
SELECT REPLACE(studentname,'周','邹') AS 新名字
FROM student WHERE studentname LIKE '周%';

日期和时间函数

SELECT CURRENT_DATE(); /*获取当前日期*/
SELECT CURDATE(); /*获取当前日期*/
SELECT NOW(); /*获取当前日期和时间*/
SELECT LOCALTIME(); /*获取当前日期和时间*/
SELECT SYSDATE(); /*获取当前日期和时间*/
-- 获取年月日,时分秒
SELECT YEAR(NOW());
SELECT MONTH(NOW());
SELECT DAY(NOW());
SELECT HOUR(NOW());
SELECT MINUTE(NOW());
SELECT SECOND(NOW());

系统信息函数

SELECT VERSION(); /*版本*/
SELECT USER(); /*用户*/

5.2、聚合函数

image-20221222214612422

-- 聚合函数
/*COUNT:非空的*/
SELECT COUNT(studentname) FROM student;
SELECT COUNT(*) FROM student;
SELECT COUNT(1) FROM student; /*推荐*/
-- 从含义上讲,count(1) 与 count(*) 都表示对全部数据行的查询。
-- count(字段) 会统计该字段在表中出现的次数,忽略字段为null 的情况。即不统计字段为null的记录。
-- count(*) 包括了所有的列,相当于行数,在统计结果的时候,包含字段为null 的记录;
-- count(1) 用1代表代码行,在统计结果的时候,包含字段为null 的记录 。
/*
很多人认为count(1)执行的效率会比count(*)高,原因是count(*)会存在全表扫描,而count(1)
可以针对一个字段进行查询。其实不然,count(1)和count(*)都会对全表进行扫描,统计所有记录的
条数,包括那些为null的记录,因此,它们的效率可以说是相差无几。而count(字段)则与前两者不
同,它会统计该字段不为null的记录条数。
下面它们之间的一些对比:
1)在表没有主键时,count(1)比count(*)快
2)有主键时,主键作为计算条件,count(主键)效率最高;
3)若表格只有一个字段,则count(*)效率较高。
*/

SELECT SUM(StudentResult) AS 总和 FROM result;
SELECT AVG(StudentResult) AS 平均分 FROM result;
SELECT MAX(StudentResult) AS 最高分 FROM result;
SELECT MIN(StudentResult) AS 最低分 FROM result;

题目:

-- 查询不同课程的平均分,最高分,最低分
-- 前提:根据不同的课程进行分组
SELECT subjectname,AVG(studentresult) AS 平均分,MAX(StudentResult) AS 最高
分,MIN(StudentResult) AS 最低分
FROM result AS r
INNER JOIN `subject` AS s
ON r.subjectno = s.subjectno
GROUP BY r.subjectno
HAVING 平均分>80;
/*
where写在group by前面.
要是放在分组后面的筛选
要使用HAVING..
因为having是从前面筛选的字段再筛选,而where是从数据表中的>字段直接进行的筛选的
*/

MD5 加密(拓展)

一、MD5简介

MD5即Message-Digest Algorithm 5(信息-摘要算法5),用于确保信息传输完整一致。是计算机广泛
使用的杂凑算法之一(又译摘要算法、哈希算法),主流编程语言普遍已有MD5实现。将数据(如汉
字)运算为另一固定长度值,是杂凑算法的基础原理,MD5的前身有MD2、MD3和MD4。不可逆

二、实现数据加密

新建一个表 testmd5

CREATE TABLE `testmd5` (
    `id` INT(4) NOT NULL,
    `name` VARCHAR(20) NOT NULL,
    `pwd` VARCHAR(50) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

插入一些数据

INSERT INTO testmd5 VALUES(1,'kuangshen','123456'),(2,'1 qinjiang','456789')

如果我们要对pwd这一列数据进行加密,语法是:

update testmd5 set pwd = md5(pwd);

如果单独对某个用户(如kuangshen)的密码加密:

INSERT INTO testmd5 VALUES(3,'kuangshen2','123456')
update testmd5 set pwd = md5(pwd) where name = 'kuangshen2';
  • 插入新的数据自动加密
INSERT INTO testmd5 VALUES(4,'kuangshen3',md5('123456'));
  • 查询登录用户信息(md5对比使用,查看用户输入加密后的密码进行比对)
SELECT * FROM testmd5 WHERE `name`='kuangshen' AND pwd=MD5('123456');

5.3、小结

-- ================ 内置函数 ================
-- 数值函数
abs(x) -- 绝对值 abs(-10.9) = 10
format(x, d) -- 格式化千分位数值 format(1234567.456, 2) = 1,234,567.46
ceil(x) -- 向上取整 ceil(10.1) = 11
floor(x) -- 向下取整 floor (10.1) = 10
round(x) -- 四舍五入去整
mod(m, n) -- m%n m mod n 求余 10%3=1
pi() -- 获得圆周率
pow(m, n) -- m^n
sqrt(x) -- 算术平方根
rand() -- 随机数
truncate(x, d) -- 截取d位小数

-- 时间日期函数
now(), current_timestamp(); -- 当前日期时间
current_date(); -- 当前日期
current_time(); -- 当前时间
date('yyyy-mm-dd hh:ii:ss'); -- 获取日期部分
time('yyyy-mm-dd hh:ii:ss'); -- 获取时间部分
date_format('yyyy-mm-dd hh:ii:ss', '%d %y %a %d %m %b %j'); -- 格式化时间
unix_timestamp(); -- 获得unix时间戳
from_unixtime(); -- 从时间戳获得时间


-- 字符串函数
length(string) -- string长度,字节
char_length(string) -- string的字符个数
substring(str, position [,length]) -- 从str的position开始,取length个字符
replace(str ,search_str ,replace_str) -- 在str中用replace_str替换search_str
instr(string ,substring) -- 返回substring首次在string中出现的位置
concat(string [,...]) -- 连接字串
charset(str) -- 返回字串字符集
lcase(string) -- 转换成小写
left(string, length) -- 从string2中的左边起取length个字符
load_file(file_name) -- 从文件读取内容
locate(substring, string [,start_position]) -- 同instr,但可指定开始位置
lpad(string, length, pad) -- 重复用pad加在string开头,直到字串长度为length
ltrim(string) -- 去除前端空格
repeat(string, count) -- 重复count次
rpad(string, length, pad) --在str后用pad补充,直到长度为length
rtrim(string) -- 去除后端空格
strcmp(string1 ,string2) -- 逐字符比较两字串大小

-- 聚合函数
count()
sum();
max();
min();
avg();
group_concat()

-- 其他常用函数
md5();
default();

6、事务(Transaction)

6.1、概述

什么是事务

  • 事务就是将一组SQL语句放在同一批次内去执行
  • 如果一个SQL语句出错,则该批次内的所有SQL都将被取消执行
  • MySQL事务处理只支持InnoDB和BDB数据表类型 (最新版MYISAM已支持)

事务的ACID原则

  • 原子性(Atomic)
    整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(ROLLBACK)到事务开始前的状态,就像这个事务从来没有执行过一样。
  • 一致性(Consist)
    一个事务可以封装状态改变(除非它是一个只读的)。事务必须始终保持系统处于一致的状态,不管在任何给定的时间并发事务有多少。也就是说:如果事务是并发多个,系统也必须如同串行事务一样操作。其主要特征是保护性不变性(Preserving an Invariant),以转账案例为例,假设有五个账户,每个账户余额是100元,那么五个账户总额是500元,如果在这个5个账户之间同时发生多个转账,无论并发多少个,比如在A与B账户之间转账5元,在C与D账户之间转账10元,在B与E之间转账15元,五个账户总额也应该还是500元,这就是保护性和不变性。
  • 隔离性(Isolated)
    事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
  • 持久性(Durable)
    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障(比如断电)也不应该对其有任何影响
1. 原子性(Atomic)

针对同一个事务

image-20221222221021980

A通过银行给B转200

这个过程包含两个步骤

  • A: 800 - 200 = 600
  • B: 200 + 200 = 400

原子性表示,这两个步骤一起成功,或者一起失败,不能只发生其中一个动作

2. 一致性(Consistency)

image-20221222221021980

操作前A:800,B:200
操作后A:600,B:400

一致性表示事务完成后,符合逻辑运算

3. 隔离性(Isolation)

针对多个用户同时操作,主要是排除其他事务对本次事务的影响

image-20221222221254526

  • 事务一 : A向B转账200
  • 事务二: C向B转账100

两个事务同时进行,其中一个事务读取到另外一个事务还没有提交的数据,执行步骤如图所示,按照数字顺序执行

image-20221222221415619

原来A+B+C总数为2000 之后总数为1900

4. 持久性(Durability)

表示事务结束后的数据不随着外界原因导致数据丢失

操作前A:800,B:200
操作后A:600,B:400

  • 如果在操作前(事务还没有提交)服务器宕机或者断电,那么重启数据库以后,数据状态应该为
    A:800,B:200
  • 如果在操作后(事务已经提交)服务器宕机或者断电,那么重启数据库以后,数据状态应该为
    A:600,B:400

事务的隔离级别

mysql默认隔离级别 — REPEATABLE-READ,可以避免脏读,不可重复读,不可避免幻读

1. Read uncommitted(读未提交)

可以读别的事务未提交的数据,提供了事务间最小限度的隔离。顾名思义,就是一个事务可以读取另一个未提交事务的数据。

示例:小明去商店买衣服,付款的时候,小明正常付款,钱已经打到商店老板账户,但是小明发起的事务还没有提交。就在这时,商店老板查看自己账户,发现钱已到账,于是小明正常离开。小明在走出商店后,马上回滚差点提交的事务,撤销了本次交易。

结果:小明未付钱买到了衣服,商店老板实际未收到小明的付款。

分析:商店老板查看自己的资金账户,这个时候看到的是小明还没有提交事务的付款。这就是脏读

注意:处于该隔离级别的事务A与B,如果事务A使用事务B不提交的变化作为计算的基础,然后哪些未提交的变化被事务A撤销,这就导致了大量的数据错误变化。

2. Read committed (读已提交)

处于Read committed (读已提交)级别的事务可以看到其他事务对数据的修改。也就是说,在事务处理期间,如果其他事务修改了相应的表,那么同一个事务的同一sql在其他事务执行前后返回的是不同的结果。一个事务要等另一个事务提交后才能读取数据。

示例:小明卡里有1000元,准备与几个朋友聚餐消费,消费1000元,当他买单时(事务开启),收费系统检测到他卡里有1000元。就在检测完毕的时候,小明女朋友发现小明有私房钱,全部转走并提交。当收费系统准备扣款时,再检查小明卡里的金额,发现已经没钱了,付款不成功。小明此时就会很纳闷,明明有钱的呀,钱呢?

分析:该示例中同一个事务范围内两个相同的查询却返回了不同数据,这就是不可重复读。该隔离级别可以解决脏读问题.

3. Repeatable read (重复读)

在开始读取数据(事务开启)时,不再允许修改操作

示例:还是小明有1000元,准备跟朋友聚餐消费这个场景,当他买单(事务开启)时,收费系统检测到他卡里有1000元,这个时候,他的女朋友不能转出金额。接下来,收费系统就可以扣款成功了,小明醉醺醺的回家,准备跪脱衣板。

分析:重复读可以解决不可重复读的问题,这句话有些别扭,大家可以仔细品一下。

写到这里,大家可能会产生疑问,什么情况下产生幻读呢?

示例来了:

小明在公司上班,女朋友告诉他,拿着他的卡去逛街消费。花了一千元,然后小明去查看他银行卡的消费记录(事务开启),看到确实是花了一千元。就在这个时候,小明女朋友又花三千元买了一些化妆品和衣服,即新增了一些消费记录。当小明打印自己银行卡消费记录单的时候(女朋友事务提交),发现花了四千元,似乎出现了幻觉,小明很心疼。这就是幻读

扩展:当我们开启一个事务以后,有如下的程序操作

第一步:更新A表id=1的记录

第二步:查询A表id=1的记录

第三步:使用第二步的查询结果作为依据继续业务逻辑

第四步:提交事务

问题来了:同一个事务中,事务未提交前,第二步的查询结果是第一步执行前的结果还是第一步执行后的结果呢?

答案:事务隔离级别是针对不通事务的,同一事务中的未提交的更新,在后续是可以查询到的。

4. Serializable (序列化)

数据库事务的最高隔离级别。在此级别下,事务串行执行。可以避免脏读、不可重复读、幻读等读现象。但是效率低下,耗费数据库性能,不推荐使用。

导致的问题
1.脏读

指一个事务读取了另外一个事务未提交的数据。

image-20221222221641202

事务B读到了事务A还没提交的数据

2.不可重复读

在一个事务内读取表中的某一行数据,多次读取结果不同。(这个不一定是错误,只是某些场合不对)

image-20221222222820059
3.虚读(幻读)

是指在一个事务内读取到了别的事务插入的数据,导致前后读取数量总量不一致。
(一般是行影响,如下图所示:多了一行)

image-20221222222918737

6.2、事务实现

-- 使用set语句来改变自动提交模式
SET autocommit = 0; /*关闭*/
SET autocommit = 1; /*开启(默认)*/
-- 注意:
--- 1.MySQL中默认是自动提交
--- 2.使用事务时应先关闭自动提交

-- 开始一个事务,标记事务的起始点
START TRANSACTION
-- 提交一个事务给数据库
COMMIT
-- 将事务回滚,数据回到本次事务的初始状态
ROLLBACK

-- 还原MySQL数据库的自动提交
SET autocommit =1;


-- 保存点
SAVEPOINT 保存点名称 -- 设置一个事务保存点
ROLLBACK TO SAVEPOINT 保存点名称 -- 回滚到保存点
RELEASE SAVEPOINT 保存点名称 -- 删除保存点

事务处理步骤:

image-20221222225411198

java中使用

image-20221222235500777

6.3、测试题目

/*
课堂测试题目
A在线买一款价格为500元商品,网上银行转账.
A的银行卡余额为2000,然后给商家B支付500.
商家B一开始的银行卡余额为10000
创建数据库shop和创建表account并插入2条数据
*/
CREATE DATABASE `shop`CHARACTER SET utf8 COLLATE utf8_general_ci;
USE `shop`;
CREATE TABLE `account` (
    `id` INT(11) NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(32) NOT NULL,
        `cash` DECIMAL(9,2) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO account (`name`,`cash`)
VALUES('A',2000.00),('B',10000.00)

-- 转账实现
SET autocommit = 0; -- 关闭自动提交
START TRANSACTION; -- 开始一个事务,标记事务的起始点
UPDATE account SET cash=cash-500 WHERE `name`='A';
UPDATE account SET cash=cash+500 WHERE `name`='B';
COMMIT; -- 提交事务 如果不提交,表内数据是没变化的
# rollback; --回滚事务
SET autocommit = 1; -- 恢复自动提交

7、索引

https://blog.csdn.net/qq_36756682/article/details/114483362

https://blog.codinglabs.org/articles/theory-of-mysql-index.html

7.1 什么是索引

  • 官方定义:一种帮助mysql提高查询效率的数据结构
  • 优点:大大加快数据查询速度
  • 缺点:
    1. 维护索引需要耗费数据库资源
    2. 索引需要占用磁盘空间
    3. 当对表的数据进行增删改的时候,因为要维护索引,速度会受到影响

​ (会导致底层数据结构变化,需要进行重排序;所以一般只在常用的搜索字段上建立索引)

7.2 索引分类

  • 主键索引 (Primary Key)
  • 唯一索引 (Unique)
  • 常规索引 (Key/ Index) 默认
  • 全文索引 (FullText)

1. 主键索引 (Primary Key)

  • 设定为主键后数据库会自动建立索引(innodb为聚簇索引)

  • 一张表只能有一个列作为主键,主键那一列的数据不可重复

特点 :

  • 最常见的索引类型
  • 确保数据记录的唯一性
  • 确定特定数据记录在数据库中的位置

2. 唯一索引(Unique)

索引列的值必须唯一,但允许有空值(主键索引不允许索引列=null,唯一索引允许为null,但只能有一个null)

作用 : 避免某一列中的数据重复

与主键索引的区别

  • 主键索引只能有一个
  • 唯一索引可能有多个
CREATE TABLE `Grade`(
    `GradeID` INT(11) AUTO_INCREMENT PRIMARYKEY,
    `GradeName` VARCHAR(32) NOT NULL UNIQUE
    -- 或 UNIQUE KEY `GradeID` (`GradeID`)
)

3. 常规索引(Key/ Index)

默认的索引

作用 : 快速定位特定数据

注意 :

  • index 和 key 关键字都可以设置常规索引
  • 应加在查询找条件的字段
  • 不宜添加太多常规索引,影响数据的插入,删除和修改操作
CREATE TABLE `result`(
    -- 省略一些代码
    INDEX/KEY `ind` (`studentNo`,`subjectNo`) -- 创建表时添加
)

-- 创建后添加
ALTER TABLE `result` ADD INDEX `ind`(`studentNo`,`subjectNo`);

4. 全文索引 (FullText)

作用 : 快速定位特定数据
注意 :

  • MySQL5.7 前只有MYISAM引擎支持全文所以,5.7后INNODB引擎也增加了支持,但很少使用
  • 只能用于CHAR , VARCHAR , TEXT数据列类型
  • 适合大型数据集

5. 复合索引

  • 基于表中的多个列共同创建一个索引,即一个索引包含多个列

复合索引必考面试题:多字段查询能够利用复合索引?

基于(name age bir)三个字段创建了组合索引,请问:

基于(name bir age)查询能否利用索引?==> 调整顺序,可以
基于(name age bir)查询能否利用索引?==> 调整顺序,可以
基于(age bir)查询能否利用索引?==> 不包含最左前缀,不可以
基于(bir age name)查询能否利用索引?==> 调整顺序,可以

官方约定:
1.如果要利用复合索引,得满足最左前缀原则(就是包含创建时最左边的字段)
2.mysql 引擎在查询为了更了更好利用索引,在查询过程中会动态调整查询字段顺序以便利用索引

因此,只要包含最左前缀的查询组合都可以利用组合索引

7.3 索引的底层原理

当我们新建一个表,乱序插入数据时;再查询该表时,会发现结果是有序的

---建表
create table t_emp(id int primary key,name varchar(20),age int);

--插入数据
insert into t_emp values(5,'d',22);
insert into t_emp values(6,'d',22);
insert into t_emp values(7,'e',21);
insert into t_emp values(1,'a',23);
insert into t_emp values(2,'b',26);
insert into t_emp values(3,'c',27);
insert into t_emp values(4,'a',32);
insert into t_emp values(8,'f',53);
insert into t_emp values(9,'v',13);

--查询
select * from t_emp;
image-20221223005558436

这是为什么呢?

原因是mysql底层为主键id自动创建索引,默认会对主键索引进行排序

那为什么要排序呢?

因为排序之后可以加快查询速度,如查询id=3的我只需要按照顺序找到3就行啦
mysql主键采用int类型就是为了排序,因此mysql主键通常不采用uuid,是因为离散不好排序

那是怎么进行排序的呢?

  • 如下图所示,底层存储是通过类链表的形式进行排序,一个数据分为三部分,第一个部分为主键值,第二部分为其他列值,第三部分是一个指针,指向下一条数据

    在这里插入图片描述

因为通过链表的方式存储有很大的不足,链表的时间复杂度为O(n),当数据量十分大时,从头开始查找效率十分低;所以为了进一步提高效率,mysql索引又进行了优化

开始 基于页的形式进行管理索引,将数据划分成一段一段,称作;然后新增了一种新的数据结构——页目录,页目录分为两部分,第一部分存储了每页第一个数据的主键值(不存储其他列数据),第二部分就是指向对应页的指针

  • mysql中默认的innodb引擎默认页大小为16KB

此时,再查询如查询id=7的,首先就会去页目录中查找在哪一页,然后再去对应的页中查找,缩小了查找的范围,提高了查询的效率
在这里插入图片描述

久而久之,数据越来越多,页目录中的数据也可能被存满,所以可能还会有再上一层的页目录,逐渐形成一种树的结构,也就是B+树InnoDB存储引擎就是用 B+Tree 实现其索引结构

在这里插入图片描述

  • B+Tree 是在 B-Tree 基础上的一种优化,使其更适合实现外存储索引结构,原因如下:
  • B树节点中不仅存了数据的key值,还有data值
    而每一个页的存储空间是有限的(16KB),如果data数据较大时将会导致一个页能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率

  • B+树只有叶子结点存放数据的data值,非叶子节点上只存储key值信息
    且所有叶子节点之间都有一个链指针在B+树中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度

  • InnoDB存储引擎中页的大小为16KB,一般表的主键类型为INT(4个字节) / BIGINT(8个字节),指针类型也一般为4或8个字节,也就是说一个页中大概存储16KB/(8B+8B)=1K=1000个键值,也就是说一个深度为3的B+Tree索引可以维护(10^3) ^3 = 10亿 条记录。
  • 实际情况中每个节点可能不能填充满,因此在数据库中,B+Tree的高度一般都在2~4层。
  • mysql的InnoDB存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只需要1~3次磁盘I/O操作

7.4 聚簇索引和非聚簇索引

  • 聚簇索引:将数据存储与索引放到了一块,索引结构的叶子节点保存了行数据

  • 非聚簇索引:可称为辅助索引,将数据与索引分开存储,索引结构的叶子节点指向了数据对应的位置

  • 一般来说,聚簇索引在表中指的就是主键索引,但是有的表很特殊,没有主键,就会像oracle一样创建row_id作为主键

  • innodb中,可以理解为在聚簇索引之上创建的索引就是非聚簇索引,比如复合索引、前缀索引、唯一索引等

  • innodb中,非聚簇索引叶子节点存储的不再是行数据,而是主键值;辅助索引访问数据总是需要二次查找,首先在自己的索引树找到对应的主键值,然后再去去主键索引中查找对应的行数据

  • 如下图所示:查找name=Ellison的数据,如果在name上建立了辅助索引,首先就在辅助索引树上检索name,到达其叶子节点获取对应的主键id=14,然后到主键索引B+树中再执行一次B+树检索操作,最终到达叶子节点即可获取整行数据

InnoDB与MyISAM的中索引的区别

  • InnoDB聚簇索引默认是主键,如果表中没有定义主键,InnoDB会选择一个唯一且非空的索引代替。如果没有这样的索引,InnoDB 会隐式定义一个主键(类似oracle中的RowId)来作为聚簇索引。如果已经设置了主键为聚簇索引又希望再单独设置聚簇索引,必须先删除主键,然后添加我们想要的聚簇索引,最后恢复设置主键即可。
  • MyISAM使用的是非聚簇索引,非聚簇索引的B+树与聚簇索引的B+树节点的结构完全一致,只是存储的内容不同而已,主键索引B+树的节点存储了主键,辅助键索引B+树存储了辅助键。表数据存储在独立的地方,这两颗B+树的叶子节点都使用一个地址指向真正的表数据,对于表数据来说,这两个键没有任何差别。由于索引树是独立的,通过辅助键检索无需访问主键的索引树。
    在这里插入图片描述

🔎 innodb中每次使用辅助索引检索都要经过两次B+树查找,看上去效率明显要低于MyISAM的非聚簇索引,这不是多此一举吗?聚簇索引的优势在哪?

  1. 由于行数据和聚簇索引的叶子节点存储在一起,同一页中会有多条行数据,访问同一数据页不同行记录时,已经把页加载到了Buffer中(缓存器),再次访问时,会在内存中完成访问,不必访问磁盘。这样主键和行数据是一起被载入内存的,找到叶子节点就可以立刻将行数据返回了,如果按照主键Id来组织数据,获得数据更快。
  2. 辅助索引的叶子节点,存储主键值,而不是数据的存放地址。好处是当行数据放生变化时,索引树的节点也需要分裂变化;或者是我们需要查找的数据,在上一次IO读写的缓存中没有,需要发生一次新的IO操作时,可以避免对辅助索引的维护工作,只需要维护聚簇索引树就好了。另一个好处是,因为辅助索引存放的是主键值,减少了辅助索引占用的存储空间大小。

🔑 聚簇索引需要注意什么?

  • 当使用主键为聚簇索引时,主键最好不要使用uuid,因为uuid的值太过离散,不适合排序;不仅如此,新增加记录的uuid可能会插入在索引树中间的位置,导致索引树调整复杂度变大,消耗更多的时间和资源。
  • 建议使用int类型的自增,方便排序并且默认会在索引树的末尾增加主键值,对索引树的结构影响最小。而且,主键值占用的存储空间越大,辅助索引中保存的主键值也会跟着变大,占用存储空间,也会影响到IO操作读取到的数据量。

🔓 为什么主键通常建议使用自增id

  • 聚簇索引的数据的物理存放顺序与索引顺序是一致的,即只要索引是相邻的,那么对应的数据一定也是相邻地存放在磁盘上的。如果主键不是自增id,就会不断地调整数据的物理地址、分页等其他措施急性调整有序。但如果是自增的,它只需要一页一页地写,索引结构相对紧凑,磁盘碎片少,效率也高。

7.5 无法利用索引的情况

  1. 查询语句中使用LIKE关键字
    在查询语句中使用 LIKE 关键字进行查询时,如果匹配字符串的第一个字符为“%”,索引不会被使用。如果“%”不是在第一个位置,索引就会被使用
  2. 查询语句中使用复合索引的情况
    只有查询条件中使用了这些字段中的第一个字段,索引才会被使用(最左前缀原则&自动调整)
  3. 查询语句中使用OR关键字
    查询语句只有OR关键字时,如果OR前后的两个条件的列都有索引,那么查询中将使用索引。如果OR前后有一个条件的列没有索引,那么查询中将不使用索引

7.6 索引的使用

/*
#方法一:创建表时
CREATE TABLE 表名 (
字段名1 数据类型 [完整性约束条件…],
字段名2 数据类型 [完整性约束条件…],
[UNIQUE | FULLTEXT | SPATIAL ] INDEX | KEY
[索引名] (字段名[(长度)] [ASC |DESC])
);

#方法二:CREATE在已存在的表上创建索引
CREATE [UNIQUE | FULLTEXT | SPATIAL ] INDEX 索引名
ON 表名 (字段名[(长度)] [ASC |DESC]) ;

#方法三:ALTER TABLE在已存在的表上创建索引
ALTER TABLE 表名 ADD [UNIQUE | FULLTEXT | SPATIAL ] INDEX
索引名 (字段名[(长度)] [ASC |DESC]) ;

#删除索引:DROP INDEX 索引名 ON 表名字;
#删除主键索引: ALTER TABLE 表名 DROP PRIMARY KEY;
#显示索引信息: SHOW INDEX FROM student;
*/

/*增加全文索引 前面的为索引名,后面为类名*/
ALTER TABLE `school`.`student` ADD FULLTEXT INDEX `studentname (`StudentName`);

/*EXPLAIN : 分析SQL语句执行性能  我们看查出来的rows的大小就行*/
EXPLAIN SELECT * FROM student WHERE studentno='1000';

/*使用全文索引*/
-- 全文搜索通过 MATCH() 函数完成。
-- 搜索字符串做为 against() 的参数被给定。搜索以忽略字母大小写的方式执行。对于表中的每个记录行,MATCH() 返回一个相关性值。即,在搜索字符串与记录行在 MATCH() 列表中指定的列的文本之间的相似性尺度。
EXPLAIN SELECT *FROM student WHERE MATCH(studentname) AGAINST('love');

/*
开始之前,先说一下全文索引的版本、存储引擎、数据类型的支持情况
MySQL 5.6 以前的版本,只有 MyISAM 存储引擎支持全文索引;
MySQL 5.6 及以后的版本,MyISAM 和 InnoDB 存储引擎均支持全文索引;
只有字段的数据类型为 char、varchar、text 及其系列才可以建全文索引。
测试或使用全文索引时,要先看一下自己的 MySQL 版本、存储引擎和数据类型是否支持全文索引。
*/

拓展:测试索引

建表app_user:

CREATE TABLE `app_user` (
    `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
    `name` varchar(50) DEFAULT '' COMMENT '用户昵称',
    `email` varchar(50) NOT NULL COMMENT '用户邮箱',
    `phone` varchar(20) DEFAULT '' COMMENT '手机号',
    `gender` tinyint(4) unsigned DEFAULT '0' COMMENT '性别(0:男;1:女)',
    `password` varchar(100) NOT NULL COMMENT '密码',
    `age` tinyint(4) DEFAULT '0' COMMENT '年龄',
    `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
    `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE
    CURRENT_TIMESTAMP,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='app用户表'

批量插入数据:100w:

DROP FUNCTION IF EXISTS mock_data;

DELIMITER $$
CREATE FUNCTION mock_data () 
RETURNS INT 
BEGIN
	DECLARE num INT DEFAULT 1000000;
	DECLARE i INT DEFAULT 0;
	WHILE i < num DO
		INSERT INTO app_user ( `name`, `email`, `phone`, `gender`, `password`, `age` )
		VALUES
			(
				CONCAT( '用户', i ),
				'24736743@qq.com',
				CONCAT('18',FLOOR( RAND()* ( 999999999-100000000 )+ 100000000 )),
                FLOOR( RAND()* 2 ),
                UUID(),
				FLOOR( RAND()* 100 )
            );
		SET i = i + 1;

	END WHILE;
	RETURN i;
	
END;

SELECT mock_data ();

索引效率测试

  • 无索引
SELECT * FROM app_user WHERE name = '用户123456'; -- 查看耗时

EXPLAIN SELECT * FROM app_user WHERE name = '用户123456'; -- 查看性能

跑了100多w条才找到

image-20221222233925607

  • 创建索引
CREATE INDEX idx_app_user_name ON app_user(name);
  • 测试普通索引
EXPLAIN SELECT * FROM app_user WHERE name = '用户123456'; -- 查看性能

找两行就找到了!

image-20221222234408028

7.7、索引准则

  • 索引不是越多越好
  • 不要对经常变动的数据加索引
  • 小数据量的表建议不要加索引
  • 索引一般应加在查找条件的字段

7.8、索引的数据结构

-- 我们可以在创建上述索引的时候,为其指定索引类型,分两类
hash类型的索引:查询单条快,范围查询慢
btree类型的索引:b+树,层数越多,数据量指数级增长(我们就用它,因为innodb默认支持它)

-- 不同的存储引擎支持的索引类型也不一样
InnoDB 支持事务,支持行级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;
MyISAM 不支持事务,支持表级别锁定,支持 B-tree、Full-text 等索引,不支持 Hash 索引;

-- 了解
Memory 不支持事务,支持表级别锁定,支持 B-tree、Hash 等索引,不支持 Full-text 索引;
NDB 支持事务,支持行级别锁定,支持 Hash 索引,不支持 B-tree、Full-text 等索引;
Archive 不支持事务,支持表级别锁定,不支持 B-tree、HashFull-text 等索引;

8、权限管理

8.1、用户管理

1. 可视化管理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tcujyJvv-1671730555196)(https://qiuyusy-img.oss-cn-hangzhou.aliyuncs.com/img/202212230134330.png)]

image-20221223000151738

2. 基本命令

image-20221223000400026

/* 用户和权限管理 */ ------------------
用户信息表:mysql.user

-- 刷新权限
FLUSH PRIVILEGES

-- 增加用户 CREATE USER kuangshen IDENTIFIED BY '123456'
CREATE USER 用户名 IDENTIFIED BY [PASSWORD] 密码(字符串)
-- 必须拥有mysql数据库的全局CREATE USER权限,或拥有INSERT权限。
-- 只能创建用户,不能赋予权限。
-- 用户名,注意引号:如 'user_name'@'192.168.1.1'
-- 密码也需引号,纯数字密码也要加引号
-- 要在纯文本中指定密码,需忽略PASSWORD关键词。要把密码指定为由PASSWORD()函数返回的混编值,需包含关键字PASSWORD


-- 重命名用户 RENAME USER kuangshen TO kuangshen2
RENAME USER old_user TO new_user

-- 设置密码
SET PASSWORD = PASSWORD('密码') -- 为当前用户设置密码
SET PASSWORD FOR 用户名 = PASSWORD('密码') -- 为指定用户设置密码

-- 删除用户 DROP USER kuangshen2
DROP USER 用户名

-- 分配权限/添加用户
GRANT 权限列表 ON 表名 TO 用户名 [IDENTIFIED BY [PASSWORD] 'password']
-- all privileges 表示所有权限
-- *.* 表示所有库的所有表
-- 库名.表名 表示某库下面的某表

-- 查看权限 SHOW GRANTS FOR root@localhost;
SHOW GRANTS FOR 用户名

-- 查看当前用户权限
SHOW GRANTS;SHOW GRANTS FOR CURRENT_USER;SHOW GRANTS FOR
CURRENT_USER();

-- 撤消权限
REVOKE 权限列表 ON 表名 FROM 用户名
REVOKE ALL PRIVILEGES, GRANT OPTION FROM 用户名 -- 撤销所有权限

3. 权限解释

-- 权限列表
ALL [PRIVILEGES] -- 设置除GRANT OPTION之外的所有简单权限
ALTER -- 允许使用ALTER TABLE
ALTER ROUTINE -- 更改或取消已存储的子程序
CREATE -- 允许使用CREATE TABLE
CREATE ROUTINE -- 创建已存储的子程序
CREATE TEMPORARY TABLES -- 允许使用CREATE TEMPORARY TABLE
CREATE USER -- 允许使用CREATE USER, DROP USER, RENAME USER和REVOKE ALL
PRIVILEGESCREATE VIEW -- 允许使用CREATE VIEW
DELETE -- 允许使用DELETE
DROP -- 允许使用DROP TABLE
EXECUTE -- 允许用户运行已存储的子程序
FILE -- 允许使用SELECT...INTO OUTFILE和LOAD DATA INFILE
INDEX -- 允许使用CREATE INDEX和DROP INDEX
INSERT -- 允许使用INSERT
LOCK TABLES -- 允许对您拥有SELECT权限的表使用LOCK TABLES
PROCESS -- 允许使用SHOW FULL PROCESSLIST
REFERENCES -- 未被实施
RELOAD -- 允许使用FLUSH
REPLICATION CLIENT -- 允许用户询问从属服务器或主服务器的地址
REPLICATION SLAVE -- 用于复制型从属服务器(从主服务器中读取二进制日志事件)
SELECT -- 允许使用SELECT
SHOW DATABASES -- 显示所有数据库
SHOW VIEW -- 允许使用SHOW CREATE VIEW
SHUTDOWN -- 允许使用mysqladmin shutdown
SUPER -- 允许使用CHANGE MASTER, KILL, PURGE MASTER LOGS和SET GLOBAL语句,mysqladmin debug命令;允许您连接(一次),即使已达到max_connections。
UPDATE -- 允许使用UPDATE
USAGE -- “无权限”的同义词
GRANT OPTION -- 允许授予权限

/* 表维护 */
-- 分析和存储表的关键字分布
ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE 表名 ...
-- 检查一个或多个表是否有错误
CHECK TABLE tbl_name [, tbl_name] ... [option] ...
option = {QUICK | FAST | MEDIUM | EXTENDED | CHANGED}
-- 整理数据文件的碎片
OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...

8.2、MySQL备份

数据库备份必要性

  • 保证重要数据不丢失
  • 数据转移

MySQL数据库备份方法

  • mysqldump备份工具
  • 数据库管理工具,如SQLyog
  • 直接拷贝数据库文件和相关配置文件

image-20221223001230737

-- 导出
1. 导出一张表 -- mysqldump -uroot -p123456 school student >D:/a.sql
mysqldump -u用户名 -p密码 库名 表名 > 文件名(D:/a.sql)
2. 导出多张表 -- mysqldump -uroot -p123456 school student result >D:/a.sql
mysqldump -u用户名 -p密码 库名 表123 > 文件名(D:/a.sql)
3. 导出所有表 -- mysqldump -uroot -p123456 school >D:/a.sql
mysqldump -u用户名 -p密码 库名 > 文件名(D:/a.sql)
4. 导出一个库 -- mysqldump -uroot -p123456 -B school >D:/a.sql
mysqldump -u用户名 -p密码 -B 库名 > 文件名(D:/a.sql)
可以-w携带备份条件
-- 导入
1. 在登录mysql的情况下: -- source D:/a.sql
source 备份文件
2. 在不登录的情况下
mysql -u用户名 -p密码 库名 < 备份文件

9、规范化数据库设计

9.1、为什么需要数据库设计

当数据库比较复杂时我们需要设计数据库

糟糕的数据库设计

  • 数据冗余,存储空间浪费
  • 数据更新和插入的异常
  • 程序性能差

良好的数据库设计

  • 节省数据的存储空间
  • 能够保证数据的完整性
  • 方便进行数据库应用系统的开发

软件项目开发周期中数据库设计

  • 需求分析阶段: 分析客户的业务和数据处理需求
  • 概要设计阶段:设计数据库的E-R模型图 , 确认需求信息的正确和完整.

设计数据库步骤

  • 收集信息
    • 与该系统有关人员进行交流 , 座谈 , 充分了解用户需求 , 理解数据库需要完成的任务.
  • 标识实体[Entity]
    • 标识数据库要管理的关键对象或实体,实体一般是名词
  • 标识每个实体需要存储的详细信息[Attribute]
  • 标识实体之间的关系[Relationship]

9.2、三大范式

问题 : 为什么需要数据规范化

不合规范的表设计会导致的问题:

  • 信息重复
  • 更新异常
  • 插入异常
    • 无法正确表示信息
  • 删除异常
    • 丢失有效信息

第一范式(1NF):属性不可分割,即每个属性都是不可分割的原子项。(实体的属性即表中的列)

第二范式(2NF):满足第一范式;且不存在部分依赖,即非主属性必须完全依赖于主属性。(主属性即主键;完全依赖是针对于联合主键的情况,非主键列不能只依赖于主键的一部分)

第三范式(3NF):满足第二范式;且不存在传递依赖,即非主属性不能与非主属性之间有依赖关系,非主属性必须直接依赖于主属性,不能间接依赖主属性。(A -> B, B ->C, A -> C)

第一范式 (1NF)

要求数据库表的每一列都是不可分割的原子数据项。

举例说明:

img

在上面的表中,“家庭信息”和“学校信息”列均不满足原子性的要求,故不满足第一范式,调整如下:

img

可见,调整后的每一列都是不可再分的,因此满足第一范式(1NF);

第二范式(2NF)

第二范式(2NF)是在第一范式(1NF)的基础上建立起来的,即满足第二范式(2NF)必须先满足第一
范式(1NF)。

第二范式要求每个表只描述一件事情

在满足1NF的前提下,表中不存在部分依赖,非主键列要完全依赖于主键。(主要是说在联合主键的情况下,非主键列不能只依赖于主键的一部分)

例1

img

在上图所示的情况中,同一个订单中可能包含不同的产品,因此主键必须是“订单号”和“产品号”联合组成,

但可以发现,产品数量、产品折扣、产品价格与“订单号”和“产品号”都相关,但是订单金额和订单时间仅与“订单号”相关,与“产品号”无关,

这样就不满足第二范式的要求,调整如下,需分成两个表:

img img

例2

如下学生成绩表(score):

stu_id(学生id)、kc_id(课程id)、score(分数)、kc_name(课程名)

primary key(stu_id, kc_id)

image-20221223003756842
表中主键为stu_id和kc_id组成的联合主键。满足1NF;非主键列score完全依赖于主键,stu_id和kc_id两个值才能决定score的值;而kc_name只依赖于kc_id,与stu_id没有依赖关系,它不完全依赖于主键,只依赖于主键的一部分,不符合2NF。

image-20221223003817902

第三范式(3NF)

在满足2NF的前提下,不存在传递依赖。(A -> B, B -> C, A->C)

在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖)

第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。

举例说明:

img

上表中,所有属性都完全依赖于学号,所以满足第二范式,但是“班主任性别”和“班主任年龄”直接依赖的是“班主任姓名”,

而不是主键“学号”,所以需做如下调整:

img img

规范化和性能的关系

规范和性能不可兼得,阿里规定关联查询的表不能超过三张

  • 为满足某种商业目标 , 数据库性能比规范化数据库更重要
  • 在数据规范化的同时 , 要综合考虑数据库的性能
  • 通过在给定的表中添加额外的字段,以大量减少需要从中搜索信息所需的时间
  • 通过在给定的表中插入计算列,以方便查询
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值