MySQL笔记

MySQL笔记

初识MySQL

javaEE:企业级java开发 web

前台(页面 :展示,数据)

后台(连接点:连接数据库JDBC,连接前端(控制 ,控制视图跳转))

数据库(存数据,txt,Excel,world)

只会写代码,学好数据库,基本混饭吃

操作系统,数据结构与算法 !当一个不错的程序猿

离散数据、数据电路、编译原理、实战经验 :高级程序猿-优秀的程序猿

为什么学习数据库

1、岗位技能需求

2、现在的世界,得数据者得天下

3、被迫需求:存储数据

4、程序,网站中,大量数据如何长久保存?

5、**数据库是几乎软件体系中最核心的一个存在。**DBA

1、数据库

什么是数据库

数据库 ( DataBase , 简称DB )

概念 : 数据仓库,软件,安装在操作系统之上,长期存放在计算机内,有组织,可共享的大量数据的集合,是一个数据 “仓库”。500万一下

作用 : 保存数据,并能安全管理数据(如:增删改查等),减少冗余…

数据库总览 :

  • 关系型数据库 ( SQL )

    • MySQL , Oracle , SQL Server , SQLite , DB2 , …
    • 关系型数据库通过外键关联来建立表与表之间的关系
    • 通过表和表之间,行和列之间的关系进行数据的存储
  • 非关系型数据库 ( NOSQL:not only sql )

  • Redis , MongoDB , …

  • 非关系型数据库通常指数据以对象的形式存储在数据库中,而对象之间的关系通过每个对象自身的属性来决定

什么是DBMS

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

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

在这里插入图片描述

为什么要说这个呢?

因为我们要学习的MySQL应该算是一个数据库管理系统.

MySQL简介

在这里插入图片描述

概念 : 是现在流行开源的,免费关系型数据库

历史 : 由瑞典MySQL AB 公司开发,目前属于 Oracle 旗下产品。

特点 :

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

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

安装步骤

安装建议:尽量不要用exe安装

安装网址:https://dev.mysql.com/downloads/mysql/

1、下载后得到zip压缩包.

2、解压到自己想要安装到的目录,本人解压到的是D:\Environment\mysql-5.7.19

3、添加环境变量:我的电脑->属性->高级->环境变量

选择PATH,在其后面添加: 你的mysql 安装文件下面的bin文件夹

4、新建 my.ini 文件 ,注意替换路径位置

[mysqld]
#目录一定要换成自己的
basedir=D:\Program Files\mysql-5.7\(自己的安装目录需要修改)
datadir=D:\Program Files\mysql-5.7\data\(自己的安装目录需要修改,data目录自动生成)
port=3306
skip-grant-tables

5、启动管理员模式下的CMD,并将路径切换至mysql下的bin目录,然后输入mysqld –install (安装mysql)

6、再输入 mysqld --initialize-insecure --user=mysql 初始化数据文件

7、然后再次启动mysql 输入命令:net start mysql 然后用命令 mysql –u root –p 进入mysql管理界面(密码可为空)

8、进入界面后更改root密码

update mysql.user set authentication_string=password('123456') where user='root' and Host = 'localhost';

9、刷新权限

flush privileges;

10、修改 my.ini文件删除最后一句skip-grant-tables,或者用#注释掉

11、重启mysql即可正常使用

net stop mysql
net start mysql

12、连接上测试出现以下结果就安装好了

在这里插入图片描述

一步步去做 , 理论上是没有任何问题的 .

如果您以前装过,现在需要重装,一定要将环境清理干净 .

好了,到这里大家都装好了,因为刚接触,所以我们先不学习命令.

这里给大家推荐一个工具 : SQLyog .

即便有了可视化工具,可是基本的DOS命名大家还是要记住!

SQLyog

可手动操作,管理MySQL数据库的软件工具

特点 : 简洁 , 易用 , 图形化

在这里插入图片描述

在这里插入图片描述

使用SQLyog管理工具自己完成以下操作 :

  • 连接本地MySQL数据库

  • 新建MySchool数据库

    • 字段

    • GradeID : int(11) , Primary Key (pk)

    • GradeName : varchar(50)

    • 数据库名称MySchool

    • 新建数据库表(grade)

在历史记录中可以看到相对应的数据库操作的语句 .

连接数据库

命令行连接!

mysql -u root -p123456 --连接数据库
alter user root@localhost identified by '123456'; --修改用户密码
flush privileges; --刷新权限

------------------------------------
--所有的语句都用;结尾
show databases;--查看所有的数据库
use school; --use 数据库名 切换数据库

show tables;--查看所有的表
desc student;--查看名为student表信息
ctrl c --强行终止运行程序

create database westos; --创建一个名为westos的数据库 
exit;--退出链接
--单行注释
/*
多行注释
*/

**数据库xxx语言 **

DDL 定义

DML 操作

DQL 查询

DCL 控制

CURD增删改查(业务) CV 程序猿 (复制粘贴) API 程序猿

2、操作数据库

2.1、操作数据库(了解)

1.创建数据库

CREATE DATABASE IF NOT EXISTS westos

2.删除数据库

DROP DATABASE IF EXISTS westos

3.使用数据库

USE `school` --加单引号会变成字段 特殊的字符一定加这个符号`

4.查看数据库

SHOW DATABASES --查看所有的数据库
  • 固定的语法要强行记住

2.2、数据库的列类型

数值

  • tinyint 十分小的数据 1个字节
  • smallint 较小的数据 2个字节
  • mediumint 中等大小的数据 3个字节
  • int 标准的整数 4个字节 常用的
  • bigint 较大的数据 8个字节(对应java中的Lang)
  • float 浮点数 4个字节
  • double 浮点数 8个字节
  • decimal 字符串形式的浮点数 金融计算的时候 一般用decimal

字符串

  • char 字符串固定大小 0~255
  • varchar 可变字符串大小 0~65535 对应String 常用
  • tinytext 微型文本 2^8-1
  • text 文本串 2^16 -1 保存大文本

时间日期

  • data YYYY-MM-DD,日期格式
  • time HH:mm:ss 时间格式
  • datetime YYYY-MM-DD HH:mm:ss 最常用的时间格式
  • timestamp 时间戳 1970.1.1到现在的毫秒数 较为常用
  • year 年份标识

null

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

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

Unsigned:

  • 无符号的整数
  • 声明了该列不能为负数

zerofill

  • 0填充的
  • 不足的位数,使用0来填充, int(3), 5— 005

自增:

  • 通常理解为自增,自动在上一条记录的基础上+1(默认)
  • 通常用来设计唯一的逐渐~ index,必须是整数类型
  • 可以自定义设计主键自增的起始值和步长

非空null not null

  • 假设设置为not null,如果不给它赋值,就会报错!
  • null 如果不填写值,默认就是null!

默认:

  • 设置默认的值
  • sex,默认值为男,如果不指定该列的值,则会有默认的值!

2.4、创建数据库表(重点)

/* 每个表,都需要存在以下五个字段  未来做项目用的,表示一个记录存在的意义  拓展
id 主键
'version'  乐观锁
is_delete 伪删除   认为被删除  实际没有
gmt_create 创建时间
gmt_update 修改时间
*/
--AUTO_INCREMENT 自增
--PRIMARY KEY 主键,一般一个表只有一个唯一的主键
--所有的字符串使用单引号括起来
--所有的语句后面加逗号(,),最后一个不用加
--DEFAULT 默认值
--COMMENT 解释
CREATE TABLE IF NOT EXISTS `student`(
	`id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
	`name` VARCHAR(30) NOT NULL DEFAULT'匿名' COMMENT'姓名',
	`paw` 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

