Mysql-学习笔记分享(一篇全)

159 阅读26分钟

笔记整理自B站尚硅谷Mysql视频以及狂神Mysql视频,其中可能有记录内容不正确,望诸位指出

作者:Vison

工作后发现,数据库我所学的东西还是太浅了,这里的体系还是很庞大的,sql这里多仔细看看没有坏处


1、数据库分类

关系型数据库:(SQL)

MySQL ,Oracle , sql Sever , DB2 , SQLlite

通过表和表之间,行和列之间的关系进行数据的存储,学员信息表,考勤表

非关系型数据库:(NoSQL)

Not Only , MongDB

非关系型数据库,对象存储,通过对象的自身的属性来决定

DBMS(数据库管理系统)

数据库的管理软件,科学有效的关系我们的数据,维护和获取数据;

MySQL 数据库管理系统


2、数据库常见命令

登录命令

mysql -h localhost -P 3306 -u root -p

主机名 端口号

1.查看当前所有的数据库

show databases;

2.打开指定的库

use 库名

3.查看当前库的所有表

show tables;

4.查看其它库的所有表

show tables from 库名;

5.创建表

create table 表名(

    列名 列类型,

    列明 列类型,

    ......

    列明 列类型

)

6.查看表结构

desc 表名;

7.查看服务器的版本

方式一:登录到mysql服务端

select version();

方式二:没有登录到mysql服务端

mysql --version 或 mysql --V

数据库xxx语言

DDL    定义

DML    操作

DQL    查询

DCL    控制


3、Mysql语法规范

1.不区分大小写,但建议关键字大写,表名,列名小写

2.每条命令最好用分号结尾

3.每条命令根据需要,可以进行缩进 或 换行

4.注释:

单行注释 #注释文字

单行注释 -- 注释文字

多行注释 / * 注释文字 * /


4、数据库操作

创建,删除,使用数据库

#创建数据库
CREATE DATABASE [IF NOT EXISTS] 数据库名;

#删除数据库
DROP DATABASE [IF EXISTS] 数据库名;

#使用数据库
--Tab 键的上面,如果你的表名或者字段名是一个特殊字符,就需要带``
USE `数据库名`;

#查看数据库
SHOW DATABASE;查看所有的数据库
SHOW 数据库名;

数据库列类型

数值

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

字符串

char字符串固定大小的0~255
varchar可变字符串0~65535     常用的变量 String
tingtext微型文本2的8次方 - 1
text文本串2的16次方- 1 保存大文本

时间日期

dateYYYY-MM-DD日期格式
timeHH : mm :ss时间格式
datatimeYYYY-MM-DD HH : mm :ss最常用的时间
timestamp时间戳1970.1.1到现在的毫秒数也较为常用
year年份表示

null

没有值,未知不要使用null进行运算,结果为null

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

Unsigned        

Navicat:无符号

无符号的整数

声明了该列不能声明为负数

zerofill        

Navicat:填充零

0填充的

不足的位数,使用0来填充, int(2). 5--->05

自增        

Navicat:自动递增

通常理解为自增,自动再上一条记录的基础上+1(默认)

通常用来设计唯一的主键~index,必须是整数类型

可以自定义设计主键自增的起始值和步长

非空 not null

假设设置为not null,如果不给它赋值,就会报错!

NULL如果不填写值,默认就是null

默认

设置默认的值

sex默认值 为 男,如果不指定该列的值,则会由默认的值

拓展:

每个表都必须存在以下五个字段,未来做项目用的

id     主键

version     乐观锁

is_delete     伪删除

gmt_create 创建时间

gmt_update 修改时间

数据表的类型

关于数据库引擎

INNODB 默认使用

MYISAM 早些年使用

MYISAMINNODB
事务支持不支持支持
数据行锁定不支持支持
外键约束不支持支持
全文索引支持不支持
表空间的大小较小较大,约为2倍

常规使用操作

MYISAM    节约空间,速度较快

INNODB    安全性高,事务的处理,多表多用户操作

在物理空间存在的位置:

所有的数据库文件都存在data目录下,一个文件夹就对应一个数据库,本质还是文件的存储

MySQL引擎在物理文件上的区别

INNODB在数据库表中只有一个 *.frm文件,以及上级目录下的    ibdata1    文件

MYISAM对应的文件:

*.frm 表结构的定义文件

