MySQL 学习记录

21 阅读26分钟

1、数据库分类

关系型数据库:(SQL)

  • MySQL,Oracle,Sql Server,DB2,SQLite
  • 通过表和表之间,行和列之间的关系进行数据的存储, 学员信息表,考勤表,……

非关系型数据库:(NoSQL) Not Only

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

连接数据库

命令行连接!

mysql -uroot -p123456 --连接数据库

update mysql.user set authentication_string=password('123456') where user='root' and Host = 'localhost'; -- 修改用户密码
flush privileges;    -- 刷新权限

----------------------------------------

-- 所有的语句都使用;结尾
show databases;   -- 查看所有的数据库

mysql> use school   -- 切换数据库 use 数据库名
Database changed

show tables; -- 查看数据库中所有的表
describe student; -- 显示数据库中所有的表的信息

create database westos; -- 创建一个数据库

exit;   --退出连接

-- 单行注释(SQL 的本来的注释)
/**/ 多行注释

数据库的列类型

数值

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

字符串

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

时间日期

java.util.Date

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

null

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

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

Unsigned :

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

zerofill:

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

自增:

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

非空 NULL not null

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

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


-- 目标 :创建一个school数据库

-- 创建学生表(列,字段) 使用SQL 创建

-- 学号int 登录密码varchar(20) 姓名,性别varchar(2),出生日期(datatime),家庭住址,email

-- 注意点,使用英文(),表的名称 和 字段 尽量使用 `` 括起来

-- 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 '姓名',

