数据库 —— MySQL 02

系列文章

数据库 —— MySQL 01
数据库 —— MySQL 02
数据库 —— Java操作MySQL



四、DQL查询数据

4.1、DQL

Data Query Language 数据查询语言

  • 所有的查询操作都用它 select
  • 简单的查询、复杂的查询它都能做
  • 数据库中最核心的语言,最重要的语句
  • 使用频率最高的语句

先复制下面代码执行,方便后面进行数据查询。

--   测 试 数 据 库
CREATE DATABASE IF NOT EXISTS `school`;

-- 创建一个school数据库
USE `school`;-- 创建学生表
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`(
	`studentno` INT(4) NOT NULL COMMENT '学号',
    `loginpwd` VARCHAR(20) DEFAULT NULL,
    `studentname` VARCHAR(20) DEFAULT NULL COMMENT '学生姓名',
    `sex` TINYINT(1) DEFAULT NULL COMMENT '性别,0或1',
    `gradeid` INT(11) DEFAULT NULL COMMENT '年级编号',
    `phone` VARCHAR(50) NOT NULL COMMENT '联系电话,允许为空',
    `address` VARCHAR(255) NOT NULL COMMENT '地址,允许为空',
    `borndate` DATETIME DEFAULT NULL COMMENT '出生时间',
    `email` VARCHAR (50) NOT NULL COMMENT '邮箱账号允许为空',
    `identitycard` VARCHAR(18) DEFAULT NULL COMMENT '身份证号',
    PRIMARY KEY (`studentno`),
    UNIQUE KEY `identitycard`(`identitycard`),
    KEY `email` (`email`)
)ENGINE=MYISAM DEFAULT CHARSET=utf8;

-- 创建年级表
DROP TABLE IF EXISTS `grade`;
CREATE TABLE `grade`(
	`gradeid` INT(11) NOT NULL AUTO_INCREMENT COMMENT '年级编号',
  `gradename` VARCHAR(50) NOT NULL COMMENT '年级名称',
    PRIMARY KEY (`gradeid`)
) ENGINE=INNODB AUTO_INCREMENT = 6 DEFAULT CHARSET = utf8;

-- 创建科目表
DROP TABLE IF EXISTS `subject`;
CREATE TABLE `subject`(
	`subjectno`INT(11) NOT NULL AUTO_INCREMENT COMMENT '课程编号',
    `subjectname` VARCHAR(50) DEFAULT NULL COMMENT '课程名称',
    `classhour` INT(4) DEFAULT NULL COMMENT '学时',
    `gradeid` INT(4) DEFAULT NULL COMMENT '年级编号',
    PRIMARY KEY (`subjectno`)
)ENGINE = INNODB AUTO_INCREMENT = 19 DEFAULT CHARSET = utf8;

-- 创建成绩表
DROP TABLE IF EXISTS `result`;
CREATE TABLE `result`(
	`studentno` INT(4) NOT NULL COMMENT '学号',
    `subjectno` INT(4) NOT NULL COMMENT '课程编号',
    `examdate` DATETIME NOT NULL COMMENT '考试日期',
    `studentresult` INT (4) NOT NULL COMMENT '考试成绩',
    PRIMARY KEY `subjectno` (`studentno`, `subjectno`)
)ENGINE = INNODB DEFAULT CHARSET = utf8;

-- 插入学生数据
INSERT INTO `student` (`studentno`,`loginpwd`,`studentname`,`sex`,`gradeid`,`phone`,`address`,`borndate`,`email`,`identitycard`)
VALUES
(1000,'111111','李华',0,1,'13800001111','北京朝阳','1981-1-11','[email protected]','12345619811111234'),
(1001,'222222','郑爽',1,2,'13800002222',NULL,'1992-2-21','[email protected]','12345619922211233'),
(1002,'333333','华晨宇',0,3,'13800003333','四川达州','1973-3-21','[email protected]','12345619733211233'),
(1003,'444444','张碧晨',1,1,'13800004444','','1984-4-11','[email protected]','12345619844111233');


-- 插入成绩数据
INSERT INTO `result`(`studentno`,`subjectno`,`examdate`,`studentresult`)
VALUES
(1000,1,'2013-11-11 16:00:00',85),
(1000,2,'2013-11-12 16:00:00',70),
(1000,3,'2013-11-11 09:00:00',68),
(1000,4,'2013-11-13 16:00:00',98),
(1000,5,'2013-11-14 16:00:00',58),
(1001,1,'2013-11-11 16:00:00',86),
(1001,2,'2013-11-12 16:00:00',71),
(1001,3,'2013-11-11 09:00:00',69),
(1001,4,'2013-11-13 16:00:00',95),
(1001,5,'2013-11-14 16:00:00',59),
(1002,1,'2013-11-11 16:00:00',55),
(1002,2,'2013-11-12 16:00:00',60),
(1002,3,'2013-11-11 09:00:00',78),
(1002,4,'2013-11-13 16:00:00',58),
(1002,5,'2013-11-14 16:00:00',88),
(1003,1,'2013-11-11 16:00:00',65),
(1003,2,'2013-11-12 16:00:00',50),
(1003,3,'2013-11-11 09:00:00',68),
(1003,4,'2013-11-13 16:00:00',88),
(1003,5,'2013-11-14 16:00:00',78);

-- 插入年级数据
INSERT INTO `grade` (`gradeid`,`gradename`) VALUES(1,'大一'),(2,'大二'),(3,'大三'),(4,'大四'),(5,'预科班');

-- 插入科目数据
INSERT INTO `subject`(`subjectno`,`subjectname`,`classhour`,`gradeid`)VALUES
(1,'高等数学-1',110,1),
(2,'高等数学-2',110,2),
(3,'高等数学-3',100,3),
(4,'高等数学-4',130,4),
(5,'C语言-1',110,1),

4.2、查询字段

语法:select 字段1, 字段2 from 表

-- 查询全部学生
SELECT * FROM student;
-- 查询指定字段: 学号、学生姓名
SELECT `studentno`, `studentname` FROM `student`;
-- 指定别名 表中显示的就是别名
SELECT `studentno` AS 学号, `studentname` AS 姓名 FROM `student`;
-- 函数 concat(a, b)
SELECT CONCAT('姓名:', `studentname`) AS 新名字 FROM `student`;

指定别名:将表抬头的字段换成指定的

查询1

函数concat():将内容和查询结果拼接起来

查询2

DISTINCT:去重复的数据

-- 查询哪些同学参加了考试
--查询考试学生的学号,但一位考生可能参加多门考试,会出现重复学号
SELECT `studentno` FROM result 
-- distinct 关键字 去除重复数据
SELECT DISTINCT `studentno` FROM `result`;
SELECT VERSION() --查询系统版本
SELECT 100*4 - 1 AS 计算结果;-- 用来计算
SELECT @@auto_increment_increment;--查询自增的步长是多少

-- 学院考试成绩+1分的结果
SELECT `studentno`,`studentresult` AS 加分后 FROM `result`

4.3、where 条件

搜索的条件由一个或多个表达式组成,结果会返回一个布尔值!

作用:检索数据中符合条件的值。

运算符 语法 描述
and 或者 && A and B A && B 逻辑与,两个都为真,结果为真
or 或者 || A or B A || B 逻辑或,其中一个为真, 结果为真
not 或者 ! not A ! A 逻辑非,真为假,假为真
-- 查询考试成绩在 95~100分之间的,下面三种效果一样
SELECT `studentno`,`studentresult` FROM `result`
WHERE `studentresult` BETWEEN 95 AND 100;

SELECT `studentno`,`studentresult` FROM `result`
WHERE `studentresult` >= 95 AND `studentresult`<=100;

SELECT `studentno`,`studentresult` FROM `result`
WHERE `studentresult`>= 95 && `studentresult`<=100;
-- 除了1000号学生之外同学的成绩,下面两个效果相同
SELECT `studentno`,`studentresult` FROM `result`
WHERE `studentno` != 1000;

SELECT `studentno`,`studentresult` FROM `result`
WHERE NOT `studentno` = 1000;

模糊查询:比较运算符

运算符 语法 描述
is null A is null 如果操作符为null,结果为真
is not null A is not null 如果操作符不为null,结果为真
between A between B and C 如果A在B和C之间,结果为真
like A like B 如果B的内容包含了A,结果为真(例如’A‘,在’BCA’中)
in A in (A1, A2, A3…) 如果A是A1或者A2或者A3…中某个值,结果为真
-- 查询姓张的同学:%表示任意个字符,_表示一个字符
SELECT `studentno`,`studentname` FROM `student` WHERE `studentname` LIKE '张%';
-- 查询名字带有华的同学
SELECT `studentno`,`studentname` FROM `student` WHERE `studentname` LIKE '%华%';
-- 查询学号1001,1003的同学
SELECT `studentno`,`studentname` FROM `student` WHERE `studentno` IN(1001,1003);
-- 查询地址为null或者空字符串''的同学
SELECT `studentno`,`studentname` FROM `student` WHERE `address` IS NULL OR `address`='';

4.4、联表查询 join on

前提:表与表之间有公共字段,例如上面创建的student表和result表,它们的studentid字段相同。

联接类型:Inner join 内联接、Outer join 外联接(包括 left join和right join)、Cross join交叉联接。

思路:

  1. 分析查询的字段来自哪些表,如果不止一张表,则需要联表查询。

  2. 确定使用哪种联表查询?参考下面的情况,细分为七种,但大题还是三种。

    操作 描述
    inner join 如果表中至少有一个匹配,则返回行
    left join 会从左表中返回所有的值,即使右表没有匹配
    right join 会从右表中返回所有的值,即使左表没有匹配
    join

1、inner join

如上面表格描述。这里我们要的是student表的学生学号、学生姓名,以及result表的课程编号、分数。条件是两个表的studentno相同。所以这里返回的结果是student表与result表都有的studentno的字段。

-- 要查询学生学号、姓名、科目编号、分数
-- student表和result表的studentno相同,用别名区分两个表的studentno
SELECT s.`studentno`,`studentname`,`subjectno`,`studentresult`
FROM `student` AS s 				-- 取别名
INNER JOIN `result` AS r			-- 取别名
ON s.`studentno` = r.`studentno` -- 获取两个表的studentno相同的对应行

结果:

inner join

2、left join和right join

例如,在student表增加一个新同学,而对于的result表不增加,即新同学没有参加考试。

新同学的student表:

student

result表:

result

此时进行left join查询:

SELECT s.`studentno`,`studentname`,`subjectno`,`studentresult`
FROM `student` AS s
LEFT JOIN `result` AS r
ON s.`studentno` = r.`studentno`
查询结果

可以发现,即使左表新同学的id 1004不存在右表(result)中,即条件s.studentno = r.studentno 不成立,但依然返回了该值。

如果换成right left,则结果不会有新同学,因为右表(result)中没有新同学id。

根据这个结果,我们可以轻松查询缺考同学。

SELECT s.`studentno`,`studentname`,`subjectno`,`studentresult`
FROM `student` AS s
LEFT JOIN `result` AS r
ON s.`studentno` = r.`studentno`
WHERE studentresult IS NULL --这里获得成绩为空的人

下面再来一个,查询参加了考试的同学的学号、姓名、科目名、分数。

SELECT s.`studentno`,`studentname`,`subjectname`,`studentresult` -- 四个字段
FROM `student` s -- 这里的AS可以省略
RIGHT JOIN `result` r -- 这里将student和result联接,并以result为基准
ON r.`studentno` = s.`studentno` -- student要和result的学号相同
-- 第二次联接
LEFT JOIN `subject` sub -- 把上面的结果和subject联接,并以上面结果为准
ON r.`subjectno` = sub.`subjectno`; -- 上面结果的课程号和subject的课程好相同

第一次联接,因为是参加了考试的同学,因此以result表为基准,这样result表的内容就都会作为结果,用right join。第二次查询因为第一次的结果都会作为最终结果的一部分,而subject表中的课程不一定存在最终结果中,因此以第一次结果为基准,用left join。

是不是从这里感觉left join、right join这些有点复杂,其实大部分时候我们只需要用到inner join就行,下面给出一个完整解决思路!

要求:查出 参加 高等数学-1 同学的学号、姓名、科目名、分数。

-- 1.先根据待查询字段写出select的目标,并判断里面连接这些表的关键字段
-- select studentno,studentname,subjectname,studentresult
-- 关键字段 studentno连接student表和result表,subjectno连接result表和subject表
-- 2.通过inner join将这些表一一联接
-- 3.写where判断条件
select s.studentno,studentname,subjectname,studentresult
from student s
inner join result r
on s.studentno = r.studentno
inner join subject sub
on r.subjectno = sub.subjectno
where sub.subjectname = '高等数学-1';

4.5、自连接

自连接就是将自己和自己连接,其核心就是把一张表拆为两张表!

考虑这样一张表

自身编号 父类编号 类别名
2 1 信息技术
3 1 软件开发
4 3 数据库
5 1 美术设计
6 3 web开发
7 5 ps技术
8 2 办公信息

可以将其拆分为父表和子表,父类编号为1表示自身就是顶层。

自身编号 父类编号 名称
2 1 信息技术
3 1 软件开发
5 1 美术设计
自身编号 父类编号 名称
4 3 数据库
8 2 办公信息
6 3 web开发
7 5 ps技术

现在,我们想通过第一张表得到一个整合的表:

父类 子类
信息技术 办公信息
软件开发 数据库
软件开发 web开发
美术设计 ps技术
-- 用两个别名,相当于把总表category拆分成两张表 parent、children
-- 父类表中自身的编号 和 子类表中父类的编号相同
SELECT parent.`名称` AS 父类, children.`名称` AS 子类
FROM category AS parent, category AS children
WHERE parent.`自身编号` = children.`父类编号`

4.5、排序和分页

排序语法:order by 基于的字段 升序/降序

-- 默认升序 ASC,降序DESC
-- 将上面联表查询拿来,对其按成绩降序排序
select s.studentno,studentname,subjectname,studentresult
from student s
inner join result r
on s.studentno = r.studentno
inner join subject sub
on r.subjectno = sub.subjectno
where sub.subjectname = '高等数学-1'
order by studentresult DESC;

分页:

-- ============== 分页 ====================
-- limit 当前数据,页面大小
-- 页面总数 = 数据总数/页面大小
-- 当前页 = (当前数据-1)*页面大小
SELECT s.studentno,studentname,subjectname,studentresult
FROM student s
INNER JOIN result r
ON s.studentno = r.studentno
INNER JOIN SUBJECT sub
ON r.subjectno = sub.subjectno
LIMIT 0,5; -- 第1页,共有20条,每页5条数据
-- LIMIT 5,5 第2页 
-- LIMIT 10,5 第3页
-- LIMIT 15,5 第4页 

4.6、嵌套子查询

1、子查询:就是利用select的查询结果,作为where条件的判断值。

-- 查询 高等数学-1 的所有考试结果(学号、课程编号、成绩) 降序排列

-- 方式一:使用联表查询
SELECT `studentno`,r.`subjectno`,`studentresult` --直接写出查询结果
FROM result r            --学号和成绩来自result表
INNER JOIN `subject` sub --课程编号来自subject表
ON sub.`subjectno` = r.`subjectno` --result和subject的课程编号相同
WHERE `subjectname` = '高等数学-1'  --条件:高等数学-1的结果
ORDER BY `studentresult` DESC


-- 方式二:使用子查询
-- 其实学号、课程编号、成绩都在result表中,但条件高等数学-1在subject表中
-- 所以可以先查出高等数学-1的课程编号,然后再去result表增加条件
SELECT `studentno`,`subjectno`,`studentresult`
FROM result
WHERE subjectno = (    -- 子查询是从里到外
	SELECT subjectno FROM `subject`
	WHERE subjectname = '高等数学-1'
)
ORDER BY `studentresult` DESC

 
练习:查询 高等数学-2 前三名同学的成绩信息(学号、姓名、分数)

-- 联表查询
SELECT s.`studentno`,`studentname`,`studentresult`
FROM `student` s
INNER JOIN `result` r
ON s.`studentno` = r.`studentno`
INNER JOIN `subject` sub
ON r.`subjectno` = sub.`subjectno`
WHERE sub.`subjectname` = '高等数学-2'
ORDER BY `studentresult` DESC
LIMIT 0,3;

-- 子查询
SELECT s.`studentno`,`studentname`,`studentresult`
FROM `student` s
INNER JOIN `result` r
ON s.`studentno` = r.`studentno`
WHERE `subjectno` = (
	SELECT `subjectno` FROM `subject`
	WHERE `subjectname` = '高等数学-2'
)
ORDER BY `studentresult` DESC
LIMIT 0,3;

两种方法结果相同:

子查询结果

4.7、分组和过滤

这里需要先知道select的完整语法,例如where必须在group by上面。

SELECT
    [ DISTINCT ] --distinct 去重
    待查询字段
    FROM 表名
    [INNER|LEFT|RIGHT JOIN 表名 ON 条件] -- 联表查询
    [WHERE 条件] -- 指定条件,可以是子查询
    [GROUP BY 列名] --通过列名分组
    [HAVING 分组过滤条件] --过滤分组后的信息,和where一样,只是位置不同
    [ORDER BY 列名 [ASC 升序| DESC 降序] ] -- 通过列名排序
    [LIMIT 当前数据,页面大小]
-- ==========分组过滤==========
-- 查询不同课程的平均分、最高分、最低分,且只显示平均分大于60的
SELECT `subjectname` AS 课程名, AVG(`studentresult`) AS 平均分,
		MAX(`studentresult`) AS 最高分, MIN(`studentresult`) AS 最低分
FROM result r
INNER JOIN `subject` sub
WHERE r.`subjectno` = sub.`subjectno`
GROUP BY r.subjectno
HAVING 平均分

五、MySQL函数

5.1、常用函数

-- ========常用函数===========
-- 数学运算
SELECT ABS(-8) -- 绝对值
SELECT CEILING(9.4) -- 向上取整
SELECT FLOOR(9.4) -- 向下取整
SELECT RAND() -- 随机数
SELECT SIGN(-10) -- 判断数的符号,负数返回-1,整数返回1
-- 字符串	
SELECT CHAR_LENGTH('12345') -- 字符串长度
SELECT CONCAT('123','456','789') -- 拼接字符串
SELECT INSERT('1234',1,2,'0') -- 把指定位置替换为新字符串
SELECT LOWER('ABC') -- 小写转大写
SELECT UPPER('abc') -- 大写转小写
SELECT REPLACE('123','1','0') -- 1替换为0
-- 时间重要
SELECT CURRENT_DATE() -- 获取当前日期,也可以直接写CURRENT_DATE
SELECT NOW() -- 当前时间
SELECT LOCALTIME() -- 本地时间
SELECT SYSDATE() -- 系统时间
SELECT YEAR(NOW()) -- 获取年

5.2、聚合函数(重要)

-- ===========聚合函数========

-- count(列名)计算该列有多少行,包括null值
-- count(1)、count(*) 都为统计所有记录数,包括null
-- 执行效率上:当数据量1W+时count(*)用时较少,1w以内count(1)用时较少
-- 总结:若字段为主键则count(主键)效率最高,否则少量数据用count(1),大量用count(*)
SELECT COUNT(`studentname`) FROM `student`;
SELECT COUNT(*) FROM `student`;
SELECT COUNT(1) FROM `student`;

SELECT SUM(`studentresult`) AS 总和 FROM result;
SELECT AVG(`studentresult`) AS 平均分 FROM result;
SELECT MIN(`studentresult`) AS 最小值 FROM result;
SELECT MAX(`studentresult`) AS 最大值 FROM result;

5.3、数据库级MD5加密

在我之前的博客 前端学习 03 —— JavaScript 02 有说如何在前端实现MD5加密,其实我们也可以在数据库中实现加密,MySQL本身带有一个md5()加密函数。

-- ===========测试MD5加密================
DROP TABLE IF EXISTS `testMD5`;
CREATE TABLE IF NOT EXISTS `testMD5`(
	`id` INT(4) NOT NULL COMMENT'用户号',
	`pwd` VARCHAR(50) NOT NULL COMMENT '密码',
	PRIMARY KEY(`id`)
)ENGINE=INNODB CHARSET=utf8;

-- 我们一般在Java中操作插入数据库时就进行加密
INSERT INTO `testMD5`(`id`,`pwd`) VALUES (1234, MD5('1234'));

-- 解密,对于用户输入的密码,也进行MD5加密。
-- 因为同一个密码经过MD5加密后的密文都相同。
-- 因此,将密文于和数据中的密码密文进行配对即可。
SELECT `id`,`pwd` FROM `testMD5` WHERE `pwd` = MD5('1234');

六、事物

6.1、什么是事务

事务原则:ACID原则(原子性、一致性、隔离性、持久性)

参考博客链接:https://blog.csdn.net/dengjili/article/details/82468576

原子性(Atomicity) :要么都成功,要么都失败。

一致性(Consistency) :事务前后的数据完整性要保持一致。

隔离性(Isolation) :多个用户访问数据库时,数据库为每个用户开启的事务相互独立,不能被其他事务 操作干扰。

持久性(Durability) :事务一旦提交则不可逆,会写入数据库中,若未提交而被中断,则会恢复到提交前状态。

一些问题:

  1. 脏读:一个事务读取了另一个事务还未提交的数据。
  2. 不可重复读:在一个事务内读取表中的某一行数据,多次读的结果不同。
  3. 虚读(幻读):在一个事务内读取到其他事务插入的数据,导致前后读取不一致。

6.2、事务的流程

-- ==========事务===============
-- mysql 默认开启事务自动提交
SET autocommit = 0; -- 关闭自动提交
SET autocommit = 1; -- 开启自动提交

-- 手动处理事务的流程!
SET autocommit = 0; -- 关闭自动提交
-- 开启一个事务
START TRANSACTION -- 标记一个事务的开始,从这之后直到提交,都处于一个事务中
-- 执行一些操作,例如插入数据
-- 提交
COMMIT
-- 回滚
ROLLBACK
-- 事务结束
SET autocommit = 1 -- 开启事务提交

-- 下面的了解即可
SAVEPOINT 保存点的名字 -- 设置事务保存点
ROLLBACK TO SAVEPOINT 保存的点的名字 -- 回滚到保存点
RELEASE SAVEPOINT 保存点的名字 -- 释放保存点

6.3、手动模拟转账

下面通过事务手动模拟转账:

-- ==========事务模拟==========
CREATE DATABASE shop CHARACTER SET  utf8 COLLATE utf8_general_ci;
USE shop
CREATE TABLE `account`(
	`id` INT(4) NOT NULL AUTO_INCREMENT,
	`name` VARCHAR(20) NOT NULL,
	`money` DECIMAL(9,2) NOT NULL,
	PRIMARY KEY(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8;


INSERT INTO `account`(`name`,`money`) 
VALUES ('小红', 1000), ('小白', 100);

-- 事务:模拟转账
SET autocommit = 0;-- 关闭自动提交
START TRANSACTION -- 开启事务
UPDATE `account` SET `money`=`money`-200 WHERE `name`='小红';
UPDATE `account` SET `money`=`money`+200 WHERE `name`='小白';
COMMIT -- 提交
ROLLBACK -- 回滚
SET autocommit = 1;-- 开启自动提交

在模拟转账中,如果我们执行了两次update后,执行回滚,可以发现表中数据会恢复原来样子,但如果我们提交了之后再回滚,表中数据就不会恢复。

七、索引

7.1、索引分类

  • 主键索引(primary key)

    • 唯一的标识,主键不可重复,只能有一个列作为主键。
  • 唯一索引(unique key)

    • 避免重复列出现,唯一索引可以重复,多个列都可以标识唯一索引 。
  • 常规索引(key/index)

    • 默认的,用index、key关键字来设置
  • 全文索引(full text)

    • 在特定的数据库引擎下才有,MyISAM
    • 快速定位数据

基础语法:

-- 索引的创建:
-- 	     1. 在创建表的时候给字段增加索引
-- 	     2. 通过alter增加
-- 显示索引信息
SHOW INDEX FROM school.student;

-- 增加一个全文索引
ALTER TABLE school.student ADD FULLTEXT INDEX `index_studentname`(`studentname`);
-- explain 分析SQL执行的状况 主要关注其中的rows列
EXPLAIN SELECT * FROM school.student;

7.2、测试索引

-- 通过循环函数 插入100百万行数据,这个函数不需要管,直接复制就行。
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`(`id`,`name`,`email`,`phone`,`gender`)
		VALUES(i,CONCAT('用户',i),'[email protected]','123456789',FLOOR(RAND()*2));
		SET i=i+1;
	END WHILE;
	RETURN i;