*.MYD 数据文件(data)

*.MYI 索引文件(index)

创建、修改、删除数据表字段

创建

使用英文的()

表的名称和字段尽量用      括起来

AUTO_INCREMENT自增

字符串使用单引号括起来

所有的语句后面加 , (英文的)最后一个不用加

PRIMARY KEY 主键,一般一个表只有一个唯一的主键

CREATE TABLE
IF
	NOT EXISTS `student` (
		`id` INT ( 4 ) NOT NULL auto_increment COMMENT '学号',
		`name` VARCHAR ( 30 ) NOT NULL DEFAULT '匿名' COMMENT '姓名',
	PRIMARY KEY ( `id` ) 
	) ENGINE = INNODB DEFAULT charset = utf8

--查看创建数据库的语句:                
			SHOW CREATE DATABASE school
      
--查看student数据表的定义语句:    
			SHOW CREATE TABLE student
      
--显示表的结构:                              
      DESC student

修改

--修改表名
		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(1)   --字段重命名
    

删除

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

--删除表(如果表存在就删除)
		DROP TABLE IF EXISTS teacher1

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

注意点:

字段名,使用这个包裹

注释 /**/

sql关键字大小写不敏感,建议写小写

所有符号用英文

Mysql的数据管理

外键

方式一:在创建表的时候增加约束

方式二:创建表的时候增加外键约束 不建议使用

DML语言(全部记住)

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

DML语言:数据操作语言

Insert       插入

Update    修改

delete      删除

添加insert

-- 插入语句(添加)
-- INSERT INTO 表名([字段名1,字段名2,字段名3])values('值1','值1',.....)
INSERT INTO `grade` (`name`) VALUES('大四')

-- 由于主键自增我们可以忽略(如果不写表的字段,他就会一一匹配)
INSERT INTO `grade` VALUES('大三')

-- 一般插入语句,我们一定要数据和字段一一对应

-- 插入多个字段
INSERT INTO `grade`(`name`) VALUES('大二'),('大一')

语法:INSERT INTO 表名([字段名1,字段名2,字段名3])values('值1','值1',.....)

注意事项:

字段和字段之间使用 英文逗号 隔开

字段是可以省略的,但后面的值必须要一一对应

修改Update

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

-- 修改学员语句
UPDATE `student` SET `name`='叫啥呢'WHERE id = 1

-- 不指定条件的情况下,会改动所有表
UPDATE `student` SET `name`='叫啥呢!'

-- 语法:
-- 	UPDATE 表名 set colnum_name = value,[colnum_name = value,......] whare 条件

-- 条件: where 子句 运算符 id等于某个值,大于某个值,在某个区间内修改......
操作符含义范围结果
=等于5=6false
<>或!=不等于5<>6true
<
>=
<=
BETWEEN...AND...在某个范围内[ 2 , 5]
AND我和你 &&5>1 and 1>2false
OR我或你5>1 and 1>2true

语法:UPDATE 表名 set colnum_name = value,[colnum_name = value,......] whare 条件

注意:

colnum_name,是数据库的列,尽量带上``

筛选的条件如果没有指定,则会修改所有的列

value是一个具体的值,也可以是一个变量

多个设置的属性之间,使用英文逗号隔开

删除delect

语法:delete from 表名[where 条件]

-- 删除数据(避免这样写)
DELETE FROM `student`
-- 删除指定数据
DELETE FROM `student` WHERE ID = 1

TRUNCATE 命令

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

-- 清空student表
TRUNCATE `student`

TRUNCATE 和 delete 的区别

相同点:

都能删除数据,都不会删除表结构

不同:

TRUNCATE 命令重新设置 自增列 计数器会归零

TRUNCATE 命令不会影响事务

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

Data Query LANGUAGE : 数据查询语言

所有的查询操作都用它 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}];

   -- 指定查询的记录从哪条至哪条

注意 : [ ] 括号代表可选的 , { }括号代表必选得

指定查询字段

-- 查询所有的学生  成绩 
-- 语法:SELECT 字段 FROM 表
SELECT * FROM student
SELECT * FROM result

-- 查询指定字段
SELECT `StudentNo` FROM student

-- 别名:给结果起一个名字 AS     可以给字段起别名,也可以给表起别名
SELECT `StudentNo` AS 学号,`StudentName` AS 学生姓名 FROM student AS s