`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

image.png

常用命令

SHOW CREATE DATABASE school -- 查看创建数据库的语句

SHOW CREATE TABLE student -- 查看student数据表的定义语句

DESC student -- 显示表的结构

数据表的类型

-- 关于数据库引擎

  • INNODB 默认使用~

  • MYISAM 早些年使用的 在数据库表引擎的选择上,MYISAM和INNODB存在诸多差异:

  • 事务支持方面,MYISAM不支持而INNODB支持;

  • 数据行锁定机制上,MYISAM不支持而INNODB支持;

  • 外键约束上,MYISAM不支持而INNODB支持;

  • 全文索引功能上,MYISAM支持而INNODB不支持;

  • 表空间大小方面,MYISAM的表空间较小,INNODB的表空间较大(约为MYISAM的2倍)。

常规使用操作:

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

设置数据库表的字符集编码

CHARSET=utf8

不设置的话,会是mysql默认的字符集编码~ (不支持中文!)

MySQL的默认编码是Latin1,不支持中文

在my.ini中配置默认的编码

character-set-server=utf8

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

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

删除

-- 删除表(如果表存在再删除)
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 '姓名',
  `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 '学生的年级',
  `address` VARCHAR(100) DEFAULT NULL COMMENT '家庭住址',
  `email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
  PRIMARY KEY(`id`),
  KEY `FK_gradeid` (`gradeid`),
  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 那个表(哪个字段)

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

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

DML语言(全部记住)

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

DML 语言:数据操作语言

  • Insert
  • update
  • delete

Insert添加数据

插入语句(添加)

-- insert into 表名(字段名1,字段2,字段3)values('值1'),('值2'),('值3',.....)
INSERT INTO `grade`(`gradename`) VALUES('大四')

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

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

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

INSERT INTO `student`(`name`) VALUES ('张三')

INSERT INTO `student`(`name`,`pwd`,`sex`) VALUES ('张三','aaaaaa','男')

INSERT INTO `student`(`name`,`pwd`,`sex`)
VALUES ('李四','aaaaaa','男'),('王五','aaaaaa','男')

语法:insert into 表名(字段名1,字段2,字段3)values('值1'),('值2'),('值3',.....)

注意事项:

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

update修改

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

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

-- 不指定条件的情况下,会改动所有表!
UPDATE `student` SET `name`='长江7号'

-- 修改多个属性,逗号隔开
UPDATE `student` SET `name`='狂神',`email`='24736743@qq.com' WHERE id = 1;

-- 语法:
-- UPDATE 表名 set colnum_name = value,[colnum_name = value,...] where [条件]
  • where子句可以有大于小于判断,between and
  • 也可以通过and连接多个条件

3.5、删除

delete 命令

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

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

-- 删除指定数据
DELETE FROM `student` WHERE id = 1;

TRUNCATE 命令

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

-- 清空 student 表
TRUNCATE `student`

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

4.1、DQL

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

  • 所有的查询操作都用它 Select
  • 简单的查询,复杂的查询它都能做~
  • 数据库中最核心的语言==

4.2、指定查询字段

-- 查询全部的学生   SELECT 字段 FROMSELECT * FROM student

-- 查询指定字段
SELECT `StudentNo`,`StudentName` 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 -- 查询自增的步长 (变量)

-- 学员考试成绩 + 1分查看
SELECT `StudentNo`,`StudentResult`+1 AS '提分后' FROM result

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

select 表达式 from 表

4.3、where 条件子句

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

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

逻辑运算符

运算符语法描述
and &&a and b a&&b逻辑与,两个都为真,结果为真
or
Not !not a ! a逻辑非,真为假,假为真!
-- ==================== where ====================
SELECT `studentNo`, `StudentResult` FROM `result`

-- 查询考试成绩在 95~100 分之间
SELECT `studentNo`, `StudentResult` FROM `result`
WHERE `StudentResult` >= 95 AND `StudentResult` <= 100

-- and  &&
SELECT `studentNo`, `StudentResult` FROM `result`
WHERE `StudentResult` >= 95 && `StudentResult` <= 100

-- 模糊查询(区间)
SELECT `studentNo`, `StudentResult` FROM `result`
WHERE `StudentResult` BETWEEN 95 AND 100

-- 除了1000号学生之外的同学的成绩
SELECT `studentNo`, `StudentResult` FROM `result`
WHERE `studentNo` != 1000;

-- !=  not
SELECT `studentNo`, `StudentResult` FROM `result`
WHERE NOT `studentNo` = 1000

模糊查询 : 比较运算符

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

image.png

image.png

联表查询 JOIN

/* 思路
1. 分析需求,分析查询的字段来自哪些表,(连接查询)
2. 确定使用哪种连接查询? 7种
确定交叉点(这两个表中哪个数据是相同的)
判断的条件 : 学生表的中 studentNo = 成绩表 studentNo
*/

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 s
RIGHT JOIN result r
ON s.studentNO = r.studentNO

-- Left Join
SELECT s.studentNO,studentName,SubjectNo,StudentResult
FROM student s
LEFT JOIN result r
ON s.studentNO = r.studentNO
操作描述
Inner join返回的是两个表的交集数据,即同时满足连接条件的行
left join会从左表中返回所有的值,即使右表中没有匹配
right join会从右表中返回所有的值,即使左表中没有匹配

自连接

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

image.png

父类
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`

SQL 常用函数

一、常用数学函数

函数说明
ABS(x)返回x的绝对值
CEIL(x)向上取整
FLOOR(x)向下取整
RAND()返回0-1的随机数
SIGN(x)返回x的符号,正数返回1,负数返回-1,0返回0
PI()返回圆周率
TRUNCATE(x, y)返回数值x保留到小数点后y位的值
ROUND(x)返回离x最近的整数(四舍五入)
MOD(x, y)返回x除以y以后的余数
POW(x, y)返回x的y次方
SQRT(x)返回x的平方根
EXP(x)返回e的x次方
-- 示例
SELECT ABS(-10),  -- 10
       CEIL(4.3),  -- 5
       FLOOR(4.7),  -- 4
       RAND(),  -- 随机数
       ROUND(3.14159, 2),  -- 3.14
       MOD(10, 3);  -- 1

二、常用字符串函数