END;

SELECT mock_data() -- 执行此函数 生成一百万条数据

SELECT * FROM `app_user` WHERE `name`='用户999999'; -- 执行时间 0.358 sec

-- 创建索引 create index 索引名 on 表名(字段名)
ALTER TABLE `app_user` ADD INDEX `id_app_user_name`(`name`);
SELECT * FROM `app_user` WHERE `name`='用户999999'; -- 执行时间 0 sec

7.3、索引原则

  • 索引不是越多越好
  • 不要对经常变动的数据加索引
  • 小数据量的表不需要索引
  • 索引一般加在常用来查询的字段

索引的数据结构:https://www.cnblogs.com/sheseido/p/11337074.html

八、权限管理和备份

SQLyog 可视化管理

权限管理

SQL命令操作

本质上是对 mysql.user表增删改查

-- 创建用户
create user A identified by '密码';
-- 修改当前用户密码
set password = password('新密码');
-- 修改指定用户的密码
set password for 用户名 = password('密码');
-- 重命名
rename user 原用户名 to 新名字
-- 用户全部授权 所有库.所有表(但不包括给其他人授权的权限)
grant all privileges on *.* to 用户名
-- 查询权限
show grants for 用户名
-- 撤销权限 哪些权限 在哪个库.哪个表 给谁撤销
revoke all privileges on *.* from 用户名
-- 删除用户
drop user 用户名;