-- 函数 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 -- 查询自增的步长(变量)
SELECT `StudentNo`,`StudentResult`+1 AS '提分后' FROM result -- 学员考试成绩+1分查看

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

语法:select 表达式 from 表

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>=95 AND StudentResult<=100

--  &&
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

模糊查询:比较运算符

运算符语法描述
IS NULLa IS NULL如果操作符为null则结果为真
IS NOT NULLa IS NOT NULL如果操作符为not null 结果为真
BETWEENa between b and c若a在 b和c 之间则结果为真
Likea like bSQL匹配,如果a匹配b,则结果为真
Ina In (a1,a2,a3,.....)假设a在a1 或者a2....其中的某一个值中,结果为真
-- ================================模糊查询========================================

-- 查询姓张的同学
-- 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 '%松%'

-- ==========in(具体的一个或多个值)===========

-- 查询1001,1002号学员
SELECT `StudentNo`,`StudentName` FROM `student`
WHERE StudentNo IN (1001,1002)

-- 查询在北京的学生
SELECT `StudentNo`,`StudentName` FROM `student`
WHERE `Address` IN ('北京朝阳')

-- ==============null    not null====================
-- 查询地址为空的学生
SELECT `StudentNo`,`StudentName` FROM `student`
WHERE address='' OR address IS NULL

-- 查询有出生日期的同学     不为空
SELECT `StudentNo`,`StudentName` FROM `student`
WHERE `BornDate` IS NOT NULL

联表查询

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

自连接(了解)

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

父类

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

子类

pidcategoryidcategoryname
34数据库
28办公信息
36web开发
57美术设计

父类子类
信息技术办公信息
软件开发数据库
软件开发web开发
美术设计ps技术

分页和排序

排序

-- 排序  : 升序ASC   降序DESC
-- 查询的结果根据成绩降序 排序
-- ORDER BY 通过哪个字段排序,怎么排
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 subjectName = '高等数学-1'
ORDER BY StudentResult DESC

分页

-- 分页:环节数据库压力,给人体验更好
-- 分页每页只显示5条数据
-- 语法:limit 起始值,页面的大小
--       limit 1~5条数据  
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 subjectName = '高等数学-1'
ORDER BY StudentResult DESC
LIMIT 0,5
-- pageSize:页面大小  
-- n:当前页  
-- (n-1)*pageSize,pageSize
-- 数据总数/页面大小 = 总页数

子查询和嵌套查询

/*============== 子查询 ================
什么是子查询?
   在查询语句中的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'
  )
)

6、常用函数

数据函数

-- 数学运算
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();     /*用户*/

聚合函数(常用)

聚合函数

函数名称描述
COUNT()计数
SUM()求和
AVG()平均值
MAX()最大值
MIN()最小值
-- 聚合函数
 /*COUNT:非空的*/
 SELECT COUNT(studentname) FROM student -- count(字段) 忽略字段为null
 SELECT COUNT(*) FROM student           -- count(*) 不会忽略null
 SELECT COUNT(1) FROM student           -- count(1) 不会忽略所有的null值
 -- 从含义上讲,count(1) 与 count(*) 都表示对全部数据行的查询。
  
 /*
 很多人认为count(1)执行的效率会比count(*)高,
 原因是count(*)会存在全表扫描,而count(1)可以针对一个字段进行查询。
 其实不然,count(1)和count(*)都会对全表进行扫描,
 统计所有记录的条数,包括那些为null的记录,因此,它们的效率可以说是相差无几。
 而count(字段)则与前两者不同,它会统计该字段不为null的记录条数。
 下面它们之间的一些对比:
 1)在表没有主键时,count(1)比count(*)快
 2)有主键时,主键作为计算条件,count(主键)效率最高;
 3)若表格只有一个字段,则count(*)效率较高。
 */

题目:

-- 查询不同课程的平均分,最高分,最低分
 -- 前提:根据不同的课程进行分组
 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不可逆,具体的值的MD5是一样的

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

-- ==========测试MD5 加密================
-- 新建一个表 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,'qinjiang','456789'),(3,'wangwu','123456')
 
 -- 对pwd加密,语法是:
 update testmd5 set pwd=MD5 (pwd) WHERE id = 1
 
 -- 插入的时候加密
 INSERT INTO testmd5 VALUES(4,'xiaoming',MD5('123456'));
 
 -- 如何校验:将用户传递进来的密码,进行MD5加密,然会比对加密后的值
 SELECT * FROM testmd5 WHERE `name`='xiaoming' AND pwd=MD5('123456')