格式

create table [if not exists] `表名`(
    `字段名` 列表名 [属性] [索引] [注释],
    `字段名` 列表名 [属性] [索引] [注释],
    ......
    `字段名` 列表名 [属性] [索引] [注释]
)[表类型][字符设置][注释]

常用命令

show create database school  -- 查看创造数据库的语句
show create table student -- 查看student数据表定义的语句
desc student -- 显示表的结构

2.5、数据表的类型

-- 关于数据库引擎
/*
INNODB 默认使用
MYISAM 早些年是用的
*/
MYISAMINNODB
事务支持不支持支持
数据行锁定不支持支持
外键约束不支持支持
全文索引支持不支持
表空间大小较小较大,约为前者两倍

常规使用操作:

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

2.6、修改和删除表

修改

-- 修改表名 ALTER TABLE 旧表名 RENAME AS 新表名
ALTER TABLE teacher RENAME AS teacher1
-- 增加表的字段 ALTER TABLE 表名 ADD 字段名 列属性
ALTER TABLE teacher1 ADD age INT(11)
-- 修改表的字段(重命名,修改约束)
-- ALTER TABLE 表名 MODIFY 字段名 列属性[]
ALTER TABLE teacher1 MODIFY age VARCHAR(11) -- 修改约束
-- ALTER TABLE 表名 CHANGE 旧名字 新名字 列属性[]
ALTER TABLE teacher1 CHANGE age age1 INT(11) -- 重命名,

-- 删除表的字段 表名 ALTER TABLE 表名 DROP 字段名
ALTER TABLE teacher1 DROP age1

删除

-- 删除表 DROP TABLE 表名(如果表存在再删除)
DROP TABLE [if exists] teacher1

所有的创建和删除操作尽量加上判断,以免报错~

注意点:

  • ``字段名,使用这个包裹
  • 注释 – 行注释 或者 /* 块注释 */
  • sql大小写敏感,建议使用小写字母
  • 所有的符号用英文

3、MySQL数据管理

3.1、外键(了解即可)

方式一、在创建表的时候,增加约束(麻烦,比较复杂)