数据库备份

数据库备份方式:

  • 拷贝物理文件 data文件夹

  • 在SQLyog这样的可视化工具中手动导出

  • 命令行 mysqldump(windows是在cmd里,不需要登录)

    mysqldump -h主机 -u用户名 -p密码 数据库 表名 > 物理磁盘位置/文件名
    

    导入,尽量先登录mysql,进入到某个数据库中。

    source 文件名
    

九、规范数据库设计

9.1、为什么需要设计

糟糕的数据库设计:

  • 数据冗余,浪费空间
  • 数据库插入和删除都会麻烦
  • 程序的性能差

良好的数据库设计:

  • 节省内存空间
  • 保证数据库的完整性
  • 方便我们开发系统

软件开发中,关于数据库的设计:

  • 分析需求:分析业务和需要处理的数据库需求
  • 概要设计:设计E-R图

例如博客数据库设计:B站up 狂神说 视频

9.2、三大范式

更详细内容参考这个博客:https://www.cnblogs.com/wsg25/p/9615100.html

第一范式(1NF):保证每一列都不可再分。

第二范式(2NF):在满足第一范式前提下,每张表都只描述一件事。

第三范式(3NF):满足第二范式前提下,表中每一列数据都和主键直接相关,不能是间接关系。

但是!!三大范式的规范性常常会和数据库性能有冲突!

阿里规定,关联查询的表不得超过三张

  • 要考虑到商业化的需求和目标(成本和用户体验),数据库的性能更加重要!
  • 有时会故意给某些表增加一些冗余字段。(以便把多表查询变为单表查询)
  • 有时会故意增加一些计算列(例如每增加一名用户,count列加1,而不用count()函数,以减少数据量)

猜你喜欢

转载自blog.csdn.net/qq_39763246/article/details/113643865
今日推荐