函数说明
CHAR_LENGTH(s)返回字符串s的字符数
LENGTH(s)返回字符串s的字节长度
CONCAT(s1, s2...)将字符串s1,s2等多个字符串合并为一个字符串
CONCAT_WS(x, s1, s2...)同CONCAT(s1,s2,...),但每个字符串之间要加上x
UPPER(s) / UCASE(s)将字符串s的所有字母变成大写字母
LOWER(s) / LCASE(s)将字符串s的所有字母变成小写字母
LEFT(s, n)返回字符串s的前n个字符
RIGHT(s, n)返回字符串s的后n个字符
TRIM(s)去掉字符串s开始和结尾处的空格
REPLACE(s, s1, s2)将字符串s2替代字符串s中的字符串s1
SUBSTR(s, start, length)从字符串s的start位置截取长度为length的子字符串
REVERSE(s)将字符串s的顺序反过来
STRCMP(s1, s2)比较字符串s1和s2
-- 示例
SELECT CHAR_LENGTH('hello'),  -- 5
       CONCAT('Hello', ' ', 'World'),  -- Hello World
       UPPER('hello'),  -- HELLO
       LEFT('Hello World', 5),  -- Hello
       REPLACE('Hello World', 'World', 'SQL'),  -- Hello SQL
       SUBSTR('Hello World', 7, 5);  -- World

三、日期和时间函数

函数说明
CURDATE() / CURRENT_DATE()返回当前日期
CURTIME() / CURRENT_TIME()返回当前时间
NOW() / SYSDATE()返回当前日期和时间
YEAR(date)返回日期date的年份
MONTH(date)返回日期date的月份
DAY(date)返回日期date的天数
HOUR(time)返回时间time的小时值
MINUTE(time)返回时间time的分钟值
SECOND(time)返回时间time的秒数
DATE_FORMAT(date, format)以不同格式显示日期/时间
DATEDIFF(date1, date2)计算两个日期相差的天数(date1 - date2)
DATE_ADD(date, INTERVAL expr type)日期加上一个时间间隔
DATE_SUB(date, INTERVAL expr type)日期减去一个时间间隔
-- 示例
SELECT CURDATE(),  -- 2023-10-01
       CURTIME(),  -- 14:30:25
       NOW(),  -- 2023-10-01 14:30:25
       YEAR('2023-10-01'),  -- 2023
       MONTH('2023-10-01'),  -- 10
       DATE_FORMAT(NOW(), '%Y-%m-%d %H:%i:%s'),  -- 2023-10-01 14:30:25
       DATEDIFF('2023-10-10', '2023-10-01'),  -- 9
       DATE_ADD('2023-10-01', INTERVAL 7 DAY);  -- 2023-10-08

四、聚合函数(常用)

函数说明
COUNT()返回指定列的总行数
SUM()返回指定列的总和
AVG()返回指定列的平均值
MAX()返回指定列的最大值
MIN()返回指定列的最小值
GROUP_CONCAT()将分组后的列值连接成字符串
-- 示例
SELECT COUNT(*) AS 总记录数,
       SUM(score) AS 总分,
       AVG(score) AS 平均分,
       MAX(score) AS 最高分,
       MIN(score) AS 最低分
FROM student_scores;

五、流程控制函数

函数说明
IF(expr, v1, v2)如果表达式expr成立,返回v1,否则返回v2
IFNULL(v1, v2)如果v1不为NULL,返回v1,否则返回v2
CASE WHEN expr1 THEN v1 [WHEN expr2 THEN v2...] [ELSE vn] END多条件判断
-- 示例
SELECT name, score,
       IF(score >= 60, '及格', '不及格') AS 状态,
       CASE 
           WHEN score >= 90 THEN '优秀'
           WHEN score >= 80 THEN '良好'
           WHEN score >= 60 THEN '及格'
           ELSE '不及格'
       END AS 等级
FROM student_scores;

六、系统信息函数

函数说明
VERSION()返回数据库的版本号
USER()返回当前用户
DATABASE()返回当前数据库名
CONNECTION_ID()返回当前连接的连接ID
0返回最后插入的ID值
-- 示例
SELECT VERSION(), USER(), DATABASE();

七、加密函数

函数说明
MD5(str)计算字符串str的MD5 32位哈希值
SHA1(str)计算字符串str的SHA1 40位哈希值
PASSWORD(str)返回字符串str的加密版本(MySQL 5.7.6+已弃用)
-- 示例
SELECT MD5('password'),
       SHA1('password');

使用注意事项:

  1. 函数嵌套:可以嵌套使用函数,如 UPPER(CONCAT(first_name, ' ', last_name))
  2. 性能影响:在WHERE子句中对列使用函数会影响索引使用
  3. NULL处理:大部分函数在参数为NULL时返回NULL
  4. 数据类型:注意函数的参数和返回值的数据类型

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

