• MySQL学习总结


    关系型数据库

    关系型数据库是建立在关系模型基础上的数据库,借助于集合代数等数学概念和方法来处理数据库中的数据。简单说,关系型数据库是由多张能互相连接的表组成的数据库。

    优点

    1. 都是使用表结构,格式一致,易于维护。
    2. 使用通用的 SQL 语言操作,使用方便,可用于复杂查询。
    3. 数据存储在磁盘中,安全。

    缺点

    1. 读写性能比较差,不能满足海量数据的高效率读写。
    2. 不节省空间。因为建立在关系模型上,就要遵循某些规则,比如数据中某字段值即使为空仍要分配空间。
    3. 固定的表结构,灵活度较低。

    常见的关系型数据库有 Oracle、DB2、PostgreSQL、Microsoft SQL Server、Microsoft Access 和 MySQL 等。

    非关系型数据库

    非关系型数据库又被称为 NoSQL(Not Only SQL ),意为不仅仅是 SQL(以前是叫做Not SQL,但后面妥协了)。通常指数据以对象的形式存储在数据库中,而对象之间的关系通过每个对象自身的属性来决定。

    优点

    1. 非关系型数据库存储数据的格式可以是 key-value 形式、文档形式、图片形式等。使用灵活,应用场景广泛,而关系型数据库则只支持基础类型。
    2. 速度快,效率高。 NoSQL 可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘。
    3. 海量数据的维护和处理非常轻松。
    4. 非关系型数据库具有扩展简单、高并发、高稳定性、成本低廉的优势。
    5. 可以实现数据的分布式处理。

    缺点

    1. 非关系型数据库暂时不提供 SQL 支持,学习和使用成本较高。
    2. 非关系数据库没有事务处理,没有保证数据的完整性和安全性。适合处理海量数据,但是不一定安全。
    3. 功能没有关系型数据库完善。

    常见的非关系型数据库有 Neo4j、MongoDBRedis、Memcached、MemcacheDB 和 HBase 等。


    1 操作数据库

    1.1 操作数据库(了解)

    1、 创建数据库

    CREATE DATABASE [IF NOT EXISTS] weston;
    

    2、删除数据库

    DROP DATABASE [IF EXISTS] weston;
    

    3、使用数据库

    USE weston;
    

    4、查看数据库

    SHOW DATABASES; --查看所有的数据库
    

    1.2 数据库的列数据类型

    数值

    • tinyint 十分小的数据 1个字节
    • smallint 较小的字节 2个字节
    • mediumint 中等大小的数据 3个字节
    • int 标准的整数 4个字节 常用,对应Java里的int类型
    • bigint 较大的数据 8个字节 对应Java里的long类型
    • float 浮点数 4个字节
    • double 浮点数 8个字节(精度问题!)
    • decimal 字符串类型的浮点数 金融计算的时候,一般是使用decimal

    字符串

    • char 字符串 0~255
    • varchar 可变字符串 0~65535 常用的,对应Java里的String类型
    • tinytext 微型文本 2^8 - 1 差不多可以写一篇博客
    • text 文本串 2^16 - 1 保存大文本

    时间日期,对应Java里的java.util.Date

    • date YY-MM-DD 日期格式
    • time HH:mm:ss 时间格式
    • datetime YY-MM-DD HH:mm:ss 日期时间 最常用的时间格式
    • timestamp 1970.1.1到现在的毫秒数 时间戳
    • year 年份表示

    null

    • 没有值,未知
    • 注意,不要使用NULL进行运算,结果为NULL

    1.3 数据库的字段属性(重点)

    • Unsigned
      • 声明该字段为无符号的整数
      • 声明了该列不能为负数
    • zerofill
      • 0填充的
      • 不足的位数,使用0来填充。如int(3)。5则为005
    • 自增
      • 通常理解为自增,自动在上一条记录的基础上 + 1(默认)
      • 通常用来设计唯一的主键,必须是整数类型
      • 可以自动以设置主键自增的起始值和步长
    • 非空 NULL not NULL
      • 假设设置为not null,如果不给该字段赋值,就会报错
      • NULL,如果不填写值,默认就是null
    • 默认
      • 设置默认的值

    拓展

    /* 每一个表,都必须存在以下5个字段,未来做项目用的,表示一个记录存在意义
    id         主键
    version    乐观锁
    is_delete  伪删除
    gmt_create 创建时间
    gmt_update 修改时间
    */
    

    1.4 创建一个表

    -- 格式
    CREATE TABLE [IF NOT EXISTS] `表名`(
    	`字段名` 字段数据类型 [属性] [索引] [字段注释],
        `字段名` 字段数据类型 [属性] [索引] [字段注释],
        `字段名` 字段数据类型 [属性] [索引] [字段注释],
        ......
        `字段名` 字段数据类型 [属性] [索引] [字段注释]
    )[表引擎类型] [字符集类型] [表注释]
    
    1.4.1 创建表举例
    -- 所有字符使用单引号括起来!
    -- 所有的语句后面加,(英文的),最后一个不加,
    CREATE TABLE IF NOT EXISTS `student`(
        -- id为自动填充4位的整型,非空,自增,注释为学号
    	`id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
        -- name为100长度的字符串,非空,默认为'匿名',注释为姓名
        `name` VARCHAR(30) NOT NULL DEFAULT '匿名' COMMENT '姓名',
        `pwd` VARCHAR(20) NOT NULL DEFAULT '123456' 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
    
    1.4.2 查看创建表语句
    -- 查看创建表语句
    SHOW CREATE TABLE `student`;
    -- 查询结果如下
    CREATE TABLE `student` (
      `id` int(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
      `name` varchar(30) NOT NULL DEFAULT '匿名' COMMENT '姓名',
      `pwd` varchar(20) NOT NULL DEFAULT '123456' 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
    
    1.4.3 查看表结构
    -- 查看表结构
    DESC `student`;
    -- 查询结果如下
    

    1.5 数据表的类型

    1.5.1 数据表的引擎类型
    • INNODB 默认使用的引擎
    • MYISAM 早期使用的引擎
    MYISAM INNODB
    是否支持事务 不支持 支持
    数据行锁 不支持(只支持表锁) 支持
    外键约束 不支持 支持
    全文索引 支持 不支持
    表空间的大小 较小 较大,约为MYISAM的两倍

    常规使用操作:

    • MYISAM:节约空间,速度较快
    • INNODB:安全性高,事务的处理,夺标多用户操作

    在物理空间存在的位置。所有的数据库文件都存在于data文件夹下,一个文件夹就对应一个数据库,本质还是文件的存储。

    • InnoDB在数据库表中只有一个*.frm文件,以及上级目录下的ibdata1文件
    • MYISAM
      • *.frm 表结构的定义文件
      • *.MYD 数据文件(data)
      • *.MYI 索引文件(index)
    1.5.2 数据表的字符集编码
    CHARSET = utf8
    

    如果不设置字符集编码的话,表会使用mysql的默认字符集编码Latin1(不支持中文)

    还可以在my.ini配置文件中加上一个全局的字符集编码配置:

    character-set-server = utf8
    

    1.6 修改表

    -- 修改表明
    ALTER TABLE `student` RENAME AS `student1`;
    -- 增加表的字段
    ALTER TABLE `student` ADD `phone` INT(11) ...;
    -- 修改表的字段(重命名,修改约束)
    ALTER TABLE `student` MODIFY `phone` VARCHAR(10) -- 修改约束(MODIFY不能重命名)
    ALTER TABLE `student` CHANGE `phone` `phone1`    -- 重命名字段(CHANGE还可以MODIFY,即修改约束)
    -- 删除表的字段
    ALTER TABLE `student` DROP `phone`
    -- 删除表
    DROP TABLE IF EXISTS `student`;
    

    2 MySQL数据管理

    2.1 外键

    -- 首先创建年级表
    CREATE TABLE IF NOT EXISTS `grade`(
    	`gradeid` INT(10) NOT NULL AUTO_INCREMENT COMMENT '年级id',
    	`gradename` VARCHAR(50) NOT NULL COMMENT '年级名称',
    	PRIMARY KEY(`gradeid`)
    )ENGINE = INNODB DEFAULT CHARSET = utf8
    
    -- 然后再创建学生表
    CREATE TABLE `student` (
        `id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
        `name` VARCHAR(30) NOT NULL DEFAULT '匿名' COMMENT '姓名',
        `pwd` VARCHAR(20) NOT NULL DEFAULT '123456' COMMENT '密码',
        `sex` VARCHAR(2) NOT NULL DEFAULT '女' COMMENT '性别',
        `birthday` DATETIME DEFAULT NULL COMMENT '出生日期',
        `gradeid` INT(10) NOT NULL COMMENT '年级id',
        `address` VARCHAR(100) DEFAULT NULL COMMENT '家庭住址',
        `email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
        -- 下面三步创建外键
        PRIMARY KEY (`id`),
        KEY `FK_gradeid` (gradeid),
        CONSTRAINT `FK_grade` FOREIGN KEY (`gradeid`) REFERENCES `grade`(`gradeid`)
    ) ENGINE=INNODB DEFAULT CHARSET=utf8
    
    -- 也可以创建表之后建立外键
    ALTER TABLE `student` ADD CONSTRAINT `FK_grade` FOREIGN KEY(`gradeid`) REFERENCES `grade`(`gradeid`);
    

    以上的操作都是物理外键,数据库级别的外键,我们不建议使用!(避免数据库过多造成困扰,所以外键的功能了解即可)

    最佳实践

    • 数据库就是单纯的数据表,只用来存数据,只有行(数据)和列(字段)
    • 我们想使用多张表的数据,即想使用外键的功能,我们就通过程序(代码)去实现。

    2.2 添加数据

    INSERT INTO `表名` (`字段1`, `字段2`, `字段3`...) VALUES ('值1','值2','值3'...);
    -- 一般写插入语句,我们一定要数据和字段一一对应
    INSERT INTO `student` (`age`, `name`, `sex`) VALUES(21, '小曾', '男');
    -- 一次性插入多行
    INSERT INTO `grade` (`gradename`) VALUES('大三'), ('大四');
    

    注意事项:

    • 字段和字段之间使用英文逗号隔开
    • 字段是可以省略的,但是后面的值必须要一一对应,不能少
    • 可以同时插入多行数据,VALUES后面的值,需要使用,隔开即可,即使用VALUES (), (), ()

    2.3 修改数据

    -- 如果不加WHERE限制,那么默认会SET所有的行
    UPDATE TABLE `student` SET `name` = '小曾' WHERE `id` = 1;
    

    2.4 删除数据

    -- 删除一行,同理,如果不加WHERE限制,那么默认会删除所有的行
    DELETE FROM TABLE `student` WHERE 1 = 1;
    -- TRUNCATE直接删除整个数据表,然后新建一个空的。自增清零,也就是从0开始重新自增
    TRUNCATE TABLE `student`;
    

    3 DQL查询数据(最重要)

    3.1 SELECT查询

    SELECT语法

    SELECT [ALL | DISTINCT]
    {* | table.* | [table.filed[as alias1][, table.field2 [as alias2]][, ......]]}
    FROM table_name [as table_alias]
    	[LEFT | RIGHT | INNER JOIN table_name2]  -- 联合查询
    	[WHERE ...]  							 -- 指定结果需满足的条件
    	[GROUP BY ...]                           -- 指定结果按照哪个字段分组(比如查询每个课程的平均分、最高分、最低分时)
    	[HAVING ...]							 -- 和GROUP BY连用,过滤分组的记录需满足的条件
    	[ORDER BY ...]                           -- 指定查询记录按一个或多个条件排序
    	[LIMIT {[offset,]row_count | row_countOFFSET offset}];
    	                                         -- 指定查询的记录从哪条至哪条(一般用于查询前x个,或者分页)
    

    3.1.1 条件查询

    SELECT * FROM `student` WHERE 1 = 1;
    

    3.1.2 模糊查询

    • LIKE
      • %可以匹配0个或者任意多个任意字符
      • _匹配一个任意字符
    • IS NULL,为NULL则返回TRUE
    • IS NOT NULL,不为NUL则返回TRUE

    3.2 左右内连接查询

    • 内连接求交集:INNER JOIN
    • 左连接,以左表为准,右表没有则为NULL:LEFT JOIN
    • 右连接,以右表为准,左表没有则为NULL:RIGHT JOIN

    3.3 自连接查询

    自己的表和自己的表连接

    SELECT f.`gradename`, s.`gradename` FROM `grade`, `grade`;
    

    3.4 分页和排序

    3.4.1 分页LIMIT
    • 为什么要分页?
      • 缓解数据库压力,给人的体验更好
    -- MySQL语法:LIMIT 起始值, 页面大小
    -- LIMIT 0, 5 表示1 ~ 5行数据
    -- LIMIT 1, 5 表示2 ~ 6行数据
    -- LIMIT 5, 5 表示6 ~ 10行数据(如果5行一页的话,其实就是第二页)
    
    -- 网页的做法:当前页、总的页数、每一页的大小,可以在MySQL中如下计算
    
    -- 第一页 LIMIT 0,5  (1-1)*5
    -- 第二页 LIMIT 5,5  (2-1)*5
    -- 第三页 LIMIT 10,5 (3-1)*5
    -- ......
    -- 第N页 LIMIT      (N-1)*pageSize
    -- 【pageSize: 页面大小】
    -- 【(N-1)*pageSize:起始值】
    -- 【N:当前页】
    -- 【数据总数 / 页面大小 = 总页数】
    
    
    3.4.2 排序ORDER BY
    -- 升序
    ORDER BY `字段` ASC;
    -- 降序
    ORDER BY `字段` DESC;
    
    思考题

    查询 Java第一学年 课程成绩排名前十的学生,并且分数要大于80的学生信息(学号,姓名,课程成绩,分数)

    SELECT s.`StudentNo`, `StudentName`, `SubjectName`, `StudentResult`
    FROM `student` AS s
    INNER JOIN `result` AS r
    ON s.StudentNo = r.StudentNo
    INNER JOIN `subject` AS sub
    ON r.`SubjectNo` = sub.`SubjectNo`
    WHERE SubjectName = 'Java第一学年' AND StudentResult >= 80
    ORDER BY StudentReslut DESC
    -- 下面筛选出成绩排名前十的同学
    LIMIT 0, 10;
    

    3.5 子查询

    问题:查询 数据库结构-1 的所有考试结果(学号、科目编号、成绩),降序排列

    -- 方式一:使用内连接查询
    SELECT `StudentNo`, r.`SubjectNo`, `StudentResult` 
    FROM `result` AS r
    INNER JOIN `subject`AS sub
    ON r.SubjectNo = sub.subjectNo
    WHERE SubjectName = '数据库结构-1'
    ORDER BY StudentResult DESC;
    
    -- 方式二:使用子查询(由里到外查询)
    SELECT `StudentNo`, r.`SubjectNo`, `StudentResult`
    FROM result
    WHERE SubjectNo = (
        SELECT SubjectNo FROM `subject`
        WHERE SubjectName = '数据库结构-1'
    )
    

    分数不小于80分的学号和姓名

    SELECT DISTINCT `StudentNo`, `StudentName`
    FROM student AS s
    INNER JOIN result AS r
    ON s.StudentNo = r.StudentNo
    WHERE r.`StudentResult` >= 80;
    
    -- 在这个基础上增加一个科目,高等数学-2
    SELECT DISTINCT `StudentNo`, `StudentName`
    FROM student AS s
    INNER JOIN result AS r
    ON s.StudentNo = r.StudentNo
    WHERE r.`StudentResult` >= 80 AND r.`SubjectNo` = (
        SELECT `SubjectNo` FROM subject WHERE `SubjectName` = '高等数学-2'
    )
    

    3.6 分组和过滤

    • GROUP BY 用于分组
    • HAVING 用于分组后过滤
    -- 查询不同课程的平均分,最高分,最低分
    -- 核心难点:根据不同的课程分组
    SELECT `SubjectName`, AVG(StudentResult) AS 平均分, MAX(StudentResult) AS 最高分, MIN(StudentResult) AS 最低分
    FROM result AS r
    INNER JOIN `subject` AS sub
    ON r.`SubjectNo` = sub.`SubjectNo`
    -- 通过什么字段来分组
    GROUP BY r.`SubjectNo` 
    HAVING 平均分 > 80;
    

    4 MySQL函数

    4.1 常用函数

    -- 数学运算
    SELECT ABS(-8)
    -- 向上取整
    SELECT CEILING(9.4)
    -- 向下取整
    SELECT FLOOR(9.4)
    -- 随机数,返回一个0~1之间的随机数
    SELECT RAND()
    -- 判断一个数的符号
    SELECT SIGN()
    
    -- 字符串函数
    -- 字符串长度
    SELECT CHAR_LENGTH()
    -- 字符串拼接
    SELECT CONCAT('我', '爱', '你们')
    -- 插入字符串
    SELECT INSERT()
    -- 转小写
    SELECT LOWER()
    -- 转大写
    SELECT UPPER()
    
    -- 时间和日期函数(记住)
    SELECT CURRENT_DATE()
    SELECT CURDATE()
    SELECT NOW()
    SELECT LOCALTIME()
    SELECT SYSDATE()
    
    -- 系统函数
    SELECT SYSTEM_USER() -- 当前用户
    SELECT USER()
    SELECT VERSION
    

    4.2 聚合函数(常用)

    函数名称 描述
    COUNT() 计数
    SUM() 求和
    AVG() 平均值
    MAX() 最大值
    MIN() 最小值
    ... ...
    -- 聚合函数
    -- 以下三种都能统计出表的行数,后两者其实是没区别的
    SELECT COUNT(studentname) FROM student -- 指定列(会忽略所有的NULL值)
    SELECT COUNT(*) FROM student -- *,不会忽略所有的NULL值
    SELECT COUNT(1) FROM student -- 1,不会忽略所有的NULL值
    
    -- 求和等
    SELECT SUM(`StudentResult`) AS 总和 FROM result
    SELECT AVG(`StudentResult`) AS 平均分 FROM result
    SELECT MAX(`StudentResult`) AS 最高分 FROM result
    SELECT MIN(`StudentResult`) AS 最低分 FROM result
    
    思考题(重要,GROUP BY出现)

    查询不同课程的平均分,最高分,最低分,且平均分大于80分

    -- 查询不同课程的平均分,最高分,最低分
    -- 核心难点:根据不同的课程分组
    SELECT `SubjectName`, AVG(StudentResult) AS 平均分, MAX(StudentResult) AS 最高分, MIN(StudentResult) AS 最低分
    FROM result AS r
    INNER JOIN `subject` AS sub
    ON r.`SubjectNo` = sub.`SubjectNo`
    -- 通过什么字段来分组
    GROUP BY r.`SubjectNo` 
    HAVING 平均分 > 80;
    

    4.3 数据库级别的MD5加密(扩展)

    • 加密算法(哈希算法)

    • 不可逆

    CREATE TABLE `testmd5`(
    	`id` INT(4) NOT NULL AUTO_INCREMENT,
        `name` VARCHAR(20) NOT NULL,
        `pwd` VARCHAR(50) NOT NULL,
        PRIMARY KEY(`id`)
    )ENGINE = INNODB DEFAULT CHARSET = utf8
    
    -- 明文密码
    INSERT INTO `testmd5` VALUES(1, 'zhangsan', '123456'),(2, 'lisi', '123456'),(3, 'wangwu', '123456');
    
    -- 加密
    UPDATE `testmd5` SET `pwd` = MD5(pwd) WHERE 1 = 1;
    
    -- 我们应该是在插入的时候加密
    INSERT INTO `testmd5` VALUES(4, 'laowang', MD5('123456'));
    
    -- 如何校验(判断密码是否正确)?
    -- 将用户传递进来的密码进行MD5加密,然后比对加密后的值是否相等
    SELECT * FROM `testmd5` WHERE name = 'wangwu' AND pwd = MD5('123456');
    

    5 MySQL事务

    什么是事务?两个或多个事务,要么全成功,要么全失败

    将一组SQL放在一个批次中去执行

    思考题

    A余额1000元,B余额200元,A打算给B转账200元。

    • SQL执行:A给B转账(A扣200元)
    • SQL执行:B收到A的转账(B加200元)

    事务原则:ACID,即原子性、一致性、隔离性、持久性

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

    • 原子性(Atomicity):AB转账,加减操作都要一起执行,或者一起不执行
    • 一致性(Consistency):AB转账前的和为1200元,转账之后的和也应该为1200元
    • 持久性(Isolation):事务没有提交,则数据恢复到原状;事务已提交,数据持久化到数据库。即事务一旦提交则不可逆
    • 隔离性(Durability):A在给B转账,此时C也在给B转账,那么这两个事务互相不会影响
      • 事务隔离可能引发的问题
        • 脏读:一个事务读取了另外一个事务未提交的数据
        • 丢失修改:B事务读取了A事务还未提交的数据
        • 不可重复读:在一个事务内读取表中某一行数据,多次读取结果不同
        • 幻读:一个事务内读取到了别的事务插入的数据,多次读取结果不同
      • 事务的隔离级别
        • 读取未提交可能会导致脏读、幻读或不可重复读
        • 读取已提交可以阻止脏读,但是幻读或不可重复读仍有可能发生
        • 可重复读(InnoDB引擎默认级别)可以阻止脏读和不可重复读,但幻读仍有可能发生
        • 可串行化该级别可以防止脏读、不可重复读以及幻读

    执行事务

    -- MySQL是默认开启事务自动提交的
    SET autocommit = 0 -- 关闭事务自动提交
    SET autocommit = 1 -- 开启事务自动提交(默认)
    
    -- 手动处理事务
    SET autocommit = 0
    -- 事务开启
    START TRANSACTION
    INSERT INTO 
    
    -- 提交(表示成功,持久化到数据库文件)
    COMMIT
    -- 回滚(表示失败回滚到原来的样子)
    ROLLBACK
    -- 事务结束(还原默认值)
    SET autocommit = 1
    SAVEPOINT '保存点名称' 			  -- 设置一个事务的保存点,方便事务回滚
    RELEASE SAVEPOINT '保存点名称' 	  -- 删除保存点
    ROLLBACK TO SAVEPOINT '保存点名称' -- 回滚到保存点
    

    模拟转账事务

    -- 创建数据库shop
    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('A', 2000.00), ('B', 10000.00);
    
    -- 模拟转账
    SET autocommit = 0;
    START TRANSACTION;  -- 开启一个事务(一组事务)
    UPDATE account SET money = money - 500 WHERE `name` = 'A';  -- A减500元
    UPDATE account SET money = money + 500 WHERE `name` = 'B';  -- B加500元
    -- 提交事务(提交之后就被持久化了,无法回滚)
    COMMIT;
    -- 事务回滚
    ROLLBACK;
    SET autocommit = 1;
    

    6 索引

    MySQL官方对索引的定义:索引(Index)是帮助MySQL高效获取数据的数据结构。

    提取句子主干,就可以得到索引的本质:索引是数据结构

    6.1 索引的分类

    在一个表中,主键索引只能有一个,而唯一索引可以有多个。

    • 主键索引:PRIMARY KEY
      • 唯一标识,主键不可重复,只能有一个列作为主键
    • 唯一索引:UNIQUE KEY
      • 避免重复的列出现(比如两行数据的name相同),唯一索引可以重复,也就是多个列都可以标识为唯一索引
    • 常规索引:KEY/INDEX
      • 默认的,INDEX或者KEY关键字来设置
    • 全文索引:FULLTEXT
      • 在特定的数据库引擎下才有,比如MYISAM
    -- 索引的使用
    -- 在创建表的时候给字段增加索引
    
    
    -- 创建表之后,增加索引
    ALTER TABLE `student` ADD FULLTEXT INDEX `studentName` (`studentName`);
    
    -- 创建表之后,增加索引
    CREATE INDEX 索引名 ON 表(字段)
    CREATE INDEX id_app_user_name ON app_user(`name`);
    
    -- EXPLAIN 分析SQL执行的状况
    EXPLAIN SELECT * FROM student; -- 非全文索引
    EXPLAIN SELECT * FROM student WHERE MATCH(studentName) AGAINST('刘');
    

    6.2 测试索引

    -- 插入100万条数据
    -- 创建一个函数来插入
    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`, `gender`, `pwd`, `age`)
    		VALUES(CONCAT('用户',i), '448751172@qq.com', CONCAT('18', FLOOR(RAND()*((99999999-10000000)+100000000))), 1, UUID(), 23);
    		SET i = i + 1;
    	END WHILE 
    	RETURN i;
    END;
    -- 最后调用函数
    SELECT mock_data();
    
    -- 数据有了,开始测试
    SELECT * FROM `app_user` WHERE `name` = '用户9999';  -- 0.993sec
    SELECT * FROM `app_user` WHERE `name` = '用户9999';  -- 1.098sec
    -- 查看SQL执行情况
    EXPLAIN SELECT * FROM `app_user` WHERE `name` = '用户9999';  -- 发现结果中的row字段等于991749,说明找了991749行才找到
    -- 然后我们创建一个索引来对比一下
    CREATE INDEX id_app_user_name ON app_user(`name`);
    -- 然后我们再查一下建立索引之后的查询时间
    SELECT * FROM `app_user` WHERE `name` = '用户9999';  -- 0.001sec
    -- 查看SQL执行情况
    EXPLAIN SELECT * FROM `app_user` WHERE `name` = '用户9999';  -- 发现结果中的row字段等于1,说明只找了1行就找到了
    

    6.3 索引原则

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

    索引总结

    • 没建索引,基本上就是一行一行地遍历查询,复杂度O(n)
    • 建立索引,就先根据索引值查到数据行的地址,然后根据地址定位出一行数据,复杂度O(1)
    • 索引在小数据量的时候,区别不大,但是在大数据的查询的时候,优化就很明显了

    7 权限管理和备份

    7.1 用户管理

    7.2 MySQL备份

    为什么要备份?

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

    MySQL数据库备份的方式

    • 直接拷贝物理文件(data文件夹)
    • 在可视化工具比如SQLyog导出sql
    • 使用mysqldump命令行导出

    8 规范地设计数据库

    9 三大范式

    第一范式

    原子性:保证每一列不可再分

    第二范式

    前提:满足第一范式的前提下,每张表只描述一件事情

    第二范式需要确保数据表中的每一列数据都和主键相关

    第三范式

    前提:满足第一范式、第二范式

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

    规范性和性能的问题

    关键的表不能超过三张

    • 考虑商业化的需求和目标,数据库的性能更加重要
    • 在规范性能的问题时,需要适当的考虑一下规范性
    • 有时我们会故意给某些表增加一些冗余的字段(从多表查询变为单表查询)
    • 有时我们会故意增加一些计算列(从大数据量降低为小数据量的查询:索引)

    10 JDBC(重点)

    10.1 数据库驱动

    我们的程序会通过数据库驱动,和数据库打交道!

    10.2 JDBC的引入

    Sun公司为了简化开发人员的(对数据库的统一)操作,提供了一套(Java操作数据库的)规范,俗称JDBC

    这些规范的实现由具体的厂商去做,比如MySQL驱动的由MySQL的厂商去实现,Oracle驱动由Oracle厂商去实现。

    对于开发人员来说,我们只需要掌握JDBC的操作规范即可。

    • java.sql
    • javax.sql
    • 还需要导入一个数据库驱动包mysql-connector-java-5.1.14.jar

    10.3 第一个JDBC程序

    创建测试数据库

    1、加载驱动

    2、获取用户信息和URL

    3、获取执行SQL的对象Statement或者PrepareStatement

    4、获取返回的结果集

    5、释放连接

    DriverManager

    // 固定写法,加载驱动
    Class.forName("com.mysql.cj.jdbc.Driver");
    
    // 获取数据库连接对象
    Connection connection = DriverManager.getConnection(url, username, pwd);
    // connection代表数据库
    // 数据库设置自动提交
    connection.setAutoCommit(true);
    // 数据库事务提交
    connection.commit();
    // 数据库事务回滚
    connection.rollback();
    

    URL

    String url = "jdbc:mysql://127.0.0.1:3306/shop?serverTimezone=UTC&useUnicode=true&characterEncoding=utf8&useSSL=true";
    String username = "root";
    String pwd = "123456";
    

    Statement/PrepareStatement 执行SQL的对象

    // 先编写SQL
    String sql = "SELECT * FROM ......";
    
    statement.executeQuery();  // 查询操作返回结果集
    statement.execute();       // 执行任何SQL
    statement.executeUpdate(); // 更新、插入、删除都是用这个,返回一个受影响的行数
    

    ResultSet 查询的结果集

    resultSet.next(); 	   // 移动到下一行数据
    resultSet.previous();  // 移动到前一行数据
    

    释放资源

    // 很消耗资源,用完即释放
    resultSet.close();
    statement.close();
    connection.close();
    

    10.4 JdbcUtils工具类

    package zr.utils;
    
    import java.io.InputStream;
    import java.sql.*;
    import java.util.Properties;
    
    public class JdbcUtils {
        private static String driver = null;
        private static String url = null;
        private static String username = null;
        private static String password = null;
    
        static {
            try{
                // 通过反射可以拿到src目录下的所有文件
                InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
                Properties properties = new Properties();
                properties.load(in);
    
                driver = properties.getProperty("driver");
                url = properties.getProperty("url");
                username = properties.getProperty("username");
                password = properties.getProperty("password");
    
                // 1. 驱动只需要加载一次
                Class.forName(driver);
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 获取数据库连接
         * @return
         * @throws SQLException
         */
        public static Connection getConnection() throws SQLException {
            return DriverManager.getConnection(url, username, password);
        }
    
        /**
         * 释放连接
         * @param conn
         * @param st
         * @param rs
         */
        public static void release(Connection conn, Statement st, ResultSet rs){
            if(rs != null){
                try{
                    rs.close();
                } catch (SQLException e){
                    e.printStackTrace();
                }
            }
            if(st != null){
                try{
                    st.close();
                } catch (SQLException e){
                    e.printStackTrace();
                }
            }
            if(conn != null){
                try{
                    conn.close();
                } catch (SQLException e){
                    e.printStackTrace();
                }
            }
        }
    }
    

    10.5 Statement对象

    执行SQL的对象,容易出现SQL注入的问题,因为是直接在字符串中写sql,可能会将一些逻辑符号也作为string复制进去执行

    package zr.mysql.test;
    import zr.utils.JdbcUtils;
    
    import java.sql.Connection;
    import java.sql.ResultSet;
    import java.sql.SQLException;
    import java.sql.Statement;
    
    public class TestInsert {
        public static void main(String[] args) {
            Connection conn = null;
            Statement st = null;
            ResultSet rs = null;
    
            try{
                // 获取数据库连接
                conn = JdbcUtils.getConnection();
                // 获取SQL的执行对象
                st = conn.createStatement();
                String sql = "INSERT INTO account (`name`, `money`) VALUES('C', 2000.0)";
                int i = st.executeUpdate(sql);
                if(i > 0){
                    System.out.println("插入成功...");
                }
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JdbcUtils.release(conn, st, rs);
            }
        }
    }
    

    10.6 PrepareStatement对象

    可以防止SQL注入, 并且效率更高。将所有的参数全作为string处理,所以不会出现将逻辑符号参数作为逻辑符号处理。

    package zr.mysql.test;
    import zr.utils.JdbcUtils;
    import java.sql.*;
    
    public class TestPrepareST {
        public static void main(String[] args) {
    
            Connection conn = null;
            PreparedStatement st = null;
            ResultSet rs = null;
    
            try{
                // 获取数据库连接
                conn = JdbcUtils.getConnection();
    
                String sql = "INSERT INTO account (`name`, `money`) VALUES(?, ?)";
                // 区别,使用?占位符代替参数
                st = conn.prepareStatement(sql); // 预编译SQL,先写SQL,不执行
                // 手动给参数赋值
                st.setString(1, "D");
                st.setDouble(2, 3000.0);
    
                // 注意,这里不用再传参了
                int i = st.executeUpdate();
                if(i > 0){
                    System.out.println("插入成功...");
                }
    
            } catch (SQLException e) {
                e.printStackTrace();
            } finally {
                JdbcUtils.release(conn, st, rs);
            }
        }
    }
    
  • 相关阅读:
    ASP.NET 作业题
    作业题
    作业题...
    作业题
    控件属性
    ASP控件解释
    排序
    5. 用自己的语言描述一下程序连接数据库的过程。
    4. 什么是AJAX
    3.怎样计算页面执行的时间?
  • 原文地址:https://www.cnblogs.com/flyingrun/p/13766710.html
Copyright © 2020-2023  润新知