7、事务

要么都成功,要么都失败


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

事务的ACID原则

分享一篇文章 ACID (脏读,幻读)

blog.csdn.net/dengjili/ar…

原子性(Atomicity)

要么都成功要么都失败

一致性(Consistency)

事务前后的数据完整性要保证一致

持久性(Durability) --事务提交

事务一旦提交则不可逆,被持久化到数据库中

隔离性(Isolation)

在两个都在操作的时候,数据库为每一个用户开启单独的事务,不会被其他事务所干扰。

隔离所导致的一些问题:

脏读:

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

不可重复读:

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

虚读(幻读):

是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致

8、索引

通过索引可以让我们更快速的获取SQL里面的结果

索引的分类

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

主键索引 (PRIMARY KEY)

唯一标识,主键不可以重复,只能有一个列作为主键

唯一索引 (UNIQUE KEY)

避免重复的列出现,唯一索引可以重复,多个列都可以标识为唯一索引

常规索引 (KEY)

默认的,index,key关键字来设置

全文索引 (FullText)

在特定的数据库引擎下才有 MyISAM

快速定位数据

基础语法

-- ==================索引的使用=====================
-- 1.在创建表的时候给字段增加索引
-- 2.创建完毕后,增加索引

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

-- 增加一个索引
ALTER TABLE `student` ADD FULLTEXT INDEX `studentName`(`studentName`)

-- 分析sql执行的状况
EXPLAIN SELECT * FROM student -- 非全文索引
SELECT * FROM student WHERE MATCH(studentName) AGAINST('李')
/*
#方法一:创建表时
    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语句执行性能*/
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 版本、存储引擎和数据类型是否支持全文索引。
*/

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

索引不是越多越好

不要对经常变动的数据加索引

小数据量的表不需要加索引

索引一般加在常用来查询的字段上

索引的数据结构

Hash类型索引

Btree:InnoDB的默认数据结构~

阅读时间1h:

阅读:blog.codinglabs.org/articles/th…

9、权限管理

用户管理

SQL命令操作

用户表:mysql.user

本质:对这张表进行增删改查

-- ===============================用户管理=======================================
-- 创建用户    
-- CREATE USER 用户名 IDENTIFIED BY '密码' 
CREATE USER liweisong IDENTIFIED BY '123456'

-- 修改密码(修改当前用户密码)
SET PASSWORD = PASSWORD('123456')
-- 修改密码(修改指定用户密码)
SET PASSWORD FOR liweisong = PASSWORD('123456')
-- 重命名
-- RENAME USER 原名字 TO 修改后名字
RENAME USER liweisong TO liweisong2
-- 用户授权  ALL PRIVILEGES 全部的权限,库,表
GRANT ALL PRIVILEGES ON *.* TO liweisong2

-- 查询权限
SHOW GRANTS FOR liweisong2 -- 查看指定用户的权限
SHOW GRANTS FOR root@localhost

-- ROOT 用户权限:GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITE GRANT OPTION 

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

-- 删除用户
DROP USER liweisong2

备份

保证重要的数据不丢失

数据转移

MySQL数据库备份的方式

直接拷贝物理文件

在可视化中手动导出

使用命令行 mysqldump 命令行

#mysql-h 主机 -u 用户名 -p 密码 数据库 表明 >物理磁盘位置/文件名
C:\Users\sss94>mysqldump -hlocalhost -uroot -p123456 school student >D:/a.sql

mysqldump: [Warning] Using a password on the command line interface can be insecure.

10、规划数据库设计

为什么需要设计

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

糟糕的数据库设计

  • 数据冗余,浪费空i教案
  • 数据插入和删除都会麻烦/异常【屏蔽使用物理外键】
  • 程序的性能差

良好的数据库设计

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

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

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

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

  • 收集信息,分析需求

            用户表(用户登录注销,用户的个人信息,写博客,创建分类)

分类表(文章分类,谁创建的)

文章表(文章的信息)

评论表

友链表(友链信息)

自定义表(系统信息,某个关键字,或者一些主字段)key:value

说说表(发表心情)

  • 标识实体(把需求落地到每个字段)
  • 标识实体之间的关系