CREATE TABLE `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 IF NOT EXISTS `student`(
	`id` INT(4) NOT NULL AUTO_INCREMENT COMMENT '学号',
	`name` VARCHAR(30) NOT NULL DEFAULT'匿名' COMMENT'姓名',
	`paw` 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`), -- FK_ 外键约束规定
	CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade`(`gradeid`)
)ENGINE=INNODB DEFAULT CHARSET=utf8

删除有外键关系的表的时候,必须要先删除引用别人的表,再删除被引用的表

方式二、建完表以后,添加外键约束

ALTER TABLE `student`
ADD CONSTRAINT `FK_gradeid` FOREIGN KEY(`gradeid`) REFERENCES `grade`(`gradeid`)
-- ALTER TABLE 表 ADD CONSTRAINT 约束名 FOREIGN KEY (作为外键的列) REFERENCES 哪个表(引用的列)

上的操作都是物理外键,数据库级别的外键,不建议使用!(避免数据库过多困扰)

最佳实践

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

3.2、DML语言(全部记住)

数据库的意义:数据存储,数据管理

DML语言:数据操作语言

  • insert 添加
  • update 修改
  • delete 删除

3.3、添加

insert

语法:INSERT INTO 表名 ([列1],[列2],[列3]) VALUES ('字段1','字段2','字段3'),('字段1','字段2','字段3')

-- 一般写插入语句,我们一定要数据和字段一一对应!
-- INSERT INTO 表名 ([列1],[列2],[列3]) VALUES ('字段1','字段2','字段3'),('字段1','字段2','字段3')
INSERT INTO `grade` (`gradename`) VALUES ('大二'),('大三')

注意事项:

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

3.4、修改

update 修改谁 (条件) set原来的值=新值

-- 修改学院名字,带了简介
UPDATE `student` SET `name`='狂神'WHERE id=1;

-- 不指定条件的情况下,会改动所有的值
UPDATE `student` SET `name`='长江7号'
-- 修改多个值要用逗号隔开
-- 语法:
-- update 表名 set 列=值,[列=值],[列=值] where [条件]

操作符返回的是布尔值

操作符含义范围结果
=等于5=6false
<> 或 !=不等于5<>6true
between…and…[ ]闭合区间,在某个范围内
and相当于 &&5>1 and 1>2false
or相当于 ||5>1 or 1>2true

注意:

  • colnum_name 是数据库的列,尽量带上``
  • 条件,筛选的条件,如果没有指定,则会修改所有的列
  • value,是一个具体的值,也可以是一个变量(一般只有时间会用)
  • 多个设置的属性之间,使用英文逗号隔开

3.5、删除

--  删除数据(避免这样写,会全部删除)
delete from `student`

-- 删除指定数据
delete from `student` where id=1;
-- 语法
delete from 表名 [where 条件]

TRUNCATE(truncate)命令

作用:完全清空数据库,表的结构和索引约束不会变!

-- 清空student表
TRUNCATE `student`

delete和TRUNCATE不同

  • 不同
    • TRUNCATE 重新设置自增列 计数器会归零
    • TRUNCATE 不会影响事务
  • 相同点
    • 都能删除数据,都不会删除表结构
DELETE FROM `test` -- 不会影响自增
TRUNCATE TABLE `table` -- 自增会归零

了解即可:DELETE删除的问题,重启数据库,现象

  • innoDB 自增列会从1开始(存在内存中的,断电即失)
  • MyISAM 继续从上一个自增量开始。(存在文件中,不会丢失)

4、DQL查询数据(最重点)

4.1、DQL

(Data Query LANGUAGE:数据查询语言)

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

select 完整的语法

select语法

SELECT [ALL | DISTINCT]
{*l table.* l [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 l row_countOFFSET offset]];--指定查询的记录从哪条至哪条
-- 位置有严格要求,where一定在left下面

4.2、指定查询字段

-- 查询全部的学生 select 字段 from 表
SELECT * FROM student

-- 查询指定字段
SELECT `studentno`,`studentname` FROM student 

-- 别名,给结果起一个名字 AS
SELECT `studentno` AS 学号,`studentname`
AS 学生姓名 FROM student -- 表也可以加AS 给表起一个别名

-- 函数 concat(a,b)
SELECT CONCAT('姓名:',studentname) AS 新名字 FROM student

语法:select 字段 from 表

有的时候表的名字不容易看出来 所以起一个别名好看出来AS 字段名 as 别名 表名 as 别名

去重复:DISTINCT

作用:去除select查询出来的结果中重复的数据,重复的数据只显示一条

-- 查询一下有哪些同学参加了考试
SELECT * FROM result -- 查询全部的考试成绩
SELECT `studentno` FROM result 
-- 去重复
SELECT DISTINCT `studentno` FROM result 

数据库的列(表达式)

SELECT VERSION() -- 查询系统版本(函数)

SELECT 100*3-1 AS 计算结果 -- 用来计算(表达式)

SELECT @@auto_increment_increment  -- 查询自增的步长(变量)
-- 学员考试成绩加1分
select `StudentNo`,`StudentResult`+1 as `提分后` from result

数据库中的表达式:文本值、列、null、函数、计算表达式、系统变量…

select 表达式 from 表名

4.3、where条件子句

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

逻辑运算符

运算符语法描述
and 或者 &&a and b a&&b逻辑与,两个都为真,结果为真
or 或者 ||a or b a||b逻辑或,其中一个为真,则结果为真
not 或者 !not a !a逻辑非,取反,真为假,假为真

尽量使用英文字母

模糊查询:比较运算符

image-20220209160359549

-- 模糊查询
-- like结合 %(代表0到任意个字符) _(代表一个字符)
-- in(具体的一个值或者多个值)

4.4、连表查询

1、join连接(多表查询)

在这里插入图片描述

-- 联表查询 join  
-- join(连接的表) on(判断的条件) 连接查询
-- where 等值查询

-- INNER JOIN
SELECT s.studentno,studentname,subjectno,studentresult
FROM student AS s
INNER JOIN result AS r
ON s.studentno = r.studentno
-- Right JOIN
SELECT s.studentno,studentname,subjectno,studentresult
FROM student AS s
RIGHT JOIN result AS r
ON s.studentno = r.studentno
-- Left JOIN
SELECT s.studentno,studentname,subjectno,studentresult
FROM student AS s
LEFT JOIN result AS r
ON s.studentno = r.studentno
-- 我要查询那些数据 select 。。。
-- 要从那些表中查询 from 表 xxx Join 连接的表 on 交叉条件
-- 假设存在一种多张表查询,慢慢来,先查询两张表然后再慢慢增加
-- from a left join b 以左边的表即a表为基准
-- from a right join b 以右边的表即b表为基准
操作描述
inner join如果表中至少又一个匹配吗,就返回行
left join会从左表中返回所有的值,即使右表中没有匹配
right join会从右表中返回所有的值,即使左表中没有匹配

2、自连接(了解)

自己跟自己连接,核心:一张表拆分为两张一样的表即可

父类

categoryidcategoryName
2信息技术
3软件开发
5美术设计

子类

pidcategoryidcategoryName
34数据库
28办公信息
36web开发
57PS技术

操作:查询父类对应的子类关系

父类子类
信息技术办公信息
软件开发数据库
软件开发web开发
美术设计PS技术
-- 查询父子信息,把一个表看成两个一模一样的表
select a.categoryName AS '父栏目',b.categoryName AS '子栏目'
from category AS a ,category AS b
where a.categoryid = b.pid

3、排序

排序

-- 分页 limit 和排序 order by
-- 排序:升序ASC,降序DESC
-- 排序语法
-- slect * from 表名 order by 字段名 ASC/DESC

4、分页

-- 为什么要分页?
-- 缓解数据库的压力,给人的体验更好,不分页就是瀑布流
-- 语法:limit 起始值,页面大小 
-- limit 0,5 从第1个开始 每页五个
-- 1,5  从第二个开始 每页五个
-- 第一页    limit 0,5
-- 第二页    limit 5,5
-- 第n页     limit (n-1)*pageSize,pageSize
-- pageSize:代表页面大小 (n-1)*pageSize:代表起始值 n:代表当前页 总页数=数据总数/页面大小

5、子查询

where (这个值是计算出来的)

本质:在where 语句中嵌套一个子查询语句

where(slect * )

-- 1、查询数据库结构的所有考试结果(学号、科目编号、成绩),降序排列
-- 方式一
select studentNo,subjectNostudentResult
from result as r
inner join subject sub
on r.subjectNo = sub.subjectNo
where subjectName='数据库结构'
order by studentResult desc
-- 方二:子查询(由里及外)
select studentNo,subjectNostudentResult
from result 
where subjectNo =(
    select subjectNo from subject where subjectName='数据库结构'
    )
                  

5、聚合函数

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、聚合函数(常用)

函数名称描述
COUNT()计数
SUM()求和
AVG()平均值
MAX()最大值
MIN()最小值
-- 聚合函数
/*COUNT:非空的*/
SELECT COUNT(studentname) FROM student;/*指定列*/
SELECT COUNT(*) FROM student;/*查询全部*/
SELECT COUNT(1) FROM student;  /*推荐*/
 
-- 从含义上讲,count(1) 与 count(*) 都表示对全部数据行的查询。
-- count(字段) 会统计该字段在表中出现的次数,忽略字段为null 的情况。即不统计字段为null 的记录。
-- count(*) 包括了所有的列,相当于行数,在统计结果的时候,包含字段为null 的记录,不会忽略null值。
-- count(1) 用1代表代码行,在统计结果的时候,包含字段为null 的记录 ,不会忽略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 r
inner join subject sub
on r.subjectNo=sub.subjcetNo
group by r.subjectNo -- 通过什么字段来分组
having 平均分>80

5.3、数据库级别的MD5加密

什么是MD5?

MD5信息摘要算法(英语:MD5 Message-Digest Algorithm),一种被广泛使用的密码散列函数,可以产生出一个128位(16字节)的散列值(hash value),用于确保信息传输完整一致。MD5由美国密码学家罗纳德·李维斯特(Ronald Linn Rivest)设计,于1992年公开,用以取代MD4算法。这套算法的程序在 RFC 1321 标准中被加以规范。1996年后该算法被证实存在弱点,可以被加以破解,对于需要高度安全性的数据,专家一般建议改用其他算法,如SHA-2。2004年,证实MD5算法无法防止碰撞(collision),因此不适用于安全性认证,如SSL公开密钥认证或是数字签名等用途。

主要增强算法复杂度和不可逆性

MD5不可逆,具体的值的MD5是一样的

MD5破解网站的原理,背后有一个字典,MD5加密后的值,加密的前值

-- md5加密
create table testmd5(
 id int(4) not null,
 name varchar(20) not null,
 pwd varchar(50) not null,
 primary key(id)
)
-- 明文密码
insert into testmd5 values(1,'zhangsan','123456'),(2,'lisi','123456'),(3,'wangwu','123456')
-- 加密
update testmd5 set pwd = MD5(pwd) -- 加密了
-- 插入的时候加密
insert into testmd5 values(4,'xiaoming',MD5('123456'))

-- 如何校验;将用户传递进来的密码,进行MD5加密,然后对比加密后的值
select * from testMD5 where name='xiaoming' and pwd=MD5('123456') 

5.4、select小结

SELECT [ALL | DISTINCT]
{*l table.* l [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 l row_countOFFSET offset]];--指定查询的记录从哪条至哪条
-- 位置有严格要求,where一定在left下面
-- 解释
select 去重 要查询的字段 from 表(注意表和字段可以取别名)
xxx join 要连接的表 on 等值判断
where (具体的值,子查询语句)
group by (通过那个字段分组)
having (过滤分组后的信息,条件和where 是一样的,位置不同而已)
order by (通过那个字段排序)[升序/降序]
limit startIndex,pageSize
--SQL执行顺序
select 
	字段列表	4
from 
	表名列表	1
where
	条件列表	2
group by
	分组字段列表	3
having
	分组后条件列表
order by
	排序字段列表	5
limit 
	分页参数	6

6、事务

6.1、要么都成功要么都失败

1、sql执行 A给B 转账 A 1000–>200 B 200

2、sql执行 B收到A的钱 A 800–> B 400

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

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

事务理解 :https://blog.csdn.net/dengjili/article/details/82468576

在这里插入图片描述

-- mysql 是默认开启事务自动提交的
SET autocommit = 0; /* 关闭自动提交 */
SET autocommit = 1; /* 开启(默认的) */


-- 手动处理事务
SET autocommit = 0; -- 先关闭自动条件


-- 事务开始
START TRANSACTION  -- 标记一个事物的开始,从这之后的sql都在一个事物内


-- 操作
insert xxx
insert xxx


-- 提交: 持久化(成功!)
COMMIT


-- 回滚: 回到原来的样子(失败!)
ROLLBACK


-- 事物结束
SET autocommit = 1; -- 结束后开启自动提交


-- 了解
SAVEPOINT 保存点名  -- 设置一个事物的保存点
ROLLBACK TO SAVEPOINT -- 回滚到保存点
RELEASE SAVEPOINT -- 撤销保存点

模拟场景

-- 转账
set autocommit = 0; /* 关闭自动提交 */
START TRANSACTION  -- 标记一个事物的开始,从这之后的sql都在一个事物内
-- 操作
update accoount set money=money-500 where name = 'A' --A减500
update accoount set money=money+500 where name = 'B' --A减500
commit;-- 提交事务,事务一但提交就被持久化了
rollback;-- 回滚
set autocommit = 1;--恢复自动提交

7、索引

MySQL官方对索引的定义为:**索引(Index)是帮助MySQL高效获取数据的数据结构。**提取句子主干,就可以得到索引的本质:索引是数据结构。

7.1、索引的分类

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

  • 主键索引 (PRIMARY KEY)

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

    • 避免重复的列出现,唯一索引可以重复,多个列都可以标识位
  • 常规索引 (KEY/INEDEX)

    • 在特定的数据库引擎下有
    • 快速定位数据
  • 全文索引 (FULLTEXT)

SHOW INDEX FROM student  -- 显示所有的索引信息

-- 增加一个全文索引 (索引名)列名 `wuhu`索引名 `studentname`列名
ALTER TABLE school.`student` ADD FULLTEXT INDEX `wuhu`(`studentname`)

-- EXPLAIN 分析sql执行的状况
EXPLAIN SELECT * FROM student;  -- 非全文索引
-- 语法格式
EXPLAIN SELECT * FROM 表名 WHERE MATCH(列名) against(值)
-- 例子
EXPLAIN SELECT * FROM student WHERE MATCH(studentName) against('刘')

测试索引

-- 插入一百万条数据‘
delimiter $$ -- 写函数之前必须要写,标志
create function mock_data()
returns int
begin
   declare num int default 1000000;-- 定义num,设置num的默认值为一百万
   declare i int default 0;
   while i<num do
   insert into () values ();-- 插入语句操作
   set i = i+1;
   end while
   return i;
end;

SELECT * FROM app_user WHERE `name` = '用户99999' -- 1.191 sec
SELECT * FROM app_user WHERE `name` = '用户99999' -- 1.070 sec

EXPLAIN SELECT * FROM app_user WHERE `name` = '用户99999' -- 1.070 sec

EXPLAIN SELECT * FROM app_user

-- id_表名_字段名 规范
-- CREATE INDEX 索引名 on 表(字段)
CREATE INDEX id_app_user_name ON app_user(`name`);
SELECT * FROM app_user WHERE `name` = '用户99999'  -- 0.006 sec

索引在小数据量的时候,用处不大,但是在大数据的时候,区别十分明显~

7.3、索引原则

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

索引的数据结构

博客地址:https://blog.csdn.net/qq_36381855/article/details/80011876

8、权限管理和备份

8.1、用户管理

SQL可视化管理

在这里插入图片描述

SQL 命令操作

-- 创建用户 create user 用户名 identified by 密码
CREATE USER shijie IDENTIFIED BY '123456'

-- 修改密码(修改当前用户密码)
SET PASSWORD = PASSWORD('123456')

-- 修改密码(修改指定用户密码)set password for 用户 = password(新密码)
SET PASSWORD FOR shijie = PASSWORD('123456')

-- 重命名  RENAME USER 原用户名 TO 新用户名
RENAME USER shijie TO shijie2

-- 用户授权  GRANT ALL PRIVILEGES(全部权限)库.表 TO 用户
-- ALL PRIVILEGES 除了给别人授权,其他都能干
GRANT ALL PRIVILEGES *.* TO -- 全部库,全部表

-- 查询权限 SHOW GRANTS FOR 用户
SHOW GRANTS FOR kuangshen -- 查看指定用户的权限
SHOW GRANTS FOR root@localhost -- root用户要加@地址

-- 撤销权限 REVOKE 权限,在哪个库,哪个表撤销,给谁撤销
REVOKE ALL PRIVILEGES ON *.* FROM shijie

-- 删除用户
DROP USER shijie 

8.2、 MySQL备份

为什么要备份:

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

MySQL数据库备份方式

  • 直接拷贝物理文件
  • 在可视化工具上导出
  • 使用命令行导出 mysqldump 命令行 cmd才叫命令行

在这里插入图片描述

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

# 导入
# 登录的情况下,切换到指定的数据库
# source 备份文件
source d:a.sql

9、规范数据库设计

9.1、为什么要需要设计

当数据库比较复杂的时候,就需要设计了

糟糕的数据库设计:

  • 数据冗余,浪费空间
  • 数据的插入和删除都会麻烦、异常【避免使用物理外键】
  • 程序的性能差

良好的数据库设计:

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

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

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

设计数据库的步骤:(个人博客)

在这里插入图片描述

9.2、三大范式

为什么需要数据规范化

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

范式

三大范式博客:https://www.cnblogs.com/linjiqin/archive/2012/04/01/2428695.html

第一范式(1NF)

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

第二范式(2NF)

前提:满足第一范式

每张表只表示一件事

第三范式(3NF)

前提:满足第一范式

前提:满足第二范式

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

规范数据库的设计

规范性 和 性能的问题 关联查询的表不要超过三张

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

10、JDBC(重点)

10.1、数据库驱动

驱动:声卡、显卡 、数据库

image-20220214140849058

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

10.2、 jdbc

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

这些规范的实现由具体的厂商去做!

对于开发人员来说,只需要掌握JDBC接口的操作即可

image-20220214141406788

10.3、第一个JDBC程序

1、创建测试数据库

1、创建一个普通项目

2、导入数据库驱动

2、测试代码

package com.kuang.lesson01;

import java.sql.*;

//我的第一个JDBC程序
public class JdbcFirstDemo {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //1.加载驱动
        Class.forName("com.mysql.cj.jdbc.Driver");// 固定写法,加载驱动(8.0版本)
       // Class.forName("com.mysql.jdbc.Driver");// 固定写法,加载驱动(5.7版本)

        //2.用户信息和url
        //useUnicode=true&characterEncoding=utf8&useSSL=true
        //支持中文编码    &设定字符为utf8          &使用安全连接        jdbcstudy是数据库名
        String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
        String username ="root";
        String password ="123456";

        //3.连接成功,数据库对象  Connection 代表数据库
        Connection connection = DriverManager.getConnection(url,username,password);

        //4.执行SQL的对象
        Statement statement= connection.createStatement();

        //5.执行SQL的对象去执行SQL,可能存在结果,查看返回结果
        String sql ="SELECT * FROM USERS";

        ResultSet resultSet = statement.executeQuery(sql);//返回的结果集,结果集中封装了我们全部的查询出来的结果

        while (resultSet.next()){
            System.out.println("====================================================");
            System.out.println("id="+resultSet.getObject("id"));
            System.out.println("name="+resultSet.getObject("NAME"));
            System.out.println("pwd="+resultSet.getObject("PASSWORD"));
            System.out.println("email="+resultSet.getObject("email"));
            System.out.println("birth="+resultSet.getObject("birthday"));
        }

        //6.释放连接
        resultSet.close();
        statement.close();
        connection.close();
    }
}

步骤总结:

1、加载驱动

2、链接数据库DriverManager

3、执行sql的对象 statement

4、获得返回的结果集

5、释放链接

DriverManger

//DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Class.forName("com.mysql.cj.jdbc.Driver");// 固定写法,加载驱动(8.0版本)

Connection connection = DriverManager.getConnection(url,username,password);

//connection代表数据库
//数据库设置自动提交
//事务提交
//事务提醒
    connection.rollback();
    connection.commit();
    connection.setAutoCommit();
//数据库能干的connection都能干

url

String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
//mysql 默认端口3306
//协议://主机地址:端口号/数据库名?参数1&参数2&参数3
//oracle默认端口1521
//jdbc:oracle:thin:@localhost:1521:sid

connection

Connection connection = DriverManager.getConnection(url, username, password);
// 设置自动提交
// 事务提交
// 事务回滚
connection.setAutoCommit();
connection.rollback();
connection.commit();

Statement执行SQL的对象 PrepareStatement执行SQL的对象

String sql= "SELECT*FROM users";//编写sql
statement.executeQuery();//查询操作返回ResultSet
statement.execute();//执行任何sql,效率会低一点
statement.executeUpdate();//更新、插入、删除都用这个,返回一个受影响的行数

resultSet查询的结果集:封装了所有的查询结果

resultSet.getObject();//不知道列类型用Object
//如果知道列的类型使用指定的类型
resultSet.getObject();
resultSet.getString();
resultSet.getInt();
resultSet.getFloat();
resultSet.getDate();
resultSet.getObject();
....

遍历、指针

resultSet.beforeFirst();// 移动到最前面
resultSet.afterLast();// 移动到最后面
resultSet.next(); //移动到下一个
resultSet.previous();//移动到前一行
resultSet.absolute();//移动到指定行

释放资源

resultSet.close();
statement.close();
connection.close(); // 耗资源,用完关掉

10.4、statement对象

jdbc中的statement对象用于向数据库发送SQL语句,想完成对数据库的增删改查,只需要通过这个对象向数据库发送增删改查语句即可。

Statement对象的executeUpdate方法,用于向数据库发送增、删、改的sql语句,executeUpdate执行完后,将会返回一个整数(即增删改语句导致了数据库几行数据发生了变化)。

Statement.executeQuery方法用于向数据库发送查询语句,executeQuery方法返回代表查询结果的ResultSet对象。

CRUD操作-create

使用executeUpdate(String sql)方法完成数据添加操作,示例操作

Statement st =conn.createStatement();
String sql ="insert into user(...)values(...)";
int num =st.executeUpdate(sql);
if(num>0){
    System.out.println("插入成功!!");
}

CRUD操作-delete

使用executeUpdate(String sql)方法完成数据删除操作,示例操作:

Statement st=conn.createStatement();
String sql ="delete from user where id=1";
int num =st.executeUpdate(sql);
if(num>0){
    System.out.println("删除成功!!")
}

CRUD操作-update

Statement st=conn.createStatement();
String sql ="update user set name='' where name=''";
int num =st.executeUpdate(sql);
if(num>0){
    System.out.println("修改成功!!")
}

CRUD操作-read

Statement st=conn.createStatement();
String sql ="select*from user where id=1";
ResultSet rs=st.executeQuery(sql);
while(rs.next()){
    //根据获取列的数据类型,分别调用rs的相应方法映射到Java对象中
}

封装工具类代码实现

1、创建一个db.properties文件在src目录下,等下引用更方便

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456

2、提取工具类

package com.kuang.lesson02.utils;
import java.io.IOException;
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;
</span><span style="color: #0000ff;">static</span><span style="color: #000000;"> {
    </span><span style="color: #0000ff;">try</span><span style="color: #000000;">{
        InputStream in </span>= JdbcUtils.<span style="color: #0000ff;">class</span>.getClassLoader().getResourceAsStream("db.properties"<span style="color: #000000;">);
        Properties properties </span>= <span style="color: #0000ff;">new</span><span style="color: #000000;"> Properties();
        properties.load(in);

        driver</span>= properties.getProperty("driver"<span style="color: #000000;">);
        url</span>= properties.getProperty("url"<span style="color: #000000;">);
        username</span>= properties.getProperty("username"<span style="color: #000000;">);
        password</span>= properties.getProperty("password"<span style="color: #000000;">);

        </span><span style="color: #008000;">//</span><span style="color: #008000;">1.驱动只用加载一次</span>

            Class.forName(driver);
}catch (IOException | ClassNotFoundException e){
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url, username, password);
}
//释放连接资源
public static void release(Connection conn, Statement st, ResultSet rs){
if(rs!=null){
try {
rs.close();
}catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(st!=null){
try {
st.close();
}catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
}catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
//备注:static关键字
/*在类中,用static声明的成员变量为静态成员变量,也称为类变量。类变量的生命周期和类相同,在整个应用程序执行期间都有效。
这里要强调一下:
static修饰的成员变量和方法,从属于类
普通变量和方法从属于对象
静态方法不能调用非静态成员,编译会报错
static关键字的用途
一句话描述就是:方便在没有创建对象的情况下进行调用(方法/变量)。
显然,被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问。
static可以用来修饰类的成员方法、类的成员变量,另外也可以编写static代码块来优化程序性能
static方法
static方法也成为静态方法,由于静态方法不依赖于任何对象就可以直接访问,因此对于静态方法来说,是没有this的,因为不依附于任何对象,既然都没有对象,就谈不上this了,并且由于此特性,在静态方法中不能访问类的非静态成员变量和非静态方法,因为非静态成员变量和非静态方法都必须依赖于具体的对象才能被调用。
虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法和静态成员变量。
*/

3、编写增删改的方法,executeUpdate

insert

package com.kuang.lesson02.utils;
import com.kuang.lesson02.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;
    </span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
        conn </span>=<span style="color: #000000;"> JdbcUtils.getConnection();
        st </span>=conn.createStatement();<span style="color: #008000;">//</span><span style="color: #008000;">获得SQL的执行对象</span>
        String sql ="INSERT INTO users(id,`NAME`,`PASSWORD`,`email`,`birthday`)" +
                "VALUES(4,'kuangshen','123456','123456@qq.com','2021-08-09')"<span style="color: #000000;">;

        </span><span style="color: #0000ff;">int</span> i =<span style="color: #000000;">st.executeUpdate(sql);
        </span><span style="color: #0000ff;">if</span> (i>0<span style="color: #000000;">){
            System.out.println(</span>"插入成功!"<span style="color: #000000;">);
        }
    } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (SQLException e) {
        e.printStackTrace();
    }</span><span style="color: #0000ff;">finally</span><span style="color: #000000;"> {
        JdbcUtils.release(conn,st,rs);
    }
}

}

delete

package com.kuang.lesson02.utils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestDelete {
public static void main(String[] args) {
Connection conn =null;
Statement st=null;
ResultSet rs =null;
//只需要变String sql 那一行
try {
conn = JdbcUtils.getConnection();
st =conn.createStatement();//获得SQL的执行对象
String sql ="DELETE FROM users WHERE id=4";
        </span><span style="color: #0000ff;">int</span> i =<span style="color: #000000;">st.executeUpdate(sql);
        </span><span style="color: #0000ff;">if</span> (i>0<span style="color: #000000;">){
            System.out.println(</span>"删除成功!"<span style="color: #000000;">);
        }
    } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (SQLException e) {
        e.printStackTrace();
    }</span><span style="color: #0000ff;">finally</span><span style="color: #000000;"> {
        JdbcUtils.release(conn,st,rs);
    }
}

}

update

package com.kuang.lesson02.utils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestUpdate {
public static void main(String[] args) {
Connection conn =null;
Statement st=null;
ResultSet rs =null;
//只需要变String sql 那一行
try {
conn = JdbcUtils.getConnection();
st =conn.createStatement();//获得SQL的执行对象
String sql ="UPDATE users SET NAME='kuangshen',email='123@qq.com' WHERE id=1";
        </span><span style="color: #0000ff;">int</span> i =<span style="color: #000000;">st.executeUpdate(sql);
        </span><span style="color: #0000ff;">if</span> (i>0<span style="color: #000000;">){
            System.out.println(</span>"更新成功!"<span style="color: #000000;">);
        }
    } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (SQLException e) {
        e.printStackTrace();
    }</span><span style="color: #0000ff;">finally</span><span style="color: #000000;"> {
        JdbcUtils.release(conn,st,rs);
    }
}

}

4、查询executeQuery

package com.kuang.lesson02.utils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class TestSelect {
public static void main(String[] args) {
Connection conn =null;
Statement st=null;
ResultSet rs=null;
try  {
conn=JdbcUtils.getConnection();
st = conn.createStatement();
//SQL
String sql ="select * from users where id =1";
        rs </span>= st.executeQuery(sql);<span style="color: #008000;">//</span><span style="color: #008000;">查询完毕会返回一个结果集</span>

        <span style="color: #0000ff;">while</span><span style="color: #000000;">(rs.next()){
            System.out.println(rs.getString(</span>"NAME"<span style="color: #000000;">));
        }

    } </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (SQLException e) {
        e.printStackTrace();
    }</span><span style="color: #0000ff;">finally</span><span style="color: #000000;"> {
        JdbcUtils.release(conn,st,rs);
    }
}

}

SQL 注入的问题

sql存在漏洞,会被攻击导致数据泄露,SQL会被拼接 因为or

博文详解:https://baike.baidu.com/item/sql%E6%B3%A8%E5%85%A5/150289?fr=aladdin

package com.li.lesson02;
import com.li.lesson02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class SQLInjection {
    public static void main(String[] args) {        //login("lisi","123456");        
    login(" 'or '1=1","'or '1=1");    
    }    
    //登录业务   
     public static void login(String username,String password){       
      Connection conn = null;       
       Statement st = null;       
        ResultSet rs = null;       
         try {           
          conn = JdbcUtils.getConnection();//获得数据库对象            
          st = conn.createStatement();            
          //SELECT * FROM `users` WHERE `name`='lisi' AND `password`='123456'            
          //SELECT * FROM `users` WHERE `name`=''or '1=1' AND `password`='123456'           
           String sql = "SELECT * FROM `users` WHERE `name`='" + username+"'" + "AND`password`='"+password+"'";            rs = st.executeQuery(sql);//查询           
            while (rs.next()){                
            System.out.println("id=" + rs.getInt("id"));                
            System.out.println("name=" + rs.getString("name"));                
            System.out.println("password=" + rs.getString("password"));                
            System.out.println("email=" + rs.getString("email"));                
            System.out.println("birth=" + rs.getDate("birthday"));                
            System.out.println("==============================================");            
            }       
             } catch (SQLException throwables) {        
               throwables.printStackTrace();        
             } finally {   
                      JdbcUtils.release(rs,st,conn);       
  }   
 }
}

10.5、 PreparedStatement对象

PreparedStatement可以防止SQL注入吗,效率更好!

PreparedStatement防止.SQL注入的本质,把传递进来的参数当做宁符假设其中存在转义字符,比如说·会被直接转义

1、增

package com.li.lesson03;
import com.li.lesson02.utils.JdbcUtils;
import java.sql.Connection;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestInsert {
    public static void main(String[] args) { 
           Connection conn = null;        
           PreparedStatement st = null;       
            try {            
            conn = JdbcUtils.getConnection();//连接数据库,获得数据库对象           
             //区别            
             //使用?占位符代理参数            
              String sql = "insert into users(`id`,`name`,`password`,`email`,`birthday`) values (?,?,?,?,?)";   
              st = conn.prepareStatement(sql);//预编译,先写sql,使用?占位符,然后不执行           
               //手动给参数赋值            
               st.setInt(1,1);            
               st.setString(2,"liliu");            
               st.setString(3,"154894");            
               st.setString(4,"32536@qq.com");            
               //注意点:sql.Date   数据库用的      java.sql.Date()           
                //      util.Date   java用的      new Date().getTime() 获得时间戳            
                st.setDate(5,new java.sql.Date(new Date().getTime()));            
                //执行            
                int i = st.executeUpdate();            
                if (i>0){                
                System.out.println("插入成功");            }        
                } catch (SQLException throwables) {         
                throwables.printStackTrace();        
                } finally { 
                           
                      JdbcUtils.release(null,st,conn);       
          }    
   }}

2、删

package com.li.lesson03;
import com.li.lesson02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestDelete {
    public static void main(String[] args) {        
    Connection conn = null;        
    PreparedStatement st = null;        
    try {            
    conn = JdbcUtils.getConnection();//连接数据库,获得数据库对象           
     String sql = "delete from users where id=?";          
     st = conn.prepareStatement(sql);//预编译            st.setInt(1,1);
     //手动给参数赋值            
     //执行            
     int i = st.executeUpdate();            
     if (i>0){               
      System.out.println("删除成功");          
        }        
        } catch (SQLException throwables) {          
        throwables.printStackTrace();        
        } finally {        
        }   
         }}

3、改

package com.li.lesson03;
import com.li.lesson02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public class TestUpdate {
    public static void main(String[] args) {        
    Connection conn = null;        
    PreparedStatement st = null;        
    try {            
    conn = JdbcUtils.getConnection();            
    String sql = "UPDATE users set `id`=?,`name`=? where `id`=?";            
    st = conn.prepareStatement(sql);            
    st.setInt(1,5);            
    st.setString(2,"耳机");            
    st.setInt(3,2);            
    int i = st.executeUpdate();           
     if (i>0){                
     System.out.println("修改成功");           
      }        
      } catch (SQLException throwables) {          
        throwables.printStackTrace();       
       } finally {        
       }   
    }}

4、查

package com.li.lesson03;

import com.li.lesson02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;public class TestSelect {
    public static void main(String[] args) {        
    Connection conn = null;        
    PreparedStatement st = null;       
     ResultSet rs = null;        
     try {            
     conn = JdbcUtils.getConnection();     
         //PreparedStatement 防止SQL注入的本质,把传递进来的参数当做字符
         //假设期中存在转义字符,会被直接转义
     String sql = "select `name` from users where id=?";            
     st = conn.prepareStatement(sql);          
     st.setInt(1,5);           
      rs = st.executeQuery();            
      while (rs.next()){                
      System.out.println("name=" + rs.getString("name"));            }       
       } catch (SQLException throwables) {          
         throwables.printStackTrace();       
        } finally {          
         JdbcUtils.release(rs,st,conn);       
         }   
          }}

10.6、 使用IDEA链接数据库

错误因为时区问题

?serverTimezone=GMT%2B8

在这里插入图片描述

连接成功后,查看数据库

在这里插入图片描述

修改数据,需要提交

在这里插入图片描述

写sql语句的地方

在这里插入图片描述

连接失败

在这里插入图片描述

10.7、事务

ACID原则

原子性:要么都成功,要么都失败

一致性:总数不变

隔离性:多个进程互不干扰

持久性:一旦提交不可逆,持久化到数据库了

隔离性的问题:

脏读:一个失误读取了另一个没有提交的事务

不可重复读:在同一个事务内,重复读取表中的数据,表数据发生了改变

虚读(幻读):在一个事务内,读取到了别人插入的数据,导致前后读出来结果不一致

代码实现

1、开启事务

2、一组业务执行完毕,提交事务

3、可以在catch语句中显示的定义回滚语句,但是默认就会回滚

package com.li.lesson03;

import com.li.lesson02.utils.JdbcUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class TestTransaction {
    public static void main(String[] args) {        
    Connection conn = null;        
    PreparedStatement st = null;       
     ResultSet rs = null;        
     try {            
     conn = JdbcUtils.getConnection();            
     //关闭数据库的自动提交事务,自动会开启事务            
     conn.setAutoCommit(false);           
      String sql1 = "UPDATE account SET money=money-500 WHERE NAME = 'A'";            
      st = conn.prepareStatement(sql1);         
      st.executeUpdate();    
         //int x/0;
      String sql2 = "UPDATE account SET money=money+500 WHERE NAME = 'B'";           
       st = conn.prepareStatement(sql2);           
       st.executeUpdate();            
       //业务完毕,提交事务           
        conn.commit();            
         System.out.println("提交成功");        
        } catch (SQLException throwables) {     
         throwables.printStackTrace();        }        
        try {           
         conn.rollback();
        //失败,自动回滚事务        
        } catch (SQLException throwables) {         
          throwables.printStackTrace();        
        } finally {           
           JdbcUtils.release(rs,st,conn);       
         }    
         }}

10.8、数据库连接池

数据库连接 – 执行完毕 —释放

连接-- 释放 十分浪费系统资源

池化技术:准备一些预先的资源,过来就连接预先准备好的

没有连接池:---- 开门–业务员—关门:等待 – 服务

有连接池:---- 开门–业务员:等待 – 服务—关门

常用连接数 10个

最小连接数 10

最大连接数 15 业务最高承载上线

超过 排队等待

等待超时 100ms

编写连接池,实现一个接口 DataSource

开源数据源实现 拿来即用

  1. DBCP
  2. C3P0
  3. Druid:阿里巴巴

DBCP

在这里插入图片描述

C3P0

在这里插入图片描述

Druid:阿里巴巴

使用了这些数据库连接池之后,我们在项目开发中就不需要编写连接数据库的代码了!

无论使用干什么是剧院,本质还是一样的

DataSource接口不会变

调优

性能监控

PROFILES 已经被替代

SHOW PROFILES 查看性能监控

SET profiling=1; 开启性能监控 开启之后可以查看

在这里插入图片描述

优化器 基于成本优化 和 基于规则优化 一般是基于规则优化

SHOW PROFILE 查看每步消耗的时间

①ALL:显示所有的开销信息。

②BLOCK IO:显示块IO开销。

③CONTEXT SWITCHES:上下文切换开销。

④CPU:显示CPU开销信息。

⑤IPC:显示发送和接收开销信息。

⑥MEMORY:显示内存开销信息。

⑦PAGE FAULTS:显示页面错误开销信息。

⑧SOURCE:显示和Source_function,Source_file,Source_line相关的开销信息。

⑨SWAPS:显示交换次数开销信息。

SHOW PROFILE cpu FOR QUERY 10 查询第10次查询时CPU耗时performance_schema MYSQL自带的性能监控表 可以了解

SHOW PROCESSLIST 查看链接信息

在这里插入图片描述

schema与数据类型优化

数据类型的优化

更小的通常更好

尽量使用可以正确存储数据的最小数据类型

因为他们占用更小的磁盘、内存和CPU缓存,并且处理时需要的CPU周期更少

简单就好

简单的数据类型操作通常需要更少的CPU周期

案列

  1. 整形比字符串操作代价更低

  2. 使用mysql自建的数据类型而不是字符串类型存储日期和时间

  3. 用整形存ip地址

    SELECT INET_ATON (‘192.168.68.60’) 3232252988

    SELECT INET_ATON (‘3232252988’) 192.168.68.60

尽量避免空值

null != null

在这里插入图片描述

合理使用范式反范式

在这里插入图片描述

主键选择

在这里插入图片描述

字符集选择 utf-8 -> utf-8mb4

存储引擎的选择

MyISAM: 数据和索引分开存储

  1. 不支持事务,但是每次查询都是原子的;
  2. 支持表级锁,即每次操作是对整个表加锁;
  3. 存储表的总行数;
  4. 一个MYISAM表有三个文件:索引文件、表结构文件、数据文件;
  5. 采用菲聚集索引,索引文件的数据域存储指向数据文件的指针。辅索引与主索引基本一致,但是辅索引不用保证唯一性。

InnoDb: 数据和索引在一起存储

  1. 支持ACID的事务,支持事务的四种隔离级别;
  2. 支持行级锁及外键约束:因此可以支持写并发;
  3. 不存储总行数;
  4. 一个InnoDb引擎存储在一个文件空间(共享表空间,表大小不受操作系统控制,一个表可能分布在多个文件里),也有可能为多个(设置为独立表空,表大小受操作系统文件大小限制,一般为2G),受操作系统文件大小的限制;
  5. 主键索引采用聚集索引(索引的数据域存储数据文件本身),辅索引的数据域存储主键的值;因此从辅索引查找数据,需要先通过辅索引找到主键值,再访问辅索引;最好使用自增主键,防止插入数据时,为维持B+树结构,文件的大调整。

在这里插入图片描述

适当的数据冗余

适当拆分

在这里插入图片描述

执行计划

explain select * from emp;

id:选择标识符

id

SELECT识别符。这是SELECT的查询序列号

我的理解是SQL执行的顺序的标识,SQL从大到小的执行

  1. id相同时,执行顺序由上至下
  2. 如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行
  3. id如果相同,可以认为是一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行

select_type:表示查询的类型。
(1) SIMPLE(简单SELECT,不使用UNION或子查询等)

(2) PRIMARY(子查询中最外层查询,查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY)

(3) UNION(UNION中的第二个或后面的SELECT语句)

(4) DEPENDENT UNION(UNION中的第二个或后面的SELECT语句,取决于外面的查询)

(5) UNION RESULT(UNION的结果,union语句中第二个select开始后面所有select)

(6) SUBQUERY(子查询中的第一个SELECT,结果不依赖于外部查询)

(7) DEPENDENT SUBQUERY(子查询中的第一个SELECT,依赖于外部查询)

(8) DERIVED(派生表的SELECT, FROM子句的子查询)

(9) UNCACHEABLE SUBQUERY(一个子查询的结果不能被缓存,必须重新评估外链接的第一行)

table:输出结果集的表

显示这一步所访问数据库中表名称(显示这一行的数据是关于哪张表的),有时不是真实的表名字,可能是简称,例如上面的e,d,也可能是第几步执行的结果的简称

partitions:匹配的分区

type:表示表的连接类型

对表访问方式,表示MySQL在表中找到所需行的方式,又称“访问类型”。

常用的类型有: ALL、index、range、 ref、eq_ref、const、system、NULL(从左到右,性能从差到好)

ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行

index: Full Index Scan,index与ALL区别为index类型只遍历索引树

range:只检索给定范围的行,使用一个索引来选择行

ref: 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
eq_ref: 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件

const、system: 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system

NULL: MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。

possible_keys:表示查询时,可能使用的索引

指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用(该查询可以利用的索引,如果没有任何索引显示 null)

key:表示实际使用的索引

key列显示MySQL实际决定使用的键(索引),必然包含在possible_keys中

如果没有选择索引,键是NULL。要想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。

key_len:索引字段的长度

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)

不损失精确性的情况下,长度越短越好

ref:列与索引的比较

列与索引的比较,表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值

rows:扫描出的行数(估算的行数)

估算出结果集行数,表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数

filtered:按表条件过滤的行百分比

Extra:执行情况的描述和说明

Using where:不用读取表中所有信息,仅通过索引就可以获取所需数据,这发生在对表的全部的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤

Using temporary:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询,常见 group by ; order by

Using filesort:当Query中包含 order by 操作,而且无法利用索引完成的排序操作称为“文件排序”

Using join buffer:改值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。

Impossible where:这个值强调了where语句会导致没有符合条件的行(通过收集统计信息不可能存在结果)。

Select tables optimized away:这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行

No tables used:Query语句中使用from dual 或不含任何from子句

总结

EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况

EXPLAIN不考虑各种Cache

EXPLAIN不能显示MySQL在执行查询时所作的优化工作

部分统计信息是估算的,并非精确值

EXPALIN只能解释SELECT操作,其他操作要重写为SELECT后查看执行计划。

索引数据类型

常见的两种数据引擎用的是B+树

MEMORY用的是hash index

索引匹配方式

在这里插入图片描述

查询优化

在这里插入图片描述

分区表

在这里插入图片描述

回表查询,先定位主键值,再定位行记录。多扫了一遍索引树。

在这里插入图片描述

覆盖索引

如果一个索引包含了(或覆盖了)满足查询语句中字段与条件的数据就叫 做覆盖索引。

最左匹配

在这里插入图片描述

索引下推

L在表中找到所需行的方式,又称“访问类型”。

常用的类型有: ALL、index、range、 ref、eq_ref、const、system、NULL(从左到右,性能从差到好)

ALL:Full Table Scan, MySQL将遍历全表以找到匹配的行

index: Full Index Scan,index与ALL区别为index类型只遍历索引树

range:只检索给定范围的行,使用一个索引来选择行

ref: 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
eq_ref: 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件

const、system: 当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量,system是const类型的特例,当查询的表只有一行的情况下,使用system

NULL: MySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。

possible_keys:表示查询时,可能使用的索引

指出MySQL能使用哪个索引在表中找到记录,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用(该查询可以利用的索引,如果没有任何索引显示 null)

key:表示实际使用的索引

key列显示MySQL实际决定使用的键(索引),必然包含在possible_keys中

如果没有选择索引,键是NULL。要想强制MySQL使用或忽视possible_keys列中的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。

key_len:索引字段的长度

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度(key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的)

不损失精确性的情况下,长度越短越好

ref:列与索引的比较

列与索引的比较,表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值

rows:扫描出的行数(估算的行数)

估算出结果集行数,表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数

filtered:按表条件过滤的行百分比

Extra:执行情况的描述和说明

Using where:不用读取表中所有信息,仅通过索引就可以获取所需数据,这发生在对表的全部的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤

Using temporary:表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询,常见 group by ; order by

Using filesort:当Query中包含 order by 操作,而且无法利用索引完成的排序操作称为“文件排序”

Using join buffer:改值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。

Impossible where:这个值强调了where语句会导致没有符合条件的行(通过收集统计信息不可能存在结果)。

Select tables optimized away:这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行

No tables used:Query语句中使用from dual 或不含任何from子句

总结

EXPLAIN不会告诉你关于触发器、存储过程的信息或用户自定义函数对查询的影响情况

EXPLAIN不考虑各种Cache

EXPLAIN不能显示MySQL在执行查询时所作的优化工作

部分统计信息是估算的,并非精确值

EXPALIN只能解释SELECT操作,其他操作要重写为SELECT后查看执行计划。

索引数据类型

常见的两种数据引擎用的是B+树

MEMORY用的是hash index

索引匹配方式

[外链图片转存中…(img-GjyjPBMm-1644829633992)]

查询优化

[外链图片转存中…(img-7oP765XN-1644829633993)]

分区表

[外链图片转存中…(img-26EcZcCn-1644829633993)]

回表查询,先定位主键值,再定位行记录。多扫了一遍索引树。

[外链图片转存中…(img-eAnVvoIc-1644829633993)]

覆盖索引

如果一个索引包含了(或覆盖了)满足查询语句中字段与条件的数据就叫 做覆盖索引。

最左匹配

[外链图片转存中…(img-SKQDvexh-1644829633993)]

索引下推

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值