MySQL

2022-10-28 11:42:22 浏览数 (2)

MySQL

1、初识数据库

安装数据库这里省略;

1.1my.ini 基本配置

代码语言:javascript复制
[mysqld]
basedir=F:javaMySQL  #基本目录
datadir=F:javaMySQLdata  #数据目录
port=3306  #端口
skip-grant-tables   # 跳过密码验证

1.2常用命令

命令

说明

net start mysql

启动mysql服务

net stop mysql

关闭mysql服务

update mysql.user set authentication_string=password('root')

where user='root' and Host = 'localhost';修改mysql密码

flush privileges

刷新权限

mysql -u root -p

登录mysql

show databases

查看所有数据库

use 数据库名

切换数据库

describe 表名

显示表信息

crate 表名

创建数据库

exit

退出

1.4注释

符号

说明

--

单行注释

/* */

多行注释

2、数据库操作

2.1结构化查询语言分类

名称

说明

命令

数据定义语言(DDL)

定义和管理数据对象,如数据库,数据表等

CREATE,DROP,ALTER

数据操作语言(DML)

用于操作数据库对象中所包含的数据

INSERT,UPDATE,DELETE

数据查询语言(DQL)

用于查询数据库的数据

SELECT

数据控制语言(DCL)

用于管理数据的语言,包括全选及数据更改

GRANT,COMMIT,ROLLBACK

2.2操作数据库

2.2.1创建数据库
代码语言:javascript复制
create database [if not exists] 数据库名;
-- 如果表名或者字段名是一个特殊的符号,就要带``
2.2.2 删除数据库
代码语言:javascript复制
drop database [if exists] 数据库名; 
2.2.3 切换数据库
代码语言:javascript复制
use 数据库名;
2.2.4 查看所有的数据库
代码语言:javascript复制
show databases --查看所有的数据库

2.3 数据库的列信息

2.3.1数值

类型

说明

字节

tinyint

十分小的数据

1个字节

smallint

较小的数据

2个字节

mediumint

中等大小的数据

3个字节

int

标准的整数

4个字节

bigint

较大的整数

8个字节

float

浮点数

4个字节

double

浮点数

8个字节

decimal

字符串形式的浮点数decimal(m,d)

m个字节

  • decimal(13,2),表示这个数字有13位,小数点后又2位;
    • decimal(m,d) m是数字的最大位数,他的范围是从1-65; d是小数点后的位数,他的范围是0-30并且不能大于m。 如果m被省略了,那么m的值默认为10, 如果d被省略了,那么d的值默认为0.
  • 数字的长度跟取值范围没有关系(它的取值范围是确定了的),只有zerofill的时候才有关系(会填充到0到指定的位数,比如int(4) 填写11->0011,填写12345->12345 );
2.3.2 字符串

类型

说明

长度

char

字符串固定大小

0~255

varcahr

可变字符串

0~65535

tinytext

微型文本(存博客等)

2^8-1

text

文本串(存大文本)

2^16-1

char 和 varchar区别

代码语言:javascript复制
 -- char(1):
 --字符串类型,小括号中指定的是字符数,固定字符长度,不足指定长度,自动使用空格,尾部补空格(查出来的时候就自动去掉空格),会额外占用空间,长度范围:1~255
-- varchar(10):
--字符串类型,小括号中指定的是字符数,可变装都字符串,可以是存储小于指定长度的字符串,不可以超出长度(不会自动扩充),长度范围:1~65535
2.3.3 时间日期

类型

时间格式

date

YYYY-MM-DD

time

HHH:mm:ss

datetime

YYYY-MM-DD HH:mm:ss

timestamp

时间戳

year

年份

2.3.4 null

类型

说明

null

没有值,未知,注意不要使用null运算,结果位null

2.4 数据库的字段属性

2.4.1 Unsigned
  • 无符号的整数
  • 声明了该列不能为负数
2.4.2 zerofill
  • 0填充的
  • 不足的位数,使用0来填充,int(3), 5 -> 005
2.4.3 auto_Increment
  • 自动增长的,每条数据,自动在上一个记录数上加1(默认)
  • 通常设置主键,且必须是整数类型
  • 可以自定义设置主键自增的起始值和步长
2.4.4 null 和 not null
  • null ,如果不填写值,默认就是null
  • not null,如果不填写值就会报错
2.4.5 业务中每个表必须存在的5个字段

字段名

说明

id

主键

version

乐观锁

is_delete

逻辑删除

gmt_create

创建时间

gmt_update

修改时间

2.5 创建数据库表

2.5.1 建表格式
代码语言:javascript复制
create table [if not exists] `表名`(
   '字段名1' 列类型 [属性][索引][注释],
   '字段名2' 列类型 [属性][索引][注释],
  #...
   '字段名n' 列类型 [属性][索引][注释]
)[表类型][表字符集][注释];


-- 快速建表,直接将查询的数据建成一张表
-- crate table table_name (select_SQL)
create table employees_temp11(select * from employees where department_id in(50,60));
2.5.2 建表案例
代码语言:javascript复制
-- auto_increment 自增
-- 字符串使用 单引号括起来
-- 所有的语句后面加,英文的逗号,最后一个不用加
-- primary key 主键,一般一个表只有一个唯一的主键

-- 目标 : 创建一个school数据库
-- 创建学生表(列,字段)
-- 学号int 登录密码varchar(20) 姓名,性别varchar(2),出生日期(datatime),家庭住址,email

-- 创建表之前, 一定要先选择数据库
USE school;

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;
2.5.3 常用命令
代码语言:javascript复制
-- 查看创建数据库的语句
SHOW CREATE DATABASE school;

-- 查看创建表的语句
SHOW CREATE TABLE student;

-- 显示表结构
DESC student;  

-- 设置严格检查模式(不能容错了)SET sql_mode='STRICT_TRANS_TABLES';

2.6 数据表的类型

2.6.1 MYSIAM 和 INNODB 对比

名称

MYISAM

INNODB

事务支持

不支持

支持

数据行锁定

不支持

支持

外键约束

不支持

支持

全文索引

支持

不支持

表空间大小

较小

较大,约为MYISAM2倍

常规使用操作:

  • MYISAM 节约空间,速度快
  • INNODB 安全性高,事务的处理,多表多用户操作
2.6.1 物理空间存在的位置

所有的数据库文件都存在date目录下,每一个文件对应一个数据库;

本质还是文件的存储!

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

  • InnoDB在数据库表中只用一个*.frm文件,以及上级目录的ibdata1文件
  • MYISAM对应文件
    • *.frm 表结构的定义文件
    • *.MYD 数据文件(data)
    • *.MYI 索引文件(index)
2.6.2 设置数据库表的字符集编码
代码语言:javascript复制
charset=utf8
--不设置的话,回是mysql默认的字符编码(不支持中文)

2.7 修改删除表

2.7.1 修改表 alter

代码语言:javascript复制
-- 修改表名 :
--ALTER TABLE 旧表名 RENAME AS 新表名;
ALTER TABLE teacher RENAME AS teacher1;

-- 添加字段 :
--ALTER TABLE 表名 ADD 字段名 列属性[属性];
ALTER TABLE teacher1 ADD age INT(12);