写博客:user-->blog

创建分类:user-->category

关注:user-->user

友链:links

评论表:user-user-blog

三大范式

为什么需要数据规范化?

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

第一范式(1NF)

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

第二范式(2NF)

前提:满足第一范式

每张表只描述一件事情

第三范式(3NF)

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

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

(规范数据库设计)

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

11、JDBC

数据库驱动

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

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

JDBC

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

第一个JDBC程序

创建测试数据库

CREATE DATABASE jdbcStudy CHARACTER SET utf8 COLLATE utf8_general_ci;
USE jdbcStudy;


CREATE TABLE users(
		id INT PRIMARY KEY,
		NAME VARCHAR(40),
		PASSWORD VARCHAR(40),
		email VARCHAR(60),
		birthday DATE

)

INSERT INTO users (id,NAME,PASSWORD,email,birthday)
VALUES
(1,'zhangsan','123456','zs@sina.com','1980-12-4'),
(2,'lisi','123456','ls@sina.com','1981-12-4'),
(3,'wangwu','123456','ww@sina.com','1979-12-4');

1.创建一个普通项目

2.导入数据库驱动

3.编写测试代码

package src.com.weisongJDBC.lesson01;

import java.sql.*;

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

        //2.用户信息和url
        //?userUnicode=true&characterEncoding=utf8&useSSL=true
        //   支持中文编码        设定字符集utf-8           使用安全的链接
        String url = "jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&characterEncoding=utf8&useSSL=true";
        String username = "root";
        String password = "123456";

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

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

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

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

        while(resultSet.next()){
            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"));
            System.out.println("====================================================");
        }
        //6.释放连接
        resultSet.close();
        statement.close();
        connection.close();

    }
}

步骤总结:

1.加载驱动

2.连接数据库 DriverManager

3.获取执行SQL的对象 Statement

4.获取返回的结果集

5.释放连接

对象解释

DriverManager

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


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

//connection  代表数据库
//数据库设置自动提交
//事务提交
//事务回滚

URL

//2.用户信息和url
        //?userUnicode=true&cahracterEncoding=utf8&useSSL=true
        //   支持中文编码        设定字符集utf-8           使用安全的链接
        String url = "jdbc:mysql://localhost:3306/jdbcstudy?userUnicode=true&characterEncoding=utf8&useSSL=true";
        String username = "root";
        String password = "123456";
//mysql默认3306
//协议://主机地址:端口号/数据库名?参数1&参数2&参数3

//oralce默认1521
//jdbc:oracle:thin:@localhost:1521:sid

statement 执行SQL的对象 PrepareStatement执行SQL的对象

String sql = "SELECT * FROM users";//编写SQL

statement.executeQuery();//查询操作返回ResulySet
statement.execute();//执行任何SQL
statement.executeUpdate();//更新,插入,删除,都用这个,返回一个受影响的行数

ResultSet 查询的结果集:封装了都有的查询结果

获取指定的数据类型

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

遍历,指针

resultSet.beforeFirst();//移动到最前面
resultSet.afterLast();//移动到最后面
resultSet.next();//移动到下一个数据
resultSet.previous();//移动到前一行
resultSet.absolute(row);//移动到只当行

释放资源

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

statement

1、增

//JdbcUtils

package com.weisong;

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 {

//			InputStream in=Jdbcutils.class.getClassLoader().getResourceAsStream("bd.properties");
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("bd.properties");
            Properties properties = new Properties();
            properties.load(in);
            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");
            Class.forName(driver);
            //驱动只加载一次


        } catch (Exception 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();
            }
        }

    }
}



//TestInsert

package com.weisong;

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) throws Exception {
        Connection conn =null;
        Statement st=null;
        ResultSet rs=null;

        try {
            conn= JdbcUtils.getConnection();
            st=conn.createStatement();
            String sql="INSERT INTO `users`  (`id`,`name`,`password`,`email`,`birthday`)"
                    + " VALUES ('4','weisong','123456','1139575852@qq.com','2000-6-28') ";
            int i=st.executeUpdate(sql);
            if(i>0) {
                System.out.println("插入成功!");
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn, st, rs);
        }
    }
}

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

2、删