什么是MD5?

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

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`)
)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 id = 1

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')

6、事务

6.1、什么是事务

要么都成功,要么都失败

——————

1、SQL 执行 A 给 B 转账 A 1000 ---->200 B 200

2、SQL 执行 B 收到 A 的钱 A 800 —> B 400

——————

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


事务原则: ACID 原则 原子性,一致性,隔离性,持久性 (脏读,幻读....)

原子性(Atomicity)

要么都成功,要么都失败

一致性(Consistency)

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

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

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

隔离性(Isolation)

事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,务之间要相互隔离。

隔离所导致的一些问题

脏读:

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

不可重复读:

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

虚读(幻读)

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

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' -- A加500

COMMIT; -- 提交事务,就被持久化了!
ROLLBACK; -- 回滚

SET autocommit = 1; -- 恢复默认值

7、索引

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

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

7.1、索引的分类

  • 主键索引 (PRIMARY KEY)
  • 唯一索引 (UNIQUE KEY)
  • 常规索引 (KEY/INDEX)
  • 全文索引 (FullText)
-- 索引的使用
-- 1、在创建表的时候给字段增加索引
-- 2、创建完毕后,增加索引

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

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

-- EXPLAIN 分析sql执行的状况
EXPLAIN SELECT * FROM student; -- 非全文索引

EXPLAIN SELECT * FROM student WHERE MATCH(studentName) AGAINST('刘');

权限管理

  • 选择所有的数据库.数据表*.*
-- 创建用户 CREATE USER 用户名 IDENTIFIED BY '密码'
CREATE USER kuangshen IDENTIFIED BY '123456'

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

-- 修改密码 (修改指定用户密码)
SET PASSWORD FOR kuangshen = PASSWORD('123456')

-- 重命名 RENAME USER 原来名字 TO 新的名字
RENAME USER kuangshen TO kuangshen2

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

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

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

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

-- 删除用户
DROP USER kuangshen

数据库备份

  • 使用命令行导出 mysqldump命令行使用
# mysqldump -h 主机 -u 用户名 -p 密码 数据库 表名 > 物理磁盘位置/文件名
mysqldump -hlocalhost -uroot -p123456 school1 student >D:/a.sql

# mysqldump -h 主机 -u 用户名 -p 密码 数据库 表1 表2 表3 > 物理磁盘位置/文件名
mysqldump -hlocalhost -uroot -p123456 school1 student >D:/a.sql

# mysqldump -h 主机 -u 用户名 -p 密码 数据库> 物理磁盘位置/文件名
mysqldump -hlocalhost -uroot -p123456 school1 >D:/c.sql
# 导入
# 登录的情况下,切换到指定的数据库
# source 备份文件
source d:/a.sql

数据库设计

9.1、为什么需要设计

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

糟糕的数据库设计:

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

良好的数据库设计:

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

三大范式

第一范式(1NF)

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

第二范式(2NF)

前提:满足第一范式

每张表只描述一件事情

第三范式(3NF)

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

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

规范性和性能的问题

关联查询的表不得超过三张表

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

10、JDBC(重点)

10.1、数据库驱动

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

image.png

10.2、JDBC

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

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

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

image.png

java.sql

javax.sql

还需要导入一个数据库驱动包 mysql-connector-java-5.1.47.jar

10.3、第一个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,'zhansan','123456','zs@sina.com','1980-12-04'),
      (2,'lisi','123456','lisi@sina.com','1981-12-04'),
      (3,'wangwu','123456','wangwu@sina.com','1979-12-04');
  1. 创建一个普通项目
  2. 导入一个数据库驱动
  • 建一个lib目录,粘贴驱动到此目录,右键添加为library

image.png 3. 编写测试代码

public class JDBCTestDome01 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //1.加载驱动
        //8.0以前没有.cj
        Class.forName("com.mysql.cj.jdbc.Driver");
        //2.用户信息与url,大部分默认端口都是3306
        //可以执行SHOW GLOBAL VARIABLES LIKE 'port';看端口
        String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
        String user = "root";
        String password = "123456";
        //3.通过连接创建数据库对象,connection就代表了数据库
        Connection connection = DriverManager.getConnection(url, user, password);
        //4,创建执行sql的对象 statement
        Statement statement = connection.createStatement();
        //5,执行sql,得到结果集
        ResultSet resultSet = statement.executeQuery("select * from users");
        while (resultSet.next()) {
            System.out.println(resultSet.getString("id"));
            System.out.println(resultSet.getString("NAME"));
            System.out.println(resultSet.getString("PASSWORD"));
            System.out.println(resultSet.getString("email"));
            System.out.println(resultSet.getString("birthday"));

        }
        //6.关闭资源
        resultSet.close();
        statement.close();
        connection.close();
    }
}

步骤总结:

1、加载驱动

2、连接数据库 DriverManager

3、获得执行sql的对象 Statement

4、获得返回的结果集

5、释放连接

DriveManger

// DriverManager.registerDriver(new com.mysql.jdbc.Driver());
Class.forName("com.mysql.jdbc.Driver"); // 固定写法,加载驱动
connection connection = DriverManager.getConnection(url, username, password);

// connection 代表数据库
// 数据库设置自动提交
// 事务提交
// 事务滚回
connection.rollback();
connection.commit();
connection.setAutoCommit();

URL

String url = "jdbc:mysql://localhost:3306/jdbcstudy?" +
             "useUnicode=true&characterEncoding=utf8&useSSL=true";

// mysql -- 3306
// 协议 ://主机地址:端口号/数据库名?参数1&参数2&参数3

// oralce -- 1521
//jdbc:oracle:thin:@localhost:1521:sid

Statement 执行SQL的对象 PreparedStatement 执行SQL的对象

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

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

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

获得指定的数据类型

  • getString()是通用的:可以读取任何数据库类型并转为字符串

  • 性能考虑:使用对应的类型方法(getInt()getDate()等)性能更好

  • 类型安全:明确数据类型可以避免意外错误

  • 可读性:使用对应的方法让代码意图更清晰

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

指针,光标

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

释放资源

释放资源

//6、释放连接
resultset.close();
statement.close();
connection.close(); // 耗资源,用完关掉!

10.4、statement对象

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

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

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

提取工具类

package com.zhang.test01.utils;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class JDBCUtils {
    private static String url ;
    private static String user ;
    private static String password ;
    static {
        //properties文件是Java中简单、轻量、易用的配置文件解决方案,特别适合存储键值对形式的配置信息。
        InputStream in = JDBCUtils.class.getClassLoader().getResourceAsStream("db.properties");
        Properties properties = new Properties();
    try {
            properties.load(in);
            String driver = properties.getProperty("driver");
            url=properties.getProperty("url");
            user=properties.getProperty("user");
            password=properties.getProperty("password");
            Class.forName(driver);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, user, password);
    }
    public static void close(Connection conn, Statement stmt, ResultSet rs) throws SQLException {
       if (rs != null) {
           rs.close();
       }
       if (stmt != null) {
           stmt.close();
       }
       if (conn != null) {
           conn.close();
       }
    }
}
driver=com.mysql.cj.jdbc.Driver
user=root
password=123456
url=jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
public class JDBCTestDome01 {
    public static void main(String[] args) throws SQLException {
        Connection conn = null;
        Statement statement = null;
        ResultSet rs = null;

        try{    //3.通过连接创建数据库对象,connection就代表了数据库

            conn = JDBCUtils.getConnection();
            //4,创建执行sql的对象 statement
            statement = conn.createStatement();
            //5,执行sql,得到结果集
            rs = statement.executeQuery("select * from users");
            while (rs.next()) {
                System.out.println(rs.getString("id"));
                System.out.println(rs.getString("NAME"));
                System.out.println(rs.getString("PASSWORD"));
                System.out.println(rs.getString("email"));
                System.out.println(rs.getString("birthday"));

            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {//6.关闭资源
            JDBCUtils.close(conn, statement, rs);
        }
    }
}

SQL 注入的问题

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

  • 下面是查询逻辑sql
"select * from users where NAME='"+username+"' and PASSWORD='"+pwd+"'"
  • 这个时候·假如有人输入

image.png

这个拼接后逻辑直接全部通过1=1,所有数据库数据都会被查出

PrepareStatment 预编译的声明

public class PrepareStatementTest {
    public static void main(String[] args) throws SQLException, ParseException {
        Connection connection = JDBCUtil02.getConnection();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        Date parse = sdf.parse("2004-10-16");
        //PreparedStatement预编译Statement,直接传一个sql,用?当占位符来当参数
        //没有字符串拼接,避免出现SQL注入
        PreparedStatement preparedStatement = connection.prepareStatement(
                "insert into users values(?,?,?,?,?)"
        );
        preparedStatement.setInt(1, 5);
        preparedStatement.setString(2, "zhanglei");
        preparedStatement.setString(3, "123456");
        preparedStatement.setString(4, "zhang@123.com");
        //数据库的Date类来自java.sql.Date包,需要传入一个long类型的时间戳来设置值
        preparedStatement.setDate(5, new java.sql.Date(parse.getTime()));
        System.out.println(parse.getTime());
        preparedStatement.executeUpdate();
        JDBCUtil02.close(connection, preparedStatement, null);
    }
}
  • 如果username = "admin' OR '1'='1"

  • PreparedStatement会自动将其转换为字符串值:"admin' OR '1'='1"

  • 而不是SQL语法的一部分

IDEA的数据库管理功能

image.png

image.png

image.png

image.png

image.png

10.8、事务

要么都成功,要么都失败

ACID原则

  • 原子性:要么全部完成,要么都不完成
  • 一致性:总数不变
  • 隔离性:多个进程互不干扰
  • 持久性:一旦提交不可逆,持久化到数据库了

隔离性的问题:

  • 脏读:一个事务读取了另一个没有提交的事务
  • 不可重复读:在同一个事务内,重复读取表中的数据,表数据发生了改变
  • 虚读(幻读) :在一个事务内,读取到了别人插入的数据,导致前后读出来结果不一致
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-100 where name = 'A'";
        st = conn.prepareStatement(sql1);
        st.executeUpdate();
        
        int x = 1/0; // 报错

        String sql2 = "update account set money = money+100 where name = 'B'";
        st = conn.prepareStatement(sql2);
        st.executeUpdate();

        //业务完毕,提交事务
        conn.commit();
        System.out.println("成功!");

    } catch (SQLException e) {
        // 如果失败,则默认回滚
        try {
            conn.rollback(); // 如果失败则回滚事务
        } catch (SQLException e1) {
        }
    }
}

开启事务后,必须显式提交或回滚。不要依赖任何"自动"行为,否则会导致数据不一致。

数据库连接池

连接池是一个预先创建并维护一组数据库连接的技术,当应用程序需要连接时,直接从池中获取,用完后归还给池,而不是真正关闭。

  • 传统JDBC连接的问题

  • 创建开销大:每次TCP三次握手、数据库认证、资源分配

  • 响应慢:连接建立通常需要100ms-500ms

  • 并发限制:数据库连接数有限制

  • 资源浪费:频繁创建/销毁连接

开源数据源实现

  • HikariCP

  • DBCP

  • C3P0

  • Druid:阿里巴巴

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

DBCP

package com.zhang.test02.util;

import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;

import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

public class DBCPUtils {
   private static BasicDataSource dataSource=null;
    static {
        InputStream in = JDBCUtil02.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
        Properties properties = new Properties();
        try {
            properties.load(in);
            dataSource = BasicDataSourceFactory.createDataSource(properties);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
    public static void close(Connection conn, Statement stmt, ResultSet rs) throws SQLException {
        dataSource.close();
    }
}

为什么 BasicDataSourceFactory 需要 commons-logging?

看一下 DBCP2 源码:

// BasicDataSourceFactory.java 第 64 行
private static final Log log = LogFactory.getLog(BasicDataSourceFactory.class);

BasicDataSourceFactory静态初始化时就创建了日志对象,所以即使你不用日志功能,这个类也会尝试加载 commons-loggingimage.png

C3P0

需要用到的 jar 包

c3p0-0.9.5.5、mchange-commons-java-0.2.19