-- 修改字段 : 
--ALTER TABLE 表名 MODIFY 字段名 列类型[属性];
ALTER TABLE teacher1 MODIFY age VARCHAR(12);	 -- 修改约束(注意,如果修改字段类型,一定要兼容之前的类型)
-- ALTER TABLE 表名 CHANGE 旧字段名 新字段名 列类型 [属性]
ALTER TABLE teacher1 CHANGE age age1 INT(12);    -- 字段重命名

-- 删除字段 : 
--`ALTER TABLE 表名 DROP 字段名;`
ALTER TABLE teacher1 DROP age1;
2.7.2 删除表
代码语言:javascript复制
-- 删除表(如果表存在再删除):
--DROP TABLE [IF EXISTS] 表名;
DROP TABLE IF EXISTS teacher1;
--所有的创建和删除操作尽量加上判断,以免报错

3、MySQL数据管理

3.1 外键(了解)

方式一 再创建表的时候,增加约束

代码语言:javascript复制
-- 年级表 (id年级名称)
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;


-- 学生表的gradeid 字段 要取引用年级表的gradeid
-- 1、定义外键
	-- KEY 外键名 (字段名)
-- 2、给这给外键添加约束(执行索引)
	-- constraint 外键名 foreign key (被引用表的字段名) references 被引用表 (被引用字段)