package com.weisong;

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;

        try {
            conn= JdbcUtils.getConnection();//获取数据库连接
            st=conn.createStatement();//获取SQL的执行对象
            String sql="DELETE FROM users WHERE id = 4";
            int i=st.executeUpdate(sql);
            if(i>0) {
                System.out.println("删除成功!");
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn, st, rs);
        }
    }
}

3、改

package com.weisong;

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;

        try {
            conn= JdbcUtils.getConnection();//获取数据库连接
            st=conn.createStatement();//获取SQL的执行对象
            String sql="UPDATE users SET `Name` = 'weisong' ,`email` = '1139575852@qq.com' WHERE id = 1";
            int i=st.executeUpdate(sql);
            if(i>0) {
                System.out.println("更新成功!");
            }
        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn, st, rs);
        }
    }
}

4、查

executeQuery

package com.weisong;

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 = st.executeQuery(sql);//查询完毕会返回结果集

            while(rs.next()){
                System.out.println(rs.getString("NAME"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally{
            JdbcUtils.release(conn,st,rs);
        }
    }
}

代码实现:

1.提取工具

2.编写增删改的方法,executeUpdate

SQL注入的问题

public class SQL注入 {
    public static void main(String[] args) {
//        login("weisong","123456");
        login("'or '1=1","123456");
                
    }

    //登录业务
    public static void login(String username , String password){
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;
        try {
            conn = JdbcUtils.getConnection();

            st = conn.createStatement();
            //SQL         SELECT * FROM users WHERE `Name` = 'weisong' AND PASSWORD = '123456';
            String sql = "SELECT * FROM users WHERE `Name` = '"+username+"' AND `PASSWORD` = '"+password+"'";

            rs = st.executeQuery(sql);//查询完毕会返回结果集

            while(rs.next()){
                System.out.println(rs.getString("NAME"));
                System.out.println(rs.getString("password"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally{
            JdbcUtils.release(conn,st,rs);
        }
    }
}

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

PreparedStatement

可以防止SQL注入,效果更高

1.新增

package com.weisong2;

import com.weisong.JdbcUtils;

import java.sql.*;

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,先写sql,然后不执行

            //手动给参数赋值
            st.setInt(1,4);//id
            st.setString(2,"jinglin");//name
            st.setString(3,"123456");//password
            st.setString(4,"1139575348@qq.com");//email
            //注意点:sql.Date    数据库                    java.sql.Date    转化为sqlDate
            //      util.Date   java                     new Date().getTime()   获得时间戳
            st.setDate(5,new java.sql.Date(new java.util.Date().getTime()));//birthday

            //执行
            int i = st.executeUpdate();
            if (i > 0){
                System.out.println("插入成功");
            }


        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally{
            JdbcUtils.release(conn,st,null);
        }
    }
}

2.删除

package com.weisong2;

import com.weisong.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= com.weisong.JdbcUtils.getConnection();//获取数据库连接

            //区别
            //使用 ? 占位符代替参数
            String sql ="delete from users where id = ?";

            st=conn.prepareStatement(sql);//预编译SQL,先写sql,然后不执行

            //手动给参数赋值
            st.setInt(1,4);

            //执行
            int i = st.executeUpdate();
            if (i > 0){
                System.out.println("删除成功");
            }


        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally{
            JdbcUtils.release(conn,st,null);
        }
    }
}

3.修改

package com.weisong2;

import com.weisong.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= com.weisong.JdbcUtils.getConnection();//获取数据库连接

            //区别
            //使用 ? 占位符代替参数
            String sql ="update users set `name` =? where id =?;";

            st=conn.prepareStatement(sql);//预编译SQL,先写sql,然后不执行

            //手动给参数赋值
            st.setString(1,"伟松");
            st.setInt(2,1);
            //执行
            int i = st.executeUpdate();
            if (i > 0){
                System.out.println("更新成功");
            }


        } catch (SQLException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }finally{
            JdbcUtils.release(conn,st,null);
        }
    }
}

4.查询

package com.weisong2;

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();

            String sql = "select * from users where id = ?";//编写sql

            st = conn.prepareStatement(sql);

            st.setInt(1,2);//传递参数

            rs = st.executeQuery();//执行

            if (rs.next()){
                System.out.println(rs.getString("NAME"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            JdbcUtils.release(conn,st,rs);
        }
    }
}

防止SQL注入

JDBC操作事务

原子性:要么都完成,要么都不完成

一致性:总数不变

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

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

隔离性的问题:

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

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

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