-- 学生信息表 (学号,姓名,性别,年级,手机,地址,出生日期,邮箱,身份证号)
CREATE TABLE `student2` (
    `studentno` INT(4) NOT NULL COMMENT '学号',
    `studentname` VARCHAR(20) NOT NULL DEFAULT '匿名' COMMENT '姓名',
    `sex` TINYINT(1) DEFAULT '1' COMMENT '性别',
    `gradeid` INT(10) DEFAULT NULL COMMENT '年级',
    `phoneNum` VARCHAR(50) NOT NULL COMMENT '手机',
    `address` VARCHAR(255) DEFAULT NULL COMMENT '地址',
    `borndate` DATETIME DEFAULT NULL COMMENT '生日',
    `email` VARCHAR(50) DEFAULT NULL COMMENT '邮箱',
    `idCard` VARCHAR(18) DEFAULT NULL COMMENT '身份证号',
    PRIMARY KEY (`studentno`),
   -- KEY `FK_gradeid` (`gradeid`),
    CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;

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

** 创建表的时候没有外键关系,后面再修改表添加外键**

代码语言:javascript复制
-- ALTER TABLE 表
--add constraint 约束名(外键名) foreign key (作为外键的列) references 被引用表 (被引用字段);
ALTER TABLE `student`
ADD CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`);

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

最佳实践

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

3.2 DML语言(全部记住)

数据库操作语言

  • insert
  • update
  • delete

3.3 添加 insert

代码语言:javascript复制
-- 插入语句(添加)
-- 语法 : INSERT INTO 表名[(字段1,字段2,字段3,...)] VALUES('值1','值2','值3')

INSERT INTO `student`(`name`) VALUES ('张三'); --插入单个字段值

INSERT INTO `student`(`name`,`address`,`sex`) VALUES ('张三','admin','男'); --插入多个字段值

INSERT INTO `student`(`name`,`address`,`sex`) 
VALUES ('李四','pppppp','女'),('王五','tttttt','男'); -- 插入多条信息


-- 将同一个数据库中的数据 一个表中的数据 导入到另外一个表中
-- 将grades_temp表中的年级数据,迁移或者备份到grades_his
-- 表结构一样
insert into grade_his select * from grade_temp;
-- 表结构不一样,指定字段名
insert into grade_his(grade_id,grade_name)  select grade_id,grade_name from grade_temp;

-- 不同数据库转储数据
-- 将数据插入到指定数据库中
-- 插入到test数据库的grade_his表(grade_id,grade_name)字段中
insert into test.grade_his(grade_id,grade_name)  select grade_id,grade_name from grade_temp;

注意事项:

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

3.4 修改 update

代码语言:javascript复制
-- UPDATE 表名 SET column_name=value [,column_name2=value2,...] [WHEREcondition];

UPDATE `student` SET `name`='subei',`birthday`=CURRENT_DATE WHERE id = 5;

条件:where 字句 运算符 id 等于某个值,再某个区间修改。。。

运算符

说明

=

等于

<> 或 !=

不等于

>

大于

<

小于

>=

大于等于

<=

小于等于

between...and...

再某个范围内[num1,num2]

and

并集

or

交集

3.5 删除 delete

代码语言:javascript复制
-- DELETE FROM 表名 [WHERE condition];

-- 删除表数据(不带where条件的delete)
DELETE FROM `student`; -- 删除所有的数据

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

代码语言:javascript复制
--清空 student表
--truncate table 表名
truncate table `student`;
-- 或 truncate 表名
truncate `student`;

** delete 和 truncate 区别**

  • 相同点:都能删除数据,都不会删除表结构
  • 不同点
    • truncate 重新设置自增列,计数器会归零
    • truncate 不会影响事务

delete删除的问题,重启数据库,现象 (了解)

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

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

4.1 DQL

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

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

select语法

代码语言:javascript复制
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}];
   -- 指定查询的记录从哪条至哪条

4.2 指定查询字段

4.2.1 基本查询
代码语言:javascript复制
-- 语法: select 字段... from 表
-- 查询全部的学生  select 字段 from 表
select * from student;

-- 查询指定字段
select `studentno`,`studentname` from student;
4.2.2 as 别名

有的时候,列名字不是那么的见名知意。我们起别名 as 字段名 as 别名

代码语言:javascript复制
-- 别名,给结果其一个名字   as  可以给字段起别名,也可以给表起别名
select `studentno` as '学号',`studentname` as '学生姓名' 
from student as s

-- 函数 concat(a,b)
select concat('姓名:',studentname) as '新名字' from student;
4.2.3 distinct 去重
代码语言:javascript复制
select distinct `studentno` from result; -- 去重
4.2.4 数据库表达式
代码语言:javascript复制
-- select 表达式 from 表
select version() -- 查询系统版本(函数)
select 100*3-1 '计算结果' -- 用来计算(表达式)
select @@auto_increment_increment  -- 自增步长(变量)

-- 学员考试  1 分查看
select `studentno`,`studentresult` 1 as '提分后' from result;

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

注意

代码语言:javascript复制
-- 用表达式
-- 查询员工表中,年薪大于9W的所有员工信息
select *
from employees
where Salary*12 > 90000;

-- 尽量不要在列上进行计算,因为如果列上有索引,会影响索引失效,导致查询速度降低
select *
from employees
where Salary > 90000/12;

4.3 where 条件子句

4.3.1 逻辑运算符

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

运算符

语法

描述

and &&

a and b a && b

逻辑与

or ||

a or b a || b

逻辑或

not !

not a ! a

逻辑非

尽量使用英文字母;

4.3.1 模糊查询比较运算符

运算符

语法

说明

is null

a is null

如果操作符为null,结果为真

is not null

a is not null

如果操作符不为null,结果为真

between

a between b and c

若a在b和c之间,则结果为真

like

a like b

sql匹配,如果a匹配b,则结果为真

in

a in (a1,a2,a3...)

假设a在a1,或则a2...其中的某个值中,结果为真

代码语言:javascript复制
-- 查询姓刘的同学
-- 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 '%奋%';

-- ============ in ================
-- 查询 1001,1002,1003号学员
select `studentno`,`studentname` from `student`
where studentno in (1001,1002,1003,1004);

-- 查询在北京的学生
select `studentno`,`studentname` from `student`
where address in ('安徽','河南洛阳');

-- ======= null not null ======
-- 查询地址为空的学生 null ''
select `studentno`,`studentname` from `student`
where address = '' or address is null;

-- 查询有出身日期的学生  不为空
select `studentno`,`studentname` from `student`
where borndate is not null;

-- 查询没有出生日期的同学  为空
select `studentno`,`studentname` from `student`
where borndate is null;

注意点

代码语言:javascript复制
在大表中使用模糊查询,如果当前查询列有索引,要避免使用左模糊查询,会导致索引失败,影响查询速度;

4.4 联表查询

4.4.1 等值连接,左连接,右连接
代码语言:javascript复制
-- inner join 
select s.studentno,studentname,subjectno,studentresult
from student as s
inner join result as r
where 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

会从右表中返回所有的值,即使左表中没有匹配(右表为基准)

代码语言:javascript复制
-- =========p20 联表查询 join============
-- 查询参加了考试的同学(学号,姓名,科目编号,分数)
select * from student
select * from result

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

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

-- inner join 
-- 等值连接
select s.studentno,studentname,subjectno,studentresult
from student as s
inner join result as r
where 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


-- 查询缺考的同学
select s.studentno,studentname,subjectno,studentresult
from student s
left join result r
on s.studentno = r.studentno
where studentresult is null;


-- 思考题 (查询了参加考试的学生信息:学号,学生姓名,科目名,分数)
/*
 思路
 1.分许需求,分析查询的字段来自哪些表,student,result,subject(连接查询)
 2. 确定使用娜总联接查询? 7 种
 确定交叉点(这两个表种那个数据是相同的)
 判断条件: 学生表种的 studentno = 成绩表 studentno
*/

select s.studentno,studentname,subjectname,studentresult
from student s
right join result r
on s.studentno = r.studentno
inner join subject sub
on r.subjectno = sub.subjectno

-- 我要查询哪些数据 select ...
-- 从哪几个表中查 from 表 xxx join 连接的表 on 交叉条件
-- 假设存在一种多张表查询,慢慢来,先查询两张表,再慢慢增加

-- from a left join b  以左边的a为基准
-- from a right join b  以右边的b为基准
4.4.自连接

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

自连接的表

categoryid

pid

categoryName

2

1

信息技术

3

1

软件开发

4

3

数据库

5

1

美术设计

6

3

web开发

7

5

ps技术

8

2

办公信息

拆成两张表

父类表

categoryid

categoryName

2

信息技术

3

软件开发

5

美术设计

子类表

pid

categoryid

categoryName

3

4

数据库

2

8

办公信息

3

6

web开发

5

7

ps技术

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

父类

子类

信息技术

办公信息

软件开发

数据库

软件开发

web开发

美术设计

ps技术

代码语言:javascript复制
-- 查询父子信息 把一张表看为两张一摸一样的表
select a.`categoryname` as '父栏目',b.`categoryname` as '子栏目'
from `category` as a,`category` as b
where a.`categoryid` = b.`pid`;

4.5 分页limit 和排序order by

4.5.1 排序
代码语言:javascript复制
-- 排序: 升序 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 asc;
4.5.2 分页

注意limit后面的参数必须是具体值不能是表达式

代码语言:javascript复制
-- 100 万条数据
-- 为什么要分页?
-- 缓解数据库的压力,给人的体验感更好(瀑布流,无限刷)

-- 分页,每页只显示五条数据
-- 语法:limit 起始数据下标,页面大小
-- 网页应用:当前页,总页数,页面大小
-- limit 0,5   1~5
-- limit 1,5   2~6
-- limit 5,5   6~10

-- 注意limit后面的参数必须是具体值,不能是表达式

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 asc
limit 5,5;

-- 第一页  limit 0,5   (1-1)*5
-- 第二页  limit 5,5   (2-1)*5
-- 第三页  limit 10,5  (3-1)*5
-- 第N页   limit       (n-1)*pagesize,pagesize
-- pagesize:页面大小
-- (n-1)*pagesize 起始值
-- n当前页
-- 总页数 = 数据总数/页面大小

语法:limit(起始下标,pagesize)

4.6 子查询

代码语言:javascript复制
-- 查询课程为 高等数学-2 且分数不小于 80 的同学的学号和姓名
select distinct s.studentno,studentname
from student s
inner join result r
on s.studentno = r.studentno
inner join `subject` sub
on r.subjectno = sub.subjectno
where sub.subjectname = '高等数学-2' and studentresult >= 80

-- 子查询
select distinct s.studentno,studentname,subjectno
from student s
inner join result r
on r.studentno = s.studentno
where studentresult >= 80 and subjectno = (
		select subjectno from subject 
		where subjectname = '高等数学-2'
)

--  子查询
-- 查高等数学-2的科目号->查成绩大于80且是高等数学的学号->查高等数学成绩大于80的学号和姓名
select s.studentno,studentname
from student s
where studentno in(
		select distinct studentno from result
		where studentresult >= 80
		and subjectno =(
			select subjectno from subject
			where subjectname = '高等数学-2'
		)
)

-- 练习
-- 查询C语言-1 前5名同学的成绩的信息(学号,姓名,分数)
-- 使用子查询
select s.studentno,studentname,studentresult
from student s
inner join result r
on s.studentno = r.studentno
where r.subjectno = (
	select subjectno from subject
	where subjectname = 'C语言-1'
)
order by r.studentresult desc
limit 0,5 
4.6.1子查询遇到的问题

1235 - This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'

代码语言:javascript复制
select s.stu_no '学号',s.stu_name '姓名'
from students s
where s.stu_no in(
		-- 查询C语言成绩前5 的学生
		select r.stu_no
		from results r
		where r.subj_no = (
				--  查询 C语言-1 的课程编号 5
				select sub.subj_no
				from subjects sub
				where sub.subj_name = 'C语言-1'
		)
		order by r.stu_result desc
		limit 0,5
-- 1001,1000,1006,1005,1017
);
-- 1235 - This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'

解决方法

代码语言:javascript复制
-- 解决方法
-- 保证in 里面是一个结果集,先将limit 分页出来的数据 作为一张表,在从这这张表中查数据
select s.stu_no '学号',s.stu_name '姓名'
from students s
where s.stu_no in(
		-- 查询C语言成绩前5 的学生
		select t.* from   (select r.stu_no
		from results r
		where r.subj_no = (
				--  查询 C语言-1 的课程编号 5
				select sub.subj_no
				from subjects sub
				where sub.subj_name = 'C语言-1'
		)
		order by r.stu_result desc
		limit 0,5) as t
-- 1001,1000,1006,1005,1017
);
4.6.2 扩展 form 子查询

将查询的结果集作为一张表;

代码语言:javascript复制
-- 查询成绩表,使用子查询,根据课程名称,查询所有的学生编号和成绩
-- 1.查询成绩表,使用子查询,根据课程名称,查询所有的学生编号和成绩
select r.stu_no,r.stu_result
from results r
where r.subj_no = (
		--  查询 C语言-1 的课程编号 5
		select sub.subj_no
		from subjects sub
		where sub.subj_name = 'C语言-1'
);

-- 将第一步的查询结果,作为一张表,跟学生表关联
select st.stu_no,st.stu_name,sr.stu_result
from students st,(
		select r.stu_no,r.stu_result
		from results r
		where r.subj_no = (
				--  查询 C语言-1 的课程编号 5
				select sub.subj_no
				from subjects sub
				where sub.subj_name = 'C语言-1'
		)
) sr -- 给结果集去别名,使用该结果集的时候直接使用别名
where st.stu_no = sr.stu_no
order by r.stu_result desc
limit 0,5
4.6.3 扩展exists用法

注意学习这种子查询的思维

子查询中的语句可以使用外层查询的字段进行条件查询

代码语言:javascript复制
-- 查询员工表中,所有管理者的信息
-- 子查询写法:使用 in 子查询,先确定所有的管理者(manager_id中都是管理者),注意去重

select e.employee_id '编号',e.last_name '姓名',e.department_id '部门编号'
from employees e
where e.employee_id in(
	select distinct manager_id
	from employees
	where manager_id is not null
)


-- 扩展用法1:使用exists,性能优于in
-- 原理:自动将外层的employee_id的值,带入exists子查询中,如果查询到记录,返回true,外层就将此记录保留,否则丢弃
select e.employee_id '编号',e.last_name '姓名',e.department_id '部门编号'
from employees e
where exists (
	select e2.employee_id
	from employees e2
	where e2.manager_id = e.employee_id
)

-- 练习
-- 查询员工表,各个部门中,比员工所在部门平均工资高的员工信息

-- 子查询常规用法
-- 各部门的平均工资
select e.department_id '部门编号',avg(e.salary) '平均工资'
from employees e
group by e.department_id;

-- 将员工表和上一步的部门平均工资结果集,通过部门编号进行连接查询(等值),可以关联为一张表数据,将进行不同字段的比较
select e.employee_id '编号',e.last_name '姓名',e.department_id '部门编号',e.salary '工资',t.avgsalary '部门平均工资'
from employees e,(
  -- 各部门的平均工资  的结果集作为一张表,与其员工表进行查询
	select department_id,avg(salary) as 'avgsalary'
	from employees
	group by department_id
) as t
where e.department_id = t.department_id
and e.salary > t.avgsalary;

-- 子查询高级用法:借助exists的原理,但是效率不是很好
select e1.employee_id '编号',e1.last_name '姓名',e1.department_id '部门编号',e1.salary '工资'
from employees e1
where e1.salary > (
	select avg(salary) 
	from employees e2
	where e2.department_id = e1.department_id
);

4.7 分组和过滤 group by having

代码语言:javascript复制
-- 分组查询,一般配合分组(多行函数)函数使用,不可以直接使用单行函数使用
-- group by  又去重的效果,也可以作为去重的方法
-- 查询不同课程的平均分,最高分,最低分,平均分大于80
select subjectname,avg(studentresult) '平均分',max(studentresult) ' 最高分',min(studentresult) '最低分'
from result r
inner join `subject` sub
on r.subjectno = sub.subjectno
group by r.subjectno -- 通过说明字段分组
having 平均分 > 80

5、MySQL常用函数

5.1 常用函数

5.1.1 数学函数(单行函数)

函数

说明

select abs(-8)

绝对值

select ceiling(9.3)

向上取整

select floor(9.8)

向下取整

select round(num,x)

四舍五入,保留为小数

select rand()

0到1之间的随机数

select sign(-10)

判断一个数的符号 0返回0,负数返回,整数返回1

select truncate(96.88,1)

截断,第二个参数表示小数位

5.1.2 字符函数

函数

说明

select char_length('我爱MySQL')

字符串长度

select concat('l','love','you')

字符串拼接

select insert('我爱你',1,2,'小可爱')

查询,从指定替换指定长度

select lower('HUAYU')

转小写

select upper('huayu')

转大写

select instr('i love mysql','l')

返回第一次出现的字串的索引

select replace('坚持就能成功','坚持','努力')

替换指定字符

select substr('狂神说坚持就能成功',4,6)

截取指定字符substr(源字符,字符起始下标,字符长度)

select reverse('开始反转吧')

反转

select replace(str,'-','');

字符替换replace(uuid(),'-','');

5.1.3 时间日期函数(单行函数

函数

说明

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 date_format(now(),'%Y年%m月%d日 %H:%i:%s');

日期格式化

5.1.4 系统函数

函数

说明

select system_user()

系统用户

select user()

用户

select version()

版本

5.2 聚合函数(多行函数)

注意聚合函数,不能放在where之后,必须在一个已经确定的集合中使用聚合函数;大多放在select或者having之后,对你确定的集合进行过滤;

函数

说明

count()

计数 count(1) count(主键)

sum()

求和

avg()

平均值

max()

最大值

min()

最小值

代码语言:javascript复制
-- 都能够统计  表中的数据 (查询一个表中有多少个记录)
-- 统计查询结果总数,复合条件的记录数,就相当于一个计数器,有一条记录就加1,跟函数传入的值无关
-- 注意对大的表,避免使用count(*),效率第
-- 推荐使用count(1),或者 count(主键列)
-- 强调:使用count(非主键列),如果当前列值允许为空,统计时遇到值为空的记录时,不会计数
select count(studentname) from student; -- count(指定字段) 会忽略所有的null值
select count(*) from student;			-- count(*) 不会忽略所有的null

null值 ,本质 计算行数
select count(1) from student;			-- count(1) 不会忽略所有的null值 , 本质计算行数

select sum(studentresult) as '总和' from result;
select avg(studentresult) as '平均分' from result;
select max(studentresult) as '最高分' from result;
select min(studentresult) as '最低分' from result;
5.1.5 ifnull

空值处理函数,因为 null 值跟任何数运算结果都是null,而我们有时候又需要对null进行计算;

代码语言:javascript复制
-- 语法: ifnull(数据列,如果为null的默认值),这个时mysql特有的,oracle没有,它里面是nvl()

-- 查询员工表中所有人的年薪 (工资 绩效)*12  注意:有的人没有绩效,绩效为null
-- (salary   commission_pct)*12 当commission_pct为null 时结果为null
-- (salary salary*ifnull(commission_pct,0))*12 
-- 当commission_pct为null 时 commission_pct 为0,结果就不会为null了;
selecte.last_name '姓名',(salary salary*ifnull(commission_pct,0))*12 '年薪'
from employees e;

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

5.3.1 什么是MD5?

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

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

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

5.3.2 MD5加密,校验
代码语言:javascript复制
-- md5(字段或指定值)
-- 加密
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');

5.4 行转列

原数据

目标数据

案例代码

代码语言:javascript复制
--语法: SUM(CASE  行转列的字段 WHEN '行转列的字段匹配的数据' THEN 行专列后的数据来源字段 ELSE 0 END) as '行转列的字段匹配的数据'
/*
查询公司在2015-2018年之间,每年雇用的人数,结果类似下面的格式
		 total	2015	2016	2017	2018
		 74	     4	    9	   30	   31
*/
-- 查询并分组每年的人 得到原数据
select total.*
from
(
select year(hire_date) 'year',count(year(hire_date)) 'num'
from employees e
group by year(hire_date)
) as total
order by total.year;

-- 行转列 得到目标数据
select sum(total.num) 'total',
SUM(CASE  total.`year` WHEN '2000' THEN total.num ELSE 0 END) as '2000',
SUM(CASE  total.`year` WHEN '2006' THEN total.num ELSE 0 END) as '2006',
SUM(CASE  total.`year` WHEN '2007' THEN total.num ELSE 0 END) as '2007',
SUM(CASE  total.`year` WHEN '2009' THEN total.num ELSE 0 END) as '2009',
SUM(CASE  total.`year` WHEN '2010' THEN total.num ELSE 0 END) as '2010',
SUM(CASE  total.`year` WHEN '2011' THEN total.num ELSE 0 END) as '2011',
SUM(CASE  total.`year` WHEN '2013' THEN total.num ELSE 0 END) as '2013',
SUM(CASE  total.`year` WHEN '2014' THEN total.num ELSE 0 END) as '2014',
SUM(CASE  total.`year` WHEN '2015' THEN total.num ELSE 0 END) as '2015',
SUM(CASE  total.`year` WHEN '2016' THEN total.num ELSE 0 END) as '2016',
SUM(CASE  total.`year` WHEN '2017' THEN total.num ELSE 0 END) as '2017',
SUM(CASE  total.`year` WHEN '2018' THEN total.num ELSE 0 END) as '2018',
SUM(CASE  total.`year` WHEN '2019' THEN total.num ELSE 0 END) as '2019'
from
(
	-- 查询并分组每年的人
	select year(hire_date) as 'year',count(year(hire_date)) as 'num'
	from employees e
	group by year(hire_date)
) as total;

6、事务

6.1 什么是事务

  • 要么都成功,要么都失败。
  • 事务就是将一组SQL语句放在同一批次内去执行;
  • 如果一个SQL语句出错,则该批次内的所有SQL都将被取消执行

6.2 ACID原则

6.2.1 原子性 (Atomicity)

要么都成功,要么都失败;

6.2.2 一致性 (Consistency)

事务前后的数据完整性要保证一致(比如转账中事务前一共有100元,事务后也要有100元);

6.2.3 隔离性(Isonlation)

事务的隔离性是指多个用户并发访问数据时,数据库为每一个用户开启事务,不能被其他的事务的操作数据所干扰,事务之间要相互隔离;(比如 A给B转账 和 C给D转账 是隔离的);

6.2.4 持久性(Durability)

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

6.3 事务的隔离级别

6.3.1 脏读

一个事务读取了另一个事务没有提交的数据

6.3.2 不可重复读

在一个事务内读取表中的某一行数据,多次读取结果不同

(多次读到的值不同可能是在两次读取中又有事务对数据进行了修改);

(这个不一定是错误,值是某些场合不对);

6.3.3 幻读(虚读)

是指在一个事务内读取到了别的是事务新插入的数据,导致前后读取不一致;(一般是行数不一致,多了行数);

6.4 执行事务的过程

代码语言:javascript复制
-- mysql 是默认开启事务自动提交的
set autocommit = 0; -- 关闭
set autocommit = 1; -- 提交

-- 手动处理事务
set autocommit = 0; -- 关闭自动提交

-- 事务开启
start transaction  -- 标记一个事务的开始,从这个之后的sql都在同一个事务内 
-- 可省略,写了表示立即启动一个事务,不写系统会自动开启一个事务

insert xx
insert xx

-- (成功)提交:持久化(成功)
commit

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

rollback

-- 事务结束
set autommit =1 -- 开启自动提交


savepoint 保存点名 --   设置一个事务的保存点,事务比较长的时候
rollback savepoint 保存点名  -- 回滚到保存点
release savepoint  保存点名 -- 撤销(删除)保存点

案例:模拟转账

代码语言:javascript复制
-- 模拟转账
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、索引

7.1索引概述

索引(index)是帮助 MysQL高效获取数据的数据结构

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

7.2 索引的分类

索引名

说明

PRIMARY KEY

主键索引

UNIQUE KEY

唯一索引

KEY/INDEX

常规索引

FullText

全文索引(MyISAM)

7.2.1 PRIMARY KEY 主键索引
  • 唯一的标识,主键不可以重复,只能由一个列作为主键;
7.2.2 UNIQUE KEY 唯一索引
  • 避免重复的列出现,唯一索引可以重复,多个列多可以标识为唯一索引;
7.2.3 KEY/INDEX 常规索引
  • 默认的,index,key关键字来设置;
7.2.4 FullText 全文索引
  • 在的顶的数据引擎下才有,MyISAM;
  • 快速定位;
代码语言:javascript复制
-- 显示所有的索引信息 
show index from student;

-- 增加一个全文索引
alter table `kuangshen_school`.`student` add fulltext index `studentname` (`studentname`);

-- explain 分析sql执行的状况
explain select * from student; -- 非全文索引

explain select * from student where match(studentname) against('刘'); -- 全文索引

7.3 测试索引

添加数据

代码语言:javascript复制
-- 建表app_user:
CREATE TABLE `app_user` (
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(50) DEFAULT'' COMMENT'用户昵称',
`email` VARCHAR(50) NOT NULL COMMENT'用户邮箱',
`phone` VARCHAR(20) DEFAULT'' COMMENT'手机号',
`gender` TINYINT(4) UNSIGNED DEFAULT '0'COMMENT '性别(0:男;1:女)',
`password` VARCHAR(100) NOT NULL COMMENT '密码',
`age` TINYINT(4) DEFAULT'0'  COMMENT '年龄',
`create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
`update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8 COMMENT = 'app用户表';

-- 批量插入数据:100w
-- 插入100万条数据
DROP FUNCTION IF EXISTS mock_data;

DELIMITER $$ -- 写函数之前必须要写,标志
CREATE FUNCTION `mock_data`()
RETURNS INT
deterministic
BEGIN
	declare num int default 1000000;
    DECLARE i INT DEFAULT 0;
    WHILE i<num DO
		INSERT INTO `app_user`(`name`,`email`,`phone`,`gender`,`password`,`age`) values(CONCAT('用户',i),'xxxxxx@qq.com',CONCAT('18',FLOOR(RAND()*999999999)),FLOOR(RAND()*2),UUID(),FLOOR(RAND()*100));
        SET i = i 1;
	END WHILE;
    RETURN i;
END;

-- 查询 执行函数
select mock_data();

-- 删除 函数
drop function mock_date;

测试

代码语言:javascript复制
-- 没有加索引之前
SELECT * FROM app_user WHERE NAME = '用户9999'; -- 查看耗时 时间: 1.218s
SELECT * FROM app_user WHERE NAME = '用户9999'; -- 时间: 1.228s
SELECT * FROM app_user WHERE NAME = '用户9999'; -- 时间: 1.218s

EXPLAIN SELECT * FROM app_user WHERE NAME = '用户9999';

-- id_表名_字段名
-- CREATE INDEX 索引名 ON 表(字段)
CREATE INDEX id_app_user_name ON app_user(`name`);

-- 加上索引后查询
SELECT * FROM app_user WHERE NAME = '用户9999'; -- 时间: 0.007s
EXPLAIN SELECT * FROM app_user WHERE NAME = '用户9999';

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

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

索引的数据结构

Hash类型的索引

Btree:InnoDB的默认数据库结构

8、权限管理

8.1 可视化操作
8.2 SQL命令操作
代码语言:javascript复制
-- 创建用户
-- create user 用户名 identified '密码'
create user huayu identified by '123456';

-- 修改密码(修改当前用户密码)
set password = password('123456');

-- 修改密码(修改指定用户密码)
set password for huayu = password('123456');

-- 重命名
-- rename user 原来的名字 to 新的名字
rename user huayu to hauyu2

-- 用户授权 all privileges 全部的权限  库.表
-- all privilege 除了给别人授权,其他权限都有
grant all privileges on *.* to huayu 

-- 查询权限
show grants for huayu; -- 查看指定用户的权限
show grants for root@localhost;

-- 撤销权限 revoke 哪些权限,在哪个库撤销,给谁撤销
revoke all privileges on *.* from huayu;

-- 删除用户
drop user huayu;

9、数据库备份

9.1 为什么要备份

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

9.2 MySQL数据库备份的方式

  • 直接拷贝物理文件
  • 在可视化界面手动导出
  • 使用命令行导出 mysqldump 命令行使用
代码语言:javascript复制
-- 使用命令行导出
-- mysqldump -h 主机 -u 用户名 -p 密码 数据库  [表名...] > 物理磁盘位置/文件名
mysqldump -hlocalhost -uroot -p123456 school student >D:/a.sql

-- 导入
-- 登录的情况下,切换到只当数据库
-- source 备份的文件
source D:/a.sql

-- mysql命令
mysql -u用户名 -p密码 库名<备份的文件

10、规范数据库设计

10.1 为什么需要设计

糟糕的数据库设计:

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

良好的数据库设计:

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

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

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

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

  • 收集信息,分析需求
    • 用户表(用户登录注销,用户的个人信息,写博客,创建分类)
    • 分类表(文章分类,谁创建的)
    • 文章表(文章信息)
    • 友情链接表
    • 自定义表(系统信息,某个关键字,或者一些主字段) key:value
    • 说说表(发表心情。。id..content...create_time)
  • 标识的实体(把需求落地到字段)
  • 标识实体之间的关系
    • 写博客:user->blog
    • 创建分类: user->category
    • 关注:user->user
    • 友链:links
    • 评论:user->user->blog

10.2 三大范式

10.2.1为什么需要数据规范化
  • 信息重复
  • 更新异常
  • 插入异常
    • 无法正常显示信息
  • 删除异常
    • 丢失有效的信息
10.2.2 三大范式
10.2.2.1 第一范式(1FN)

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

10.2.2.2 第二范式(2FN)

前提:满足第一范式;

非主键必须完全依赖于主键,不能是部分依赖主键(完全依赖是针对于联合主键的情况,非主键列不能只依赖于主键的一部分);

10.2.2.3 第三范式(3FN)

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

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

10.2.3 规范性 和 性能的问题

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

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

11、JDBC(重点)

11.1 数据库驱动

我们安装好数据库之后,我们的应用程序也是不能直接使用数据库的,必须要通过相应的数据库驱动程序,通过驱动程 序去和数据库打交道;

11.2 JDBC

SUN公司为了简化、统一对数据库的操作,定义了一套Java操作数据库的规范(接口),称之为JDBC。

这套接口由数据库厂商去实现,这样,开发人员只需要学习jdbc接口,并通过jdbc加载具体的驱动,就 可以操作数据库。

  • JDBC全称为:Java Data Base Connectivity(java数据库连接),它主要由接口组成。
  • 组成JDBC的2个包:java.sql、javax.sql
  • 开发JDBC应用需要以上2个包的支持外,还需要导入相应JDBC的数据库实现(即数据库驱动包—mysql-connector-java-5.1.47.jar 具体版本根据实际需求)。

mysql-connector-java-5.1.47.jar。

11.3 第一个JDCBC程序

11.3.1 创建测试数据库表
代码语言:javascript复制
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');
11.3.2 步骤
  1. 新建一个Java项目;
  2. 添加lib目录(放jar包); jar包链接
  3. 编写测试代码;
代码语言:javascript复制
package com.huayu.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
        String url = "jdbc:mysql://localhost:3306/kuangshen_school?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT+8";
        String username = "root";
        String password = "root";

        //3.连接成功,数据库对象
        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); //返回的结果集,结果集中封装了我们查询的所有结果

        System.out.println("idtnametpasswordtemailttttbirthday");
        while(resultSet.next()){
            //字段名查询
            System.out.print(resultSet.getObject("id"));
            System.out.print("t" resultSet.getObject("name"));
            System.out.print("t" resultSet.getObject("password"));
            System.out.print("tt" resultSet.getObject("email"));
            System.out.println("tt" resultSet.getObject("birthday"));

            //小标查询 下表从1开始
//            System.out.print(resultSet.getObject(1));
//            System.out.print("t" resultSet.getObject(2));
//            System.out.print("t" resultSet.getObject(3));
//            System.out.print("tt" resultSet.getObject(4));
//            System.out.println("tt" resultSet.getObject(5));
        }

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

}

运行结果

代码语言:javascript复制
id	name	password	email				birthday
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
11.3.3 步骤总结

加载驱动 Class.forName

代码语言:javascript复制
 Class.forName("com.mysql.jdbc.Driver");

连接数据库 DriverManager

代码语言:javascript复制
Connection connection = DriverManager.getConnection(url, username, password);

获得执行sql的对象 Statement PreparedStatement

代码语言:javascript复制
Statement statement = connection.createStatement();
//PreparedStatement preparedStatement = connection.prepareStatement();

获得返回结果集 ResultSet

ResultSet resultSet = statement.executeQuery(sql);

释放连接

代码语言:javascript复制
resultSet.close();
statement.close();
//preparedStatement.close()
connection.close();

11.4 JDBC中对象解释

11.4.1 DriverManager
代码语言:javascript复制
Class.forName("com.mysql.jdbc.Driver"); //固定写法,加载驱动
Connection connection = DriverManager.getConnection(url, username, password);

//connection  代表数据库
//数据库设置自动提交
//事务提交
//事务回滚
connection.setAutoCommit(false);
connection.commit();
connection.rollback();
11.4.2 URL
代码语言:javascript复制
 String url = "jdbc:mysql://localhost:3306/kuangshen_school?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT+8";

//(协议)://主机地址:端口号/数据库名?参数1&参数2&参数3
// mysql 默认端口3306
jdbc:mysql://localhost:3306/sid
//如果连接的是本地的Mysql数据库,并且连接使用的端口是3306,那么的url地址可以简写为
jdbc:mysql:///sid
//oralce 默认端口1521
jdbc:oracle:thin:@localhost:1521:sid
//SqlServer写法 默认端口 1433
SqlServer写法:jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=sid
11.4.3 Statement执行SQ的对象
代码语言:javascript复制
String sql = "select * from users"; //编写sql

statement.executeQuery(); //查询操作返回ResultSet
statement.execute();      //执行任何SQL
statement.executeUpdate();//跟新,插入,删除,返回受影响的行数
11.4.4 Result 查询的结果集:封装了所有的查询结果

获得指定类型的数据

代码语言:javascript复制
//在不知道列类型的清空下使用
resultSet.getObject();
//如果知道列的类型就使用指定类型
resultSet.getInt();
resultSet.getFloat();
resultSet.getDate();

遍历,指针

代码语言:javascript复制
resultSet.beforeFirst(); //移动到最前面
resultSet.afterLast(); //移动到最后面
resultSet.next(); //移动到下一个数据
resultSet.previous();//移动到前一行
resultSet.absolute(row); //移动到指定行
11.4.5 释放资源
代码语言:javascript复制
resultSet.close();
statement.close();
//preparedStatement.close()
connection.close();

11.5 Statement对象CRUD

11.5.1db.properties
代码语言:javascript复制
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/kuangshen_school?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=GMT+8
username=root
password=root
11.5.2 JdbcUtil工具类
代码语言:javascript复制
package com.huayu.lesson02.utils;

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

public class JdbcUtil {

    private static String driver = null;
    private static String url = null;
    private static String username = null;
    private static String password = null;

    static{
        try {
            InputStream in = JdbcUtil.class.getClassLoader().getResourceAsStream("db.properties");
            Properties properties = new Properties();
            properties.load(in);

            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");

            //1.驱动只加载一次
            Class.forName(driver);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获取连接
    public static Connection getConnection() throws SQLException{
        return DriverManager.getConnection(url, username, password);
    }

    //释放资源
    public static void release(Connection conn, Statement st, ResultSet rs){
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException sqlException) {
                sqlException.printStackTrace();
            }
        }
        if(st != null){
            try {
                st.close();
            } catch (SQLException sqlException) {
                sqlException.printStackTrace();
            }
        }
        if (rs != null){
            try {
                rs.close();
            } catch (SQLException sqlException) {
                sqlException.printStackTrace();
            }
        }

    }


}
11.5.3 CRUD操作-create

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

代码语言:javascript复制
package com.huayu.lesson02.utils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class TestInstert {
    public static void main(String[] args) {

        Connection conn = null;
        Statement st= null;
        ResultSet rs = null;

        try {
            conn = JdbcUtil.getConnection(); //获取数据库连接
            st = conn.createStatement();
            String sql = "insert into users (id,`name`,`password`,email,birthday)"  
                    "values (5,'huauyu','123456','123456@qq.com','2000-01-01');";

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

        } catch (SQLException sqlException) {
            sqlException.printStackTrace();
        }finally {
            JdbcUtil.release(conn,st,rs);
        }
    }
}
11.5.4 CRUD操作-delete

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

代码语言:javascript复制
//其他代码一样
String sql = "delete from users where id = 4";

int i = st.executeUpdate(sql);
if(i>0){
    System.out.println("删除成功!");
}
11.5.5 CRUD操作-update

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

代码语言:javascript复制
//其他代码一样
 String sql = "update users set `name` = 'huayu',email = '12345@qq.com' where id = 2";

int i = st.executeUpdate(sql);
if(i>0){
    System.out.println("修改成功!");
}
11.5.6 CRUD操作- read

使用executeQuery(String sql)方法完成数据查询操作,示例操作:

代码语言:javascript复制
//其他代码一样
 String sql = "select * from users where id = 1";

rs = st.executeQuery(sql);
while (rs.next()){
    System.out.println(rs.getString("name"));
}

11.6 Statement SQL 注入的问题

sql存在漏洞,sql存在拼接sql的漏洞;

代码语言:javascript复制
package com.huayu.lesson02.utils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class SQL注入 {
    public static void main(String[] args) {

        //正常登录
//        login("huayu","123456");

        //sql注入
        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 = JdbcUtil.getConnection();
            st = conn.createStatement();

            //select * from users where `name` = 'huayu' 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.print("name:" rs.getString("name"));
                System.out.println("tpassword:" rs.getString("password"));
            }

        } catch (SQLException sqlException) {
            sqlException.printStackTrace();
        }finally {
            JdbcUtil.release(conn,st,rs);
        }
    }

}

11.7 PreparedStatement 对象 CRUD

11.7.1 PreparedStatement 对象简介

PreparedStatement 对象,可以防止SQL注入,效率更高;

PreparedStatement是Statement的子类;

PreparedStatement源码

代码语言:javascript复制
// PreparedStatement 是 Statement 的子类
public interface PreparedStatement extends Statement
11.7.2 create
代码语言:javascript复制
package com.huayu.lesson03;

import com.huayu.lesson02.utils.JdbcUtil;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Date;

public class TestInsert {
    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement pst = null;

        try {
            conn = JdbcUtil.getConnection();

            //区别
            //使用问号? 占用符代替参数
            String sql = "insert into users (id,`name`,`password`,email,birthday) values (?,?,?,?,?);";

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

            //手动设置参数

            pst.setInt(1,4);
            pst.setString(2,"xiaoyu");
            pst.setString(3,"123456");
            pst.setString(4,"123@qq.com");
            //注意点:sql.Date  数据库 java.sql.Date()
            //       util.Date  java new Date().getTime()  获得时间戳
            pst.setDate(5,new java.sql.Date(new Date().getTime()));

            int i = pst.executeUpdate();
            if(i > 0){
                System.out.println("插入成功!");
            }

        } catch (SQLException sqlException) {
            sqlException.printStackTrace();
        }finally {
            JdbcUtil.release(conn,pst,null);
        }


    }
}
11.7.3 delete
代码语言:javascript复制
//其他代码一样
String sql = "delete from users where id = ?";

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

//手动设置参数
pst.setInt(1,4);

int i = pst.executeUpdate();
if(i > 0){
    System.out.println("删除成功!");
}
11.7.4 update
代码语言:javascript复制
//其他代码一样
 String sql = "update users set `name` = ?  where id = ? ;";

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

//手动设置参数

pst.setString(1,"huayu");
pst.setInt(2,1);

int i = pst.executeUpdate();
if(i > 0){
    System.out.println("更新入成功!");
}
11.7.5 read
代码语言:javascript复制
String sql = "select * from users where id = ?";
pst = conn.prepareStatement(sql); //预编译SQL,先些sql,然后不执行

//手动设置参数
pst.setInt(1,1);

rs = pst.executeQuery();

while (rs.next()){
    System.out.println(rs.getString("name"));
}

11.8 PrepareStatement 对象SQL 不会被注入

代码语言:javascript复制
package com.huayu.lesson03;

import com.huayu.lesson02.utils.JdbcUtil;

import java.sql.*;

public class SQL注入 {
    public static void main(String[] args) {

        //正常登录
//        login("huayu","123456");

        //sql注入
        login(" 'or ' 1=1 "," 'or ' 1=1 ");

    }

    //登录业务
    public static  void login(String username,String password){
        Connection conn = null;
        PreparedStatement pst = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtil.getConnection();

            //prepareStatement 防止SQL注入的本质,把传递进来的参数当作字符
            //假设其中存在转移字符,单引号 ' 会被直接转义
            String sql = "select * from users where name = ? and password = ?";
            pst = conn.prepareStatement(sql);

            //手动设置参数
            pst.setString(1,username);
            pst.setString(2,password);

            rs = pst.executeQuery();

            while (rs.next()){
                System.out.println(rs.getString("name"));
            }

        } catch (SQLException sqlException) {
            sqlException.printStackTrace();
        }finally {
            JdbcUtil.release(conn,pst,rs);
        }

    }
}

11.9 事务

要么都成功,要么都失败;

11.9.1ACID原则

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

一致性:总数不变

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

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

隔离性的问题:

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

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

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

11.9.2 代码实现

步骤:

  1. 开启事务 conn.setAutoCommit(false); 关闭自动提交的同时会开启事务;
  2. 一组事务执行完毕,提交事务
  3. 可以在catch语句中显示的定义 回滚语句,但默认失败就会回滚

事务案例

代码语言:javascript复制
package com.huayu.lesson04;

import com.huayu.lesson02.utils.JdbcUtil;

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 pst = null;
        ResultSet rs = null;

        try {
            conn = JdbcUtil.getConnection();

            //关闭数据库的自动提交,自动开启事务
            conn.setAutoCommit(false);

            String sql1 = "update account set money = money-100 where name= 'A';";
            pst =  conn.prepareStatement(sql1);
            pst.executeUpdate();

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

            //模拟错误
            //int x = 1/0;

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


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

            } catch (SQLException throwables) {
                throwables.printStackTrace();
            }
            sqlException.printStackTrace();
        }finally {
            JdbcUtil.release(conn,pst,rs);
        }
    }
}

11.10 数据库连接池

11.10.1 池化技术

数据库连接 -- 执行完毕 -- 释放;

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

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

最小连接数,最大连接数,等待超时;

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

11.10.2 开源数据源实现
  • DBCP
  • C3P0
  • Druid:阿里巴巴

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

10.10.3 DBCP

使用的jar包

Commons-dbcp-1.4.jar

Commons-pool-1.6.jar

  • 在src目录下加入dbcp的配置文件:dbcpconfig.properties
代码语言:javascript复制
#连接设置
driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/jdbcStudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=root

#<!-- 初始化连接 -->
initialSize=10

#最大连接数量
maxActive=50

#<!-- 最大空闲连接 -->
maxIdle=20

#<!-- 最小空闲连接 -->
minIdle=5

#<!-- 超时等待时间以毫秒为单位 6000毫秒/1000等于60秒 -->
maxWait=60000

#JDBC驱动建立连接时附带的连接属性属性的格式必须为这样:[属性名=property;]
#注意:"user" 与 "password" 两个属性会被明确地传递,因此这里不需要包含他们。
connectionProperties=useUnicode=true;characterEncoding=UTF8

#指定由连接池所创建的连接的自动提交(auto-commit)状态。
defaultAutoCommit=true

#driver default 指定由连接池所创建的连接的只读(read-only)状态。
#如果没有设置该值,则“setReadOnly”方法将不被调用。(某些驱动并不支持只读模式,如:Informix)
defaultReadOnly=

#driver default 指定由连接池所创建的连接的事务级别(TransactionIsolation)。
#可用值为下列之一:(详情可见javadoc。)NONE,READ_UNCOMMITTED, READ_COMMITTED,REPEATABLE_READ, SERIALIZABLE
defaultTransactionIsolation=READ_UNCOMMITTED

工具类

代码语言:javascript复制
import org.apache.commons.dbcp.BasicDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUtil_DBCP {

    private static DataSource dataSource = null;

    static{
        try {
            InputStream in = JdbcUtil_DBCP.class.getClassLoader().getResourceAsStream("dbcpconfig.properties");
            Properties properties = new Properties();
            properties.load(in);

            //创建数据源 工厂模式 -- >创建
            dataSource = BasicDataSourceFactory.createDataSource(properties);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获取连接
    public static Connection getConnection() throws SQLException{
        return dataSource.getConnection();
    }

    //释放资源
    public static void release(Connection conn, Statement st, ResultSet rs){
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException sqlException) {
                sqlException.printStackTrace();
            }
        }
        if(st != null){
            try {
                st.close();
            } catch (SQLException sqlException) {
                sqlException.printStackTrace();
            }
        }
        if (rs != null){
            try {
                rs.close();
            } catch (SQLException sqlException) {
                sqlException.printStackTrace();
            }
        }

    }


}

测试案例

代码语言:javascript复制
//只需要将获取连接换一下就好
conn = JdbcUtil_DBCP.getConnection();
11.10.4 C3P0
  • C3P0是一个开源的JDBC连接池,它实现了数据源和JNDI绑定,支持JDBC3规范和JDBC2的标准扩展。目前使用它的开源项目有Hibernate,Spring等。
  • c3p0与dbcp区别:
    • dbcp没有自动回收空闲连接的功能;
    • c3p0有自动回收空闲连接功能。
  • 要使用C3P0数据源,需要导入如下两个 jar 文件:
    • c3p0-0.9.5.5.jar
    • mchange-commons-java-0.2.19.jar
  • 在src目录下加入C3P0的配置文件:c3p0-config.xml
代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
    <!--
    C3P0的缺省(默认)配置,
    如果在代码中“ComboPooledDataSource ds = new ComboPooledDataSource();”这样写
    就表示使用的是C3P0的缺省(默认)配置信息来创建数据源
    -->
    <default-config>
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcStudy? useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true</property>
        <property name="user">root</property>
        <property name="password">root</property>
        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </default-config>
    <!--
    C3P0的命名配置,
    如果在代码中“ComboPooledDataSource ds = new
    ComboPooledDataSource("MySQL");”这样写就表示使用的是name是MySQL的配置
    信息来创建数据源
    -->
    <named-config name="MySQL">
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcStudy?useUnicode=true&amp;characterEncoding=utf8&amp;useSSL=true</property>
        <property name="user">root</property>
        <property name="password">root</property>
        <property name="acquireIncrement">5</property>
        <property name="initialPoolSize">10</property>
        <property name="minPoolSize">5</property>
        <property name="maxPoolSize">20</property>
    </named-config>
</c3p0-config>

工具类

代码语言:javascript复制
import com.mchange.v2.c3p0.ComboPooledDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class JdbcUtil_C3P0 {

    private static ComboPooledDataSource dataSource = null;

    static{
        try {
            //代码版配置
//            dataSource = new ComboPooledDataSource();
//            dataSource.setDriverClass();
//            dataSource.setUser();
//            dataSource.setPassword();
//            dataSource.setJdbcUrl();
//
//            dataSource.setMaxPoolSize();
//            dataSource.setMinPoolSize();

            //创建数据源 工厂模式 -- >创建
            dataSource = new ComboPooledDataSource("MySQL"); //配置文件写入

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获取连接
    public static Connection getConnection() throws SQLException{
        return dataSource.getConnection();
    }

    //释放资源
    public static void release(Connection conn, Statement st, ResultSet rs){
        if(conn != null){
            try {
                conn.close();
            } catch (SQLException sqlException) {
                sqlException.printStackTrace();
            }
        }
        if(st != null){
            try {
                st.close();
            } catch (SQLException sqlException) {
                sqlException.printStackTrace();
            }
        }
        if (rs != null){
            try {
                rs.close();
            } catch (SQLException sqlException) {
                sqlException.printStackTrace();
            }
        }

    }

}

测试

代码语言:javascript复制
//获取连接
conn = JdbcUtil_C3P0.getConnection();  //原来是自己实现的,现在用别人实现的

结论:无论使用什么数据源,本质还是一样的,DataSource接口不会变,方法就不会变;

笔记视频大半部分来源->狂神说Java】MySQL最新教程通俗易懂

笔记部分参考->狂神说笔记——MySQL快速入门12

0 人点赞