乐趣区

关于sql:MySQL个人学习笔记

MySQL

数据库概述

什么是数据库?

所谓的数据库就是指存储和治理数据的仓库

扩大内容 1:数据库有哪些分类?(理解)

晚期: 档次式数据库、网络型数据库
当初:关系型数据库、非关系型数据库

什么是关系型数据库?

底层以二维表的模式保留数据的库就是关系型数据库

扩大内容 2:常见的关系型数据库有哪些?(理解)

  • Sql Server:微软提供,免费,实用于一些中型或大型的我的项目中,在 java 中的应用占比不高(.NET 中应用的较多)
  • Oracle:甲骨文公司提供,免费,实用于一些大型或者超大型的我的项目中,在 java 中的应用占比十分高
  • mysql:瑞典 MySQLAB 公司提供,收费开源,实用于一些小型或者中型的我的项目中,在 Java 中的应用占比拟高(玲珑轻量)mariadb 其实就是 MySQL 的一个分支,用法和 MySQL 齐全一样。
  • DB2:IBM 公司提供,免费,在一些银行、金融等行业中应用较多。在 java 中的应用占比也不高。
  • Sqlite:迷你数据库,嵌入式设施中(安卓、苹果手机、pad)

数据库相干概念

1、什么是数据库服务器

数据库服务器就是一个软件(比方 mysql 软件)将数据库软件装置在电脑上,以后电脑就是一个数据库服务器。就能够对外提供存取数据的服务

在一个数据库服务器中能够创立多个数据库(dataBases),每一个数据库都是一个独自的仓库。

2、什么是数据库

数据库就是存储和治理数据的仓库,通常状况下,一个网站的中的所有数据会寄存在一个数据库中。例如:

jd.com db_jd(数据库)
taobao.com db_taobao(数据库)

3、什么是表

一个数据库中能够创立多张表,每张表用于存储一类信息(数据库),例如:

jd.com 中的用户数据 tb_user(表)

jd.com 中的商品数据 tb_product(表)

jd.com 中的订单数据 tb_order(表)

4、什么表记录

一张表中能够蕴含多行表记录,每一行表记录用于存储某一个具体的数据

学生编号

姓名

年龄

1001

刘沛霞

35

1002

陈子枢

18

。。。

。。。

。。。

什么是 SQL 语言?

SQL 是一门用于操作关系型数据库的通用的语言(应用 SQL 能够操作所有的关系型数据库)

应用 SQL 能够操作数据库、表、表记录

(1)创立数据库、删除数据库、批改数据库、查询数据库

(2)创立表、删除表、批改表、查问表

(3)新增表记录、删除表记录、批改表记录、查问表记录

应用 SQL 也能够操作存储过程 / 视图 / 索引等。

提醒:SQL 是一个规范通用的操作关系型数据库的语言(普通话),每个数据库厂商为了加强本人数据库的性能,都提供了反对本人数据库的语言,称之为数据库的方言。方言不通用!

连贯 mysql 服务器

通过命令行工具能够登录 MySQL 客户端,连贯 MySQL 服务器,从而拜访服务器中的数据。

1、连贯 mysql 服务器:

mysql -uroot - p 明码

-u:前面的 root 是用户名,这里应用的是超级管理员 root;

-p:(小写的 p)前面的 root 是明码,这是在装置 MySQL 时就曾经指定的明码;

2、连贯 mysql 服务器并指定 IP 和端口:

mysql -uroot -proot -h127.0.0.1 -P3306

-h:前面给出的 127.0.0.1 是服务器主机名或 ip 地址,能够省略的,默认连贯本机;

-P:(大写的 P)前面的 3306 是连贯端口,能够省略,默认连贯 3306 端口;

3、退出客户端命令:quit 或 exit 或 q

4、FAQ:常见问题:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetse23a06d3921d5d68c48d6e51024aae8a.jpg?lastModify=1616081589)

解决办法:复制 mysql 装置目录下的 bin 目录的门路,将 bin 目录的门路增加到 path 环境变量中!!

能够在 cmd 中通过 echo %path% 查看 path 环境变量的值。

扩大内容 3:

(1)在 cmd 中连贯 mysql 服务器之后,能够应用 #、/**/、– 等符号增加正文,例如:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets7765164a8afaa8d26d9b31ff3356bc33.png?lastModify=1616081589)

(2)在 cmd 中连贯 mysql 服务器之后,在书写 SQL 语句时,能够通过 c 勾销以后语句的执行。例如:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets4c6edbd263d6c870ac745e34d4b207d0.png?lastModify=1616081589)

数据库及表操作

创立、删除、查看数据库

提醒:(1)SQL 语句对大小写不敏感。举荐关键字应用大写,自定义的名称(库名,表名,列名、别名等)应用小写。

SHOW DATABASES; — 查看以后数据库服务器中的所有库
CREATE DATABASE mydb1; — 创立 mydb1 库

(2)并且在自定义名称时,针对多个单词不要应用驼峰命名,而是应用下划线连贯。(例如:tab_name,而不是 tabName)

— 01. 查看 mysql 服务器中所有数据库

show databases; — 查看所有库

show datebases; — 谬误写法,是 data,不是 date

— 02. 进入某一数据库(进入数据库后,能力操作库中的表和表记录)

— 语法:USE 库名;

use test; — 进入 test 数据库
show tables; — 查看 test 库中的所有表

— 查看已进入的库(理解)

select database();

— 03. 查看以后数据库中的所有表

show tables; — 查看 test 库中的所有表

— 04. 删除 mydb1 库

— 语法:DROP DATABASE 库名;

drop database mydb1; — 删除不存在的库,这种写法会报错!

-- 思考:当删除的库不存在时,如何防止谬误产生?

drop database if exists mydb1; — 如果存在 mydb1,则删除;

— 05. 从新创立 mydb1 库,指定编码为 utf8

— 语法:CREATE DATABASE 库名 CHARSET 编码;

— 须要留神的是,mysql 中不反对横杠(-),所以 utf- 8 要写成 utf8;

create database mydb1 charset utf8;
— 建库时,最好指定编码,如果不指定,这个库的编码有可能是 latin1

— 如果不存在则创立 mydb1;

create database if not exists mydb1 charset utf8;

— 06. 查看建库时的语句(理解, 并验证数据库库应用的编码)

— 语法:SHOW CREATE DATABASE 库名;

show create database mydb1;

创立、删除、查看表

— 07. 进入 mydb1 库,删除 stu 学生表(如果存在)

— 语法:DROP TABLE 表名;

use mydb1;
drop table if exists stu; — 如果存在则删除

— 08. 创立 stu 学生表(编号[数值类型]、姓名、性别、出生年月、考试成绩[浮点型]),建表的语法:

CREATE TABLE 表名(
列名 数据类型,
列名 数据类型,

列名 数据类型

);

SQL 语句:

create database if not exists mydb1;
use mydb1;
drop table if exists stu;
create table stu(

id int primary key auto_increment,
name varchar(50), -- 50 示意 name 这一列,最多能存 50 个字符
gender varchar(10),
birthday date,
score double

);

— 09. 查看 stu 学生表构造

— 语法:desc 表名

desc stu;

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsimage-20201030000618981.png?lastModify=1616081589)

新增、批改、删除表记录

— 10. 往学生表 (stu) 中插入记录(数据)

— 语法:INSERT INTO 表名(列名 1, 列名 2, 列名 3 …) VALUES(值 1, 值 2, 值 3 …);

insert into stu(id,name,gender,birthday,score) value(null,’tom’,’male’,’2000-1-1′,85);
— 如果插入的有中文数据,在 cmd 中,先 set names gbk; 再插入,能够避免中文乱码(或者中文数据插入失败)
insert into stu value(null,’ 孙尚香 ’,’female’,’2001-2-3′,90);
insert into stu value(null,’ 后嗣 ’,’male’,’2002-3-4′,78);

提醒:

(1)当为所有列插入值时,能够省写列名,但值的个数和程序必须和申明时列的个数和程序保持一致!
(2)SQL 语句中的值为字符串或日期时,值的两边要加上单引号(有的版本的数据库双引号也能够,但举荐应用单引号)。
(3)(针对 cmd 窗口)在插入数据之前,先设置编码:set names gbk;
或者用以下命令连贯 mysql 服务器:
mysql –default-character-set=gbk -uroot -proot
等价于:
mysql -uroot -proot
set names gbk;

— 11. 查问 stu 表所有学生的信息

— 语法:SELECT 列名 | * FROM 表名

select from stu; — 号是通配符,示意查问所有列

— 12. 批改 stu 表中所有学生的问题,加 10 分专长分

— 批改语法: UPDATE 表名 SET 列 = 值, 列 = 值, 列 = 值 …[WHERE 子句];

update stu set score=score+10;
— score+=10 谬误写法,mysql 不反对 +=

— 13. 批改 stu 表中编号为 1 的学生问题,将问题改为 83 分。

update stu set score=83 where id=1;

提醒:where 子句用于对记录进行筛选过滤,保留符合条件的记录,将不符合条件的记录剔除。

— 14. 删除 stu 表中所有的记录

— 删除记录语法: DELETE FROM 表名 [where 子句]

delete from stu; — 删除所有记录

— 仅删除符合条件的(例如,删除 id 大于 1 的记录)

delete from stu where id>1;

查问表记录

— 筹备数据:以下练习将应用 db10 库中的表及表记录,请先进入 db10 数据库!!!

根底查问

SELECT 语句用于从表中选取数据。后果被存储在一个后果表中(称为后果集)。

语法:SELECT 列名称 | * FROM 表名

提醒:(1) *(星号)为通配符,示意查问所有列。

(2)但应用 *(星号)有时会把不必要的列也查出来了,并且效率不如间接指定列名

— 15. 查问 emp 表中的所有员工,显示姓名,薪资,奖金

select from emp; — 示意查问 emp 表中的所有列
select name,sal,bonus from emp; — 查问指定的列

— 16. 查问 emp 表中的所有部门和职位

select dept,job from emp;

思考:如果查问的后果中,存在大量反复的记录,如何剔除重复记录,只保留一条?

— 在 select 之后、列名之前,应用 DISTINCT 剔除反复的记录

select distinct dept,job from emp;

WHERE 子句查问

WHERE 子句查问语法:SELECT 列名称 | * FROM 表名称 WHERE 列 运算符 值

WHERE 子句前面跟的是条件,条件能够有多个,多个条件之间用连接词(or | and)进行连贯

上面的运算符可在 WHERE 子句中应用:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets7b2b5598fb3af3f96d552b5d9779d892.png?lastModify=1616081589)

— 17. 查问 emp 表中【薪资大于 3000】的所有员工,显示员工姓名、薪资

select name, sal from emp where sal>3000;

— 18. 查问 emp 表中【总薪资 (薪资 + 奖金) 大于 3500】的所有员工,显示员工姓名、总薪资

select name, sal+bonus from emp
where sal+bonus > 3500;

— ifnull(列名, 值)函数: 判断指定的列是否蕴含 null 值,如果有 null 值,用第二个值替换 null 值

select name, sal+ifnull(bonus,0) from emp
where sal+ifnull(bonus,0) > 3500;

— 留神查看下面查问后果中的表头,如何将表头中的 sal+bonus 批改为 “ 总薪资 ”

— 应用 as 能够为表头指定别名(格局:列名 as 别名)

select name as 姓名, sal+ifnull(bonus,0) as 总薪资 from emp
where sal+ifnull(bonus,0) > 3500;

— 另外 as 能够省略

select name 姓名, sal+ifnull(bonus,0) 总薪资 from emp
where sal+ifnull(bonus,0) > 3500;

— 19. 查问 emp 表中【薪资在 3000 和 4500 之间】的员工,显示员工姓名和薪资

select name,sal from emp
where sal>=3000 and sal<=4500;
— and:必须同时满足 and 两边的条件

— 提醒: between…and… 在 … 和 … 之间

select name,sal from emp
where sal between 3000 and 4500;

— 20. 查问 emp 表中【薪资为 1400、1600、1800】的员工,显示员工姓名和薪资

select name,sal from emp
where sal=1400 or sal=1600 or sal=1800;
— or: 或,只有满足 or 两边中的任何一个条件即可!

— 或者应用 in

select name,sal from emp
where sal in(1400,1600,1800);
— in:只有 sal 是 in 前面括号中的任何一个值,就算符合条件!

— 21. 查问薪资不为 1400、1600、1800 的员工,显示员工姓名和薪资

select name,sal from emp
where not(sal=1400 or sal=1600 or sal=1800);
— —————————————
select name,sal from emp
where sal not in(1400,1600,1800);
— not 示意对条件进行取反

— 22.(本人实现) 查问 emp 表中【薪资大于 4000 和薪资小于 2000】的员工,显示员工姓名、薪资。

select name,sal from emp
where sal>4000 or sal<2000;

— 23.(本人实现) 查问 emp 表中薪资大于 3000 并且奖金小于 600 的员工,显示员工姓名、薪资、奖金。

select name,sal,bonus from emp
where sal>3000 and bonus<600;

— 解决 null 值

select name,sal,bonus from emp
where sal>3000 and ifnull(bonus,0)<600;

— 24. 查问没有部门的员工(即部门列为 null 值)

select * from emp
where dept is null; — 查问 dept 列为 null 的

— 思考:如何查问有部门的员工(即部门列不为 null 值)

select * from emp
where not(dept is null); — 查问 dept 列不为 null 的
— 或
select * from emp
where dept is not null; — 查问 dept 列不为 null 的

含糊查问

LIKE 操作符用于在 WHERE 子句中搜寻列中的指定模式。

能够和通配符(%、_)配合应用,其中"%" 示意 0 或多个任意的字符,"_" 示意一个任意的字符

语法:SELECT 列 | * FROM 表名 WHERE 列名 LIKE 值

示例:

— 25. 查问 emp 表中姓名中以 ” 刘 ” 字结尾的员工,显示员工姓名。

select name from emp
where name like ‘ 刘 %’;
— % 示意匹配 0 个、1 个或多个任意字符

— 26. 查问 emp 表中姓名中蕴含 ” 涛 ” 字的员工,显示员工姓名。

select name from emp
where name like ‘% 涛 %’;
— % 涛 %, 示意匹配某一列蕴含涛的记录,’ 涛 ’ 可能在结尾、结尾或两头某一个地位

— 27. 查问 emp 表中姓名以 ” 刘 ” 结尾,并且姓名为两个字的员工,显示员工姓名。

— 以 ’ 刘 ’ 结尾,姓名为两个字的员工
select name from emp
where name like ‘ 刘_’; — _(下划线) 示意任意一个字符
— 以 ’ 刘 ’ 结尾,姓名为三个字的员工
select name from emp
where name like ‘ 刘__’;

多行函数查问

多行函数也叫做聚合 (汇集) 函数,依据某一列或所有列进行统计。

常见的多行函数有:

多行函数

作用

COUNT(列名 | *)

统计后果集中指定列的记录的行数。

MAX(列名)

统计后果集中某一列值中的最大值

MIN(列名)

统计后果集中某一列值中的最小值

SUM(列名)

统计后果集中某一列所有值的和

AVG(列名)

统计后果集中某一列值的平均值

提醒:(1)多行函数不能用在 where 子句中

(2)多行函数和是否分组无关,分组与否会间接影响多行函数的执行后果。

(3)多行函数在统计时会对 null 值进行过滤,间接将 null 值抛弃,不参加统计。

— 28. 统计 emp 表中薪资大于 3000 的员工个数

— 查问薪资大于 3000 的员工有哪些?
select * from emp where sal>3000;
— count: 用于统计行数
select count(*) from emp where sal>3000; — 7
select count(id) from emp where sal>3000; — 7
select count(bonus) from emp where sal>3000; — 6
— 因为 bonus 中有 null 值,在统计时,null 值间接别抛弃,不参加统计

— 29. 求 emp 表中的最高薪资

— emp 表中的最高薪资(max 函数)
select max(sal) from emp;
— emp 表中的最低薪资(min 函数)
select min(sal) from emp;

— 求 emp 表中薪资最高的员工
select name,max(sal) from emp;
— 下面的后果,max(sal) 是求薪资中的最大值,而 name 只是 name 列中的第一个姓名,因而 name 和 max(sal)很可能是不对应的。因而后果是错的!

— 30. 统计 emp 表中所有员工的薪资总和(不蕴含奖金)

select sum(sal) from emp; — 求薪资这一列所有值的和
select sum(bonus) from emp; — 求奖金这一列所有值的和

— 31. 统计 emp 表员工的均匀薪资(不蕴含奖金)

select avg(sal) from emp; — 薪资平均值(总薪资 /12)
select avg(bonus) from emp; — 奖金平均值(总奖金 /11), 有 null 值

多行函数须要留神的问题:

  • 多行函数和是否分组无关,如果查问后果中的数据没有通过分组,默认整个查问后果是一个组,多行函数就会默认统计以后这一个组的数据。产生的后果只有一个。
  • 如果查问后果中的数据通过分组(分的组不止一个),多行函数会依据分的组进行统计,有多少个组,就会统计出多少个后果。

例如:统计 emp 表中的人数

— 如果没有分组,默认整个查问后果是一个组
select count(*) from emp; — 12

后果返回的就是 emp 表中的所有人数

再例如:依据性别对所有员工进行分组,再统计每组的人数,显示性别和对应人数

— 依据性别分组,最终分为两个组,统计两个组的人数(10、2)
select gender,count(*) from emp group by gender;

分组查问

GROUP BY 语句依据一个或多个列对后果集进行分组。

在分组的列上咱们能够应用 COUNT,SUM,AVG,MAX,MIN 等函数。

语法:SELECT 列 | * FROM 表名 [WHERE 子句] GROUP BY 列;

— 32. 对 emp 表,依照部门对员工进行分组,查看分组后成果。

— 依照部门分组(3 个组)
select * from emp group by dept;
— 对下面 3 个组的人数进行统计
select dept,count(*) from emp group by dept;

— 33. 对 emp 表依照职位进行分组,并统计每个职位的人数,显示职位和对应人数

— 依照职位分组(3 个组)
select * from emp group by job;
— 对下面 3 个组的人数进行统计
select job,count(*) from emp group by job;

— 34. 对 emp 表依照部门进行分组,求每个部门的最高薪资(不蕴含奖金),显示部门名称和最高薪资

— 依据部门进行分组(3 个组)
select * from emp group by dept;
— 求每个部门(每个组)的最高薪资
select dept,max(sal) from emp group by dept;

排序查问

应用 ORDER BY 子句将后果集中记录依据指定的列排序后再返回

语法:SELECT 列名 FROM 表名 ORDER BY 列名 [ASC|DESC]

ASC(默认)升序,即从低到高;DESC 降序,即从高到低。

— 35. 对所有员工的薪资进行升序 (从低到高) 排序,显示员工姓名、薪资。

— 依照薪资进行排序,默认是升序(asc),即从低到高
select name,sal from emp order by sal;
select name,sal from emp order by sal asc;

— 36. 对 emp 表中所有员工的奖金进行降序 (从高到低) 排序,显示员工姓名、奖金。

— 依照奖金进行降序排序
select name,bonus from emp order by bonus desc;

分页查问

在 mysql 中,通过 limit 进行分页查问,查问公式为:

limit (页码 -1)* 每页显示记录数, 每页显示记录数

— 37. 查问 emp 表中的所有记录,分页显示:每页显示 3 条记录,返回所有页的数据。

— 分页查问:每页显示 3 条,返回第 1 页数据:
select * from emp limit 0, 3;
— 分页查问:每页显示 3 条,返回第 2 页数据:
select * from emp limit 3, 3;
— 分页查问:每页显示 3 条,返回第 3 页数据:
select * from emp limit 6, 3;
— 分页查问:每页显示 3 条,返回第 4 页数据:
select * from emp limit 9, 3;

— 38. 求 emp 表中薪资最高的前 3 名员工的信息,显示姓名和薪资

— 依照薪资对员工信息进行排序,降序排序
select name,sal from emp order by sal desc;
— 在下面查问的根底上,分页查问,每页显示 3 条,查问第一页
select name,sal from emp order by sal desc limit 0, 3;

其余函数

函数名

解释阐明

curdate()

获取以后日期,格局是:年月日

curtime()

获取以后工夫,格局是:时分秒

sysdate()/now()

获取以后日期 + 工夫,格局是:年月日 时分秒

year(date)

返回 date 中的年份

month(date)

返回 date 中的月份

day(date)

返回 date 中的天数

hour(date)

返回 date 中的小时

minute(date)

返回 date 中的分钟

second(date)

返回 date 中的秒

CONCAT(s1,s2..)

将 s1,s2 等多个字符串合并为一个字符串

CONCAT_WS(x,s1,s2..)

同 CONCAT(s1,s2,..)函数,然而每个字符串之间要加上 x,x 是分隔符

— 39. 查问 emp 表中所有【在 1993 和 1995 年之间出世】的员工,显示姓名、出生日期。

select name,birthday from emp
where birthday between 1993 and 1995;
— birthday(日期) 和 1993、1995 不能比拟,因为类型不同

— 解决方案 1:将 1993、1995 转换成日期后,再和 birthday 进行比拟
select name,birthday from emp
where birthday between ‘1993-1-1’ and ‘1995-12-31’;

— 解决方案 2:将 birthday 中的年份取出,和 1993、1995 进行比拟
select name,birthday from emp
where year(birthday) between 1993 and 1995;

— 40. 查问 emp 表中本月过生日的所有员工

— 求本月过生日的员工
select * from emp
where month(now())=month(birthday);
— 求下个月过生日的员工
select * from emp
where month(now())+1=month(birthday); — 问题:如果以后是 12 月份

select * from emp
where (month(now())+1 )%12=month(birthday); — 问题:如果以后是 11 月份
— 最终版本:求下个月过生日的员工
select * from emp
where (month(now())+1 )%12=month(birthday)%12;

— 41. 查问 emp 表中员工的姓名和薪资(薪资格局为: xxx(元)、xxx/ 元)

select name,concat(sal,'(元)’) from emp;
select name,concat(sal,’/ 元 ’) from emp;
select name,concat_ws(‘/’,sal,’ 元 ’) from emp;

mysql 的数据类型

MySQL 反对多种类型,大抵能够分为三类:数值、字符串 (字符) 类型和日期 / 工夫。

数值类型

MySQL 中反对多种整型,其实很大水平上是雷同的,只是存储值的大小范畴不同而已,上面的表显示了罕用的数值类型的存储和范畴。

类型

大小

用处

tinyint

1 byte(绝对于 java 中的 byte)

小整数值

smallint

2 bytes(绝对于 java 中的 short)

大整数值

int

4 bytes(绝对于 java 中的 int)

大整数值

bigint

8 bytes(绝对于 java 中的 long)

极大整数值

float

4 bytes(绝对于 java 中的 float)

单精度浮点数值

double

8 bytes(绝对于 java 中的 double)

双精度浮点数值

decimal

存储准确的小数值

decimal 数据类型用于在数据库中存储准确的数值

decimal 用法:column_name  decimal(P,D);
1)P 是示意无效数的精度。P 范畴为 1〜65。
2)D 是示意小数点后的位数。D 的范畴是 0~30。MySQL 要求 D 小于或等于(<=)P
例如:amount DECIMAL(6,2); 示意 amount 列最多能够存储 6 位数字,小数位数为 2 位,因而,amount 列的范畴是从 -9999.99 到 9999.99

字符串类型

mysql 中的字符串类型有很多,例如:CHAR、VARCHAR、TEXT、BLOB、MEDIUMTEXT、MEDIUMBLOB、LONGTEXT、LONGBLOB 等

上面说几个较为常见的字符串类型:

类型

大小

用处

char

0~255(字符)

定长字符串

varchar

0~65535(字节)

变长字符串

mediumtext

0~16 777 215(字节, 约 16M)

大文本 / 长文本

mediumblob

0~16 777 215(字节, 约 16M)

二进制

longtext

0-4 294 967 295(字节, 约 4G)

极大文本

longblob

0-4 294 967 295(字节, 约 4G)

极大二进制

1、char(n) 定长字符串,最长 255 个字符。n 示意字符数,例如:

— 创立 user 表,指定用户名为 char 类型,字符长度不超过 10 个字符

create table user(
  username char(10),
  …
);

所谓的定长,是当插入的数据的长度小于指定的长度时,残余的空间会用空格填充。(这样会节约空间)

char 类型往往用于存储长度固定的数据,这样不会节约空间,并且在存储数据的效率上比 varchar 类型略高一些。

2、varchar(n) 变长字符串,最长不超过 65535 个字节,n 示意字符数,个别超过 255 个字符,会应用 text 类型,例如:

iso8859- 1 码表:一个字符占用 1 个字节,1*n < 65535,n 最多等于 65535
utf8 码表:一个中文汉字占用 3 个字节,3*n < 65535,n 最多等于 65535/3
GBK 码表:一个中文汉字占用 2 个字节,2*n < 65535,n 最多等于 65535/2

— 创立 user 表,指定用户名为 varchar 类型,长度不超过 10 个字符

create table user(
username varchar(10)
);

所谓的不定长,是当插入的数据的长度小于指定的长度时,残余的空间能够留给别的数据应用。(节俭空间)

总结:长度固定的数据,用 char 类型,这样既不会节约空间,效率也比拟高

如果长度不固定,应用 varchar 类型,这样不会节约空间。

日期类型

示意工夫值的日期和工夫类型为 date、time、datetime、timestamp 和 year。

类型

大小(bytes)

范畴

格局

用处

date

3

1000-01-01/9999-12-31

YYYY-MM-DD

日期

time

3

‘-838:59:59’/’838:59:59’

HH:MM:SS

工夫

datetime

8

1000-01-01 00:00:00/9999-12-31 23:59:59

YYYY-MM-DD HH:MM:SS

日期和工夫

timestamp

4

1970-01-01 00:00:00/2038

YYYYMMDD HHMMSS

日期和工夫

year

1

1901/2155

YYYY

年份

timestamp:工夫戳,理论存储的是一个从 1970-1- 1 到该 日期工夫值 的工夫毫秒值

mysql 的字段束缚

字段束缚 / 列束缚 –> 束缚: 限度

主键束缚

主键束缚:如果为一个列增加了主键束缚,那么这个列就是主键,主键的特点是惟一且不能为空。

主键的作用: 作为一个惟一标识,惟一的示意一条表记录(作用相似于人的身份证号,能够惟一的示意一个人一样。)

增加主键束缚,例如将 id 设置为主键:

create table stu(
id int primary key,

);

如果主键是数值类型,为了不便插入主键(并且保障插入数据时,主键不会因为反复而报错),能够设置一个主键自增策略。

create table stu(
id int primary key auto_increment,

);

主键自增策略是指:设置了自增策略的主键,能够在插入记录时,不给 id 赋值,只须要设置一个 null 值,数据库会主动为 id 调配一个值(AUTO_INCREMENT 变量,默认从 1 开始,前面顺次 +1),这样既能够保障 id 是惟一的,也省去了设置 id 的麻烦。

将 id 主键设置为自增:

create table stu(
id int primary key auto_increment,

);

非空束缚

非空束缚:如果为一个列增加了非空束缚,那么这个列的值就不能为空,但能够反复。

增加非空束缚,例如为 password 增加非空束缚:

create table user(
password varchar(50) not null,

);

惟一束缚

惟一束缚:如果为一个列增加了惟一束缚,那么这个列的值就必须是惟一的(即不能反复),但能够为空。

增加惟一束缚,例如为 username 增加惟一束缚及非空束缚:

create table user(
username varchar(50) unique not null,

);

外键束缚

外键其实就是用于告诉数据库两张表数据之间对应关系的这样一个列。

这样数据库就会帮咱们保护两张表中数据之间的关系。

(1) 创立表的同时增加外键

create table emp(
id int,
name varchar(50),
dept_id int,
foreign key(dept_id) references dept(id)
);

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets6b56c23ba5cf6a63d9f9e8a8b5fd8467.png?lastModify=1616081589)

(1)如果是要示意两张表的数据之间存在对应关系,只须要在其中的一张表中增加一个列,保留另外一张表的主键,就能够保留两张表数据之间的关系。

然而增加的这个列(dept_id)对于数据库来说就是一个一般列,数据库不会晓得两张表存在任何关系,因而数据库也不会帮咱们保护这层关系。

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsimage-20200530160646152.png?lastModify=1616081589)

(2)如果将 dept_id 列设置为外键,等同于告诉数据库,部门表和员工表之间存在对应关系,dept_id 列中的数据要参考部门的主键,数据库一旦晓得部门和员工表之间存在关系,就会帮咱们保护这层关系。

思考:如果在创立表时没有指定外键,那么前期该如何指定外键?以及如何删除外键?

表关系

常见的表关系分为以下三种:

一对多(多对一)·、一对一、多对多

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets1e4a36daf517c88d356cbc3c8dbd3ea8.png?lastModify=1616081589)

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets392ee4d4c0f8fdd86adc4b9abc3ad6f5.png?lastModify=1616081589)

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsimage-20201030000709386.png?lastModify=1616081589)

多表查问

— 筹备数据:以下练习将应用 db30 库中的表及表记录,请先进入 db30 数据库!!!

连贯查问

— 42. 查问部门和部门对应的员工信息

select * from dept,emp;

下面的查问中存在大量谬误的数据,个别咱们不会间接应用这种查问。

笛卡尔积查问:所谓笛卡尔积查问就是指,查问两张表,其中一张表有 m 条记录,另一张表有 n 条记录,查问的后果是 m * n 条。

尽管笛卡尔积查问中蕴含大量谬误数据,但咱们能够通过 where 子句将谬误数据剔除,保留下来的就是正确数据。

— 通过 where 子句将笛卡尔积查问后果中谬误的数据剔除, 保留正确数据!
select * from dept,emp
where emp.dept_id = dept.id;
— 因为 id 在两张表中都存在, 所以为了辨别, 必须在列名后面加上[表名.]

通过 where 子句将笛卡尔积查问中的谬误数据剔除, 保留正确的数据, 这就是连贯查问!

下面的查问能够换成上面的查问:

select * from dept inner join emp
on emp.dept_id=dept.id;
— 内连贯查问, 后果和下面的连贯查问雷同!

左外连贯查问

— 43. 查问【所有部门】及部门对应的员工,如果某个部门下没有员工,员工显示为 null

— 这是连贯查问, 后果中蕴含的都是 [有员工的部门] 和[有部门的员工]
select * from dept, emp
where dept_id=dept.id;
— 如果要查问其中一张表 (比方部门表) 的所有记录, 另一张表只显示对应的记录
select * from dept left join emp
on dept_id=dept.id;
— ——————————
select * from emp right join dept
on dept_id=dept.id;

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsimage-20201030000731608.png?lastModify=1616081589)

左外连贯查问:能够将右边表中的所有记录都查问进去,左边表只显示和右边绝对应的数据,如果右边表中某些记录在左边没有对应的数据,左边显示为 null 即可。

右外连贯查问

— 44. 查问【所有员工】及员工所属的部门,如果某个员工没有所属部门,部门显示为 null 即可

— 查问的后果中都是有员工的部门和有部门的员工
select * from dept, emp
where emp.dept_id=dept.id;
— 如果要查问所有员工及员工对应的部门
select * from dept right join emp
on emp.dept_id=dept.id;
— 或者应用左外连贯查问
select * from emp left join dept
on emp.dept_id=dept.id;

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsimage-20201030000757358.png?lastModify=1616081589)

右外连贯查问:能够将左边表中的所有记录都查问进去,右边表只显示和左边绝对应的数据,如果左边表中某些记录在右边没有对应的数据,能够显示为 null。

扩大:如果想将两张表中的所有数据都查问进去(左外 + 右外并去除重复记录),能够应用全外连贯查问,然而 mysql 又不反对全外连贯查问。

select * from dept left join emp on emp.dept_id=dept.id
union
select * from dept right join emp on emp.dept_id=dept.id;

能够应用 union 将左外连贯查问的后果和右外连贯查问的后果合并在一起,并去除反复的记录。例如:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsd0c3a3d4ad4f48d4779d1d40cac31755.png?lastModify=1616081589)

须要留神的是:union 能够将两条 SQL 语句执行的后果合并,然而有前提:

(1)两条 SQL 语句查问的后果列数必须统一

(2)两条 SQL 语句查问的后果列名、程序也必须统一

并且 union 默认就会将两个查问中反复的记录去除(如果不心愿去除重复记录,能够应用 union all)

子查问练习

— 筹备数据:以下练习将应用 db40 库中的表及表记录,请先进入 db40 数据库!!!

— 45. 列出薪资比 ’ 王海涛 ’ 的薪资高的所有员工,显示姓名、薪资

— 1)查问出 ’ 王海涛 ’ 的薪资是多少
select sal from emp where name=’ 王海涛 ’; — 2450
— 2) 查问比 ’ 王海涛 ’ 薪资高的员工有哪些?
select name,sal from emp where sal > (select sal from emp where name=’ 王海涛 ’);

— 46. 列出与 ’ 刘沛霞 ’ 从事雷同职位的所有员工,显示姓名、职位。

— 1)查问出 ’ 刘沛霞 ’ 从事的职位
select job from emp where name=’ 刘沛霞 ’; — 推销员
— 2) 和 ’ 刘沛霞 ’ 从事雷同职位的员工有哪些?
select name,job from emp
where job=(select job from emp where name=’ 刘沛霞 ’);

多表查问练习

— 47. 列出在 ’ 培优部 ’ 任职的员工,假设不晓得 ’ 培优部 ’ 的部门编号,显示部门名称,员工名称。

— 1)连贯查问部门表和员工表, 显示部门和部门对应的员工
select dept.name,emp.name from dept, emp
where emp.dept_id = dept.id;
— 表别名: 能够为表名起一个别名, 一旦起了表别名, 就肯定要用别名代替表名
select d.name,e.name from dept d, emp e
where e.dept_id = d.id;
— 2) 只查问部门名称为 ’ 培优部 ’ 的员工
select d.name,e.name from dept d, emp e
where e.dept_id = d.id and d.name=’ 培优部 ’;

— 48.(自查问)列出所有员工及其间接下级,显示员工姓名、下级编号,下级姓名

/* emp e1(员工表), emp e2(下级表)
查问的表: emp e1, emp e2
显示的列: e1.name, e1.topid, e2.name
连贯条件: e1.topid = e2.id;
*/
select e1.name, e1.topid, e2.name
from emp e1, emp e2
where e1.topid = e2.id;

— 49. 列出最低薪资大于 1500 的各种职位,显示职位和该职位的最低薪资

— 1)求出每个职位的最低薪资 (依照职位进行分组, 求每个组 / 职位的最低薪资)
select job,min(sal) from emp group by job;
— 2) 求出最低薪资大于 1500 的职位有哪些?
select job,min(sal) from emp
/ where xxx /
group by job
having min(sal)>1500;

补充内容:where 和 having 子句的区别:

相同点: 都是对查问的后果进行筛选过滤
不同点:
(1)where 是在分组之前对数据进行筛选过滤; 而 having 是在分组之后对数据进行筛选过滤
(2)where 子句中不能应用多行数据, 也不能应用列别名, 但能够应用表别名; 而 having 中能够应用多行函数, 也能够应用列别名和表别名;

— 50. 列出在每个部门就任的员工数量、平均工资。显示部门编号、员工数量,均匀薪资。

— 依据部门编号 (dept_id) 进行分组
select dept_id,count(*),avg(sal) from emp group by dept_id;

— 51. 列出受雇日期早于间接下级的所有员工,显示员工编号、员工姓名、部门名称。

/* emp e1(员工表), emp e2(下级表), dept d
查问的表:emp e1,emp e2,dept d
显示的列:e1.id,e1.name,d.name
连贯条件:e1.topid=e2.id e1.dept_id=d.id
筛选条件: e1.hdate<e2.hdate
*/
select e1.id,e1.name,d.name
from emp e1,emp e2,dept d
where e1.topid=e2.id and e1.dept_id=d.id
and e1.hdate<e2.hdate;

— 补充 52:查问员工表中薪资最高的员工信息

— 查问 emp 表中的最高薪资
select max(sal) from emp;
— 查问 emp 表中的最高薪资及对应的员工姓名
select name,max(sal) from emp; — 谬误,name 和最高薪资不对应

— 形式 1:排序 + 分页,依据薪资降序排序,分页查问,取第一条
select * from emp order by sal desc limit 0,1;
— 形式 2:子查问,求 emp 表中的最高薪资,再依据这个薪资找对应的员工
— 求 emp 表中的最高薪资是多少
select max(sal) from emp; — 5000
— 求 emp 表中最高薪资对应的员工
select * from emp where sal=(select max(sal) from emp);

扩大内容

数据库备份与复原

B 站视频链接:https://www.bilibili.com/video/BV1ez4y19776

备份数据库

在 cmd 窗口中(未登录的状态下),能够通过如下命令对指定的数据库进行备份:

mysqldump - u 用户名 -p 数据库的名字 > 备份文件的地位

示例 1: 对 db40 库中的数据 (表,表记录) 进行备份,备份到 d:/db40.sql文件中

mysqldump -uroot -p db40 > d:/db40.sql

键入明码,如果没有提醒,即示意备份胜利!

也能够一次性备份所有库,例如:

对 mysql 服务器中所有的数据库进行备份,备份到 d:/all.sql 文件中

mysqldump -uroot -p --all-database > d:/all.sql

键入明码,如果没有提醒谬误(正告信息不是谬误,能够疏忽),即示意备份胜利!

复原数据库

1、复原数据库形式一:

在 cmd 窗口中(未登录的状态下),能够通过如下命令对指定的数据库进行复原:

mysql - u 用户名 -p 数据库的名字 < 备份文件的地位

示例:将 d:/db40.sql 文件中的数据恢复到 db60 库中

— 在 cmd 窗口中(已登录的状态下),先创立 db60 库:

create database db60 charset utf8;

— 在 cmd 窗口中(未登录的状态下)

mysql -uroot -p db60 < d:/db40.sql

2、复原数据库形式二:

在 cmd 窗口中(已登录的状态下),能够通过 source 执行指定地位的 SQL 文件:

source sql 文件的地位

示例:将 d:/db40.sql 文件中的数据恢复到 db80 库中

— 在 cmd 窗口中(已登录的状态下),先创立 db80 库,进入 db80 库:

create database db80 charset utf8;
use db80;

— 再通过 source 执行指定地位下的 sql 文件:

source d:/db40.sql

Navicat 软件的应用

B 站视频链接:https://www.bilibili.com/video/BV1yA41147Vi/

Navicat Premium 是一套带图形用户界面的数据库管理工具,让你从繁多应用程序中同时连贯 MySQL、MariaDB、MongoDB、SQL Server、Oracle、PostgreSQL 和 SQLite 数据库。应用 Navicat 能够疾速、轻松地创立、治理和保护数据库。

1、应用 navicat 连贯 mysql 服务器(应用 cmd 连贯 mysql 服务器)
2、查看所有库、进入数据库、创立数据库、删除数据库、批改数据库
3、创立表、查看表、批改表、删除表
4、新增表记录、查问表记录、批改表记录、删除表记录
5、应用 navicat 书写 SQL 语句操作数据库、表和表记录


现创立学生表:

use test; — 进入 test 库
drop table if exists stu; — 删除学生表(如果存在)
create table stu(— 创立学生表

id int, -- 学生 id
name varchar(20), -- 学生姓名
gender char(1), -- 学生性别
birthday date -- 出生年月

);

批改表—新增列

语法:ALTER TABLE tabname ADD col_name datatype DEFAULT expr;

1、往 stu 表中增加 score 列,double 类型

alter table stu add score double;

批改表—批改列

语法:ALTER TABLE tabname MODIFY (col_name datatype DEFAULT expr…);

1、批改 id 列,将 id 设置为主键

alter table stu modify id int primary key;

2、批改 id 列,将 id 主键设置为主动增长

alter table stu modify id int auto_increment;

批改表—删除列

语法:ALTER TABLE tabname DROP [COLUMN] col_name;

1、删除 stu 表中的 score 列

alter table stu drop score;

增加或删除主键及自增

思考:a) 在建表时,如何为 id 指定主键束缚和自增?

b) 建好的表,如何通过批改增加主键束缚和自增?

c) 如何删除表中的主键束缚和自增?

1、创立 stu 学生表,不增加主键自增, 查看表后果

use mydb1; — 切换到 mydb1 库
drop table if exists stu; — 删除 stu 学生表(如果存在)
create table stu(— 重建 stu 学生表,没有主键自增
  id int,
  name varchar(20),
  gender char(1),
  birthday date
);
desc stu; — 查看表构造

表构造如下: 没有主键束缚和自增。

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets4f7cf015ff7829f26d326b6f443628cc-1595871301917.png?lastModify=1616081589)

2、如果表没有创立,或者要删除重建,在创立时能够指定主键或主键自增

drop table if exists stu; — 删除 stu 表
create table stu(— 从新创立 stu 表时,指定主键自增
  id int primary key auto_increment,
  name varchar(20),
  gender char(1),
  birthday date
);
desc stu; — 查看表构造

表构造如下: 曾经增加了主键束缚和自增。

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets1606d49b391f6e0d23b526912dbb22cf-1595871300159.png?lastModify=1616081589)

3、如果不想删除重建表,也能够通过批改表增加主键或主键自增

再次执行第 1 步,创立 stu 学生表,不增加主键自增,查看表后果

— 例如: 将 stu 学生表中的 id 设置为主键和主动增长

alter table stu modify id int primary key auto_increment;
desc stu; — 查看表构造

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets1606d49b391f6e0d23b526912dbb22cf.png?lastModify=1616081589)

如果只增加主键束缚,不设置自增

alter table stu modify id int primary key;

如果曾经增加主键束缚,仅仅设置自增,但需注意:

(1)如果没有设置主键,不可增加自增

(2)只有当主键是数值时,才能够增加自增

alter table stu modify id int auto_increment;

4、如果想删除主键自增

— 删除主键自增时,要先删除自增

alter table stu modify id int;

— 再删除主键束缚

alter table stu drop primary key;
desc stu; — 查看表构造

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets4f7cf015ff7829f26d326b6f443628cc.png?lastModify=1616081589)

增加外键束缚

1、增加外键形式一:建表时增加外键

现有部门表如下:

— 创立部门表

create table dept(
id int primary key auto_increment, — 部门编号
name varchar(20) — 部门名称
);

要求创立员工表,并在员工表中增加外键关联部门主键

— 创立员工表

create table emp(
  id int primary key auto_increment, — 员工编号
  name varchar(20), — 员工姓名
  dept_id int, — 部门编号
  foreign key(dept_id) references dept(id) — 指定 dept_id 为外键
);

2、增加外键形式二:建表后增加外键

现有部门表和员工表:

— 创立部门表

create table dept(
  id int primary key auto_increment, — 部门编号
  name varchar(20) — 部门名称
);

— 创立员工表

create table emp(
  id int primary key auto_increment, — 员工编号
  name varchar(20), — 员工姓名
  dept_id int — 部门编号
);

— 如果表已存在,能够应用上面这种形式:

alter table emp add constraint fk_dept_id foreign key(dept_id) references dept(id);

其中 fk_dept_id (名字由本人定义),是指外键束缚名称,也能够将【constraint fk_dept_id】省略,MySQL 会主动调配一个外键名称,未来能够通过该名称删除外键。

foreign key(dept_id)中的 dept_id 为外键

删除外键束缚

1、首先通过“show create table 表名”语法,查问含有外键表的建表语句,例如:

show create table emp;

显示后果如下:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetse5ad1306d0985dadf8ffbbd3eff61174.png?lastModify=1616081589)

其中,emp_ibfk_1 是在创立表时,数据库为外键束缚指定的一个名字,删除这个名字即可删除外键关系,例如:

alter table emp drop foreign key emp_ibfk_1;

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsimage-20201030000837137.png?lastModify=1616081589)

外键删除胜利!

增加外键束缚(多对多)

— 现有学生 (stu) 表和老师 (tea) 表:

— 创立学生表

create table stu(
  stu_id int primary key auto_increment, — 学生编号
  name varchar(20) — 学生姓名
);

— 创立老师表

create table tea(
tea_id int primary key auto_increment, — 老师编号
name varchar(20) — 老师姓名
);

— 增加第三方表 (stu_tea) 示意学生表和老师表关系

— 创立学生和老师关系表

create table stu_tea(
  stu_id int, — 学生编号
  tea_id int, — 老师编号
  primary key(stu_id,tea_id), — 设置联结主键
  foreign key(stu_id) references stu(stu_id), — 增加外键
  foreign key(tea_id) references tea(tea_id) — 增加外键
);

其中为了避免反复数据,将 stu_id 和 tea_id 设置为联结主键。

将 stu_id 设置为外键,参考 stu 表中的 stu_id 列

并将 tea_id 设置为外键,参考 tea 表中的 tea_id 列

级联更新、级联删除

— 创立 db20 库、dept 表、emp 表并插入记录

— 删除 db20 库(如果存在),并从新创立 db20 库

drop database if exists db20;
create database db20 charset utf8;
use db20;

— 创立部门表, 要求 id, name 字段

create table dept(
id int primary key auto_increment, — 部门编号
name varchar(20) — 部门名称
);

— 往部门表中插入记录

insert into dept values(null, ‘ 财务部 ’);
insert into dept values(null, ‘ 人事部 ’);
insert into dept values(null, ‘ 科技部 ’);
insert into dept values(null, ‘ 销售部 ’);

— 创立员工表, 要求 id, name, dept_id

create table emp(
  id int primary key auto_increment, — 员工编号
  name varchar(20), — 员工姓名
  dept_id int, — 部门编号
  foreign key(dept_id) references dept(id) — 指定外键
   on update cascade — 级联更新
   on delete cascade — 级联删除
);
insert into emp values(null, ‘ 张三 ’, 1);
insert into emp values(null, ‘ 李四 ’, 2);
insert into emp values(null, ‘ 老王 ’, 3);
insert into emp values(null, ‘ 赵六 ’, 4);
insert into emp values(null, ‘ 刘能 ’, 4);

级联更新:主表 (dept 表) 中的主键产生更新时(例如将销售部的 id 改为 40),从表(emp 表)中的记录的外键数据也会跟着该表(即赵六和刘能的部门编号也会更新为 40)

级联删除:如果不增加级联删除,当删除部门表中的某一个部门时(例如删除 4 号部门),若该部门在员工表中有对应的员工(赵六和刘能),删除会失败!

若果增加了级联删除,当删除部门表中的某一个部门时,若该部门在员工表中有对应的员工,会在删除部门的同时,将员工表中对应的员工也删除!

where 中不能应用列别名

SQL 语句的书写程序:

select * | 列名 — 确定要查问的列有哪些
from 表名 — 确定查问哪张表
where 条件 — 通过筛选过滤,剔除不符合条件的记录
group by 分组的列 — 指定依据哪一列进行分组
having 条件 — 通过条件对分组后的数据进行筛选过滤
order by 排序的列 — 指定依据哪一列进行排序
limit (countPage-1)*rowCount, rowCount — 指定返回第几页记录以及每页显示多少条

SQL 语句的执行程序:

from 表名 — 确定查问哪张表
where 条件 — 通过筛选过滤,剔除不符合条件的记录
select * | 列名 列别名 — 确定要查问的列有哪些,
group by 分组的列 — 指定依据哪一列进行分组
having 条件 — 通过条件对分组后的数据进行筛选过滤
order by 排序的列 — 指定依据哪一列进行排序
limit (countPage-1)*rowCount, rowCount

对于 where 中不能应用列别名然而能够应用表别名?**

是因为,表别名是申明在 from 中,from 先于 where 执行,先申明再应用没有问题,然而列别名是申明在 select 中,where 先于 select 执行,如果先应用列别名,再申明,这样执行会报错!!

unit07-JDBC

学习指标:

  • 理解什么是 JDBC?为什么要学习这门技术?
  • 把握通过 JDBC 连贯并拜访数据库
  • 把握 PreparedStatement 传输器的应用(SQL 注入攻打)
  • 把握什么是连接池?为什么要应用连接池?
  • 把握 C3P0 连接池的用法
  • 数据库事务(放在前面讲,框架之前)

JDBC 概述

什么是 JDBC?为什么要学习 JDBC?

JDBC(Java DataBase Connectivity) Java 数据库连贯

其实就是 利用 Java 语言 / 程序连贯并拜访数据库的一门技术

之前咱们能够通过 CMD 或者 navicat 等工具连贯数据库

但在企业开发中,更多的是通过程序(Java 程序)连贯并拜访数据库,通过 Java 程序拜访数据库,就须要用到 JDBC 这门技术。

如何通过 JDBC 程序拜访数据库?

1、提出需要:

创立一个 jt_db 数据库,在库中创立一个 account 表,并插入三条记录,而后利用 Java 程序查问出 account 表中所有的记录,并将查问的后果打印在管制台上。

2、开发步骤:

(1)筹备数据, 创立 jt_db 库, 创立 account 表

drop database if exists jt_db;
create database jt_db charset utf8;
use jt_db;
create table account(

id int primary key auto_increment,
name varchar(50),
money double

);
insert into account values(null, ‘tom’, 1000);
insert into account values(null, ‘andy’, 1000);
insert into account values(null, ‘tony’, 1000);

如果曾经执行过课前材料中的 ”SQL 脚本文件 ”,此步骤能够跳过。

(2)创立 JAVA 工程:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsimage-20200318153229937.png?lastModify=1616081589)

(3)导入 jar 包——mysql 驱动包:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsimage-20201030000404489.png?lastModify=1616081589)

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets6ba3042ef2947f314836128a5c19e2c2.png?lastModify=1616081589)

(4)创立类并实现 JDBC 程序(六个步骤)

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsdccd4a8163b4835b74e77f945d106683.png?lastModify=1616081589)

代码实现:

public static void main(String[] args) throws Exception {
   //1. 注册数据库驱动
   Class.forName(“com.mysql.jdbc.Driver”);
   //2. 获取数据库连贯
   Connection conn = DriverManager.getConnection(
       “jdbc:mysql://localhost:3306/jt_db?characterEncoding=utf-8”,
       “root”, “root”);
   //3. 获取传输器
   Statement stat = conn.createStatement();
   //4. 发送 SQL 到服务器执行并返回执行后果
   String sql = “select * from account”;
   ResultSet rs = stat.executeQuery(sql);
   //5. 处理结果
   while(rs.next() ) {
       int id = rs.getInt(“id”);
       String name = rs.getString(“name”);
       double money = rs.getDouble(“money”);
       System.out.println(id+” : “+name+” : “+money);
  }
   //6. 开释资源
   rs.close();
   stat.close();
   conn.close();
   System.out.println(“TestJdbc.main()….”);
}

3、执行后果:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsa37ab7234bb1beb988f4b9c01c8cee2f.png?lastModify=1616081589)

JDBC API 总结

1、注册数据库驱动

Class.forName(“com.mysql.jdbc.Driver”);

所谓的注册驱动,就是让 JDBC 程序加载 mysql 驱动程序,并治理驱动

驱动程序实现了 JDBC API 定义的接口以及和数据库服务器交互的性能,加载驱动是为了方便使用这些性能。

2、获取连贯之数据库 URL

Connection conn = DriverManager.getConnection(
   “jdbc:mysql://localhost:3306/jt_db?characterEncoding=utf-8”,
   “root”, “root” );

DriverManager.getConnection() 用于获取数据连贯,返回的 Connection 连贯对象是 JDBC 程序连贯数据库至关重要的一个对象。

参数 2 参数 3 别离是所连贯数据库的用户名和明码。

参数 1 :”jdbc:mysql://localhost:3306/jt_db” 是连贯数据库的 URL,用于指定拜访哪一个地位上的数据库服务器及服务器中的哪一个数据库,其写法为:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets7edb0faa5fac5f29cd19411909c708cb.png?lastModify=1616081589)

当连贯本地数据库,并且端口为 3306,能够简写为如下模式:

jdbc:mysql:///jt_db

3、Statement 传输器对象

Statement stat = conn.createStatement();
该办法返回用于向数据库服务器发送 sql 语句的 Statement 传输器对象

该对象上提供了发送 sql 的办法:

executeQuery(String sql) —
用于向数据库发送查问类型的 sql 语句,返回一个 ResultSet 对象中
executeUpdate(String sql) —
用于向数据库发送更新 (减少、删除、批改) 类型的 sql 语句,返回一个 int 值,示意影响的记录行数

4、ResultSet 后果集对象

ResultSet 对象用于封装 sql 语句查问的后果,也是一个十分重要的对象。该对象上提供了遍历数据及获取数据的办法。

(1)遍历数据行的办法

next() – 使指向数据行的箭头向下挪动一行,并返回一个布尔类型的后果,true 示意箭头指向了一行数据,false 示意箭头没有指向任何数据(前面也没有数据了)

(2)获取数据的办法

getInt(int columnIndex)
getInt(String columnLable)
getString(int columnIndex)
getString(String columnLable)
getDouble(int columnIndex)
getDouble(String columnLable)
getObject(int columnIndex)
getObject(String columnLable)

5、开释资源

rs.close();
stat.close();
conn.close();

此处开释资源必须依照肯定的程序开释,越晚获取的越先敞开。所以先敞开 rs 对象,再敞开 stat 对象,最初敞开 conn 对象。

另,为了防止下面的程序抛出异样,开释资源的代码不会执行,应该把开释资源的代码放在 finally 块中.

try{

}catch(Exception e){

}finally{
   if (rs != null) {
       try {
      rs.close();
      } catch (SQLException e) {
           e.printStackTrace();
      } finally {
           rs = null;
      }
  }
   if (stat != null) {
       try {
      stat.close();
      } catch (SQLException e) {
           e.printStackTrace();
      } finally {
           stat = null;
      }
  }
   if (conn != null) {
       try {
      conn.close();
      } catch (SQLException e) {
           e.printStackTrace();
      } finally {
           conn = null;
      }
  }
}

增删改查

JDBC 增删改查

1、新增: 往 account 表中增加一个名称为 john、money 为 3500 的记录

/ 1、新增: 往 account 表中增加一个名称为 john、money 为 3500 的记录 /
@Test
public void testInsert() {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
// 注册驱动并获取连贯
conn = JdbcUtil.getConn();
// 获取传输器
stat = conn.createStatement();
// 发送 sql 语句到服务器执行, 并返回执行后果
String sql = “insert into account values(null, ‘john’, 3500)”;
int rows = stat.executeUpdate(sql);
// 处理结果
System.out.println(“ 影响行数: “+rows);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 通过 JdbcUtil 工具类中的 close 办法开释资源
JdbcUtil.close(conn, stat, rs);
}
}

2、批改: 将 account 表中名称为 john 的记录,money 批改为 1500

/ 2、批改: 将 account 表中名称为 john 的记录,money 批改为 1500 /
@Test
public void testUpdate() {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
// 注册驱动并获取连贯
conn = JdbcUtil.getConn();
// 获取传输器
stat = conn.createStatement();
// 发送 sql 语句到服务器执行, 并返回执行后果
String sql = “update account set money=1500 where name=’john'”;
int rows = stat.executeUpdate(sql);
// 处理结果
System.out.println(“ 影响行数: “+rows);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 通过 JdbcUtil 工具类中的 close 办法开释资源
JdbcUtil.close(conn, stat, rs);
}
}

3、查问(本人实现): 查问 account 表中名称为 john 的记录

/ 3、查问: 查问 account 表中 id 为 1 的记录 /
@Test
public void testFindById() {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
// 注册驱动并获取连贯
conn = JdbcUtil.getConn();
// 获取传输器
stat = conn.createStatement();
// 执行 sql 语句, 返回执行后果
String sql = “select * from account where id=1”;
rs = stat.executeQuery(sql);
// 处理结果
if(rs.next() ) {
int id = rs.getInt(“id”);
String name = rs.getString(“name”);
double money = rs.getDouble(“money”);
System.out.println(id+” : “+name+” : “+money);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
JdbcUtil.close(conn, stat, rs);
}
}

4、删除(本人实现): 删除 account 表中名称为 john 的记录

/ 4、删除: 删除 account 表中名称为 john 的记录 /
@Test
public void testDelete() {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
// 注册驱动并获取连贯
conn = JdbcUtil.getConn();
// 获取传输器
stat = conn.createStatement();
// 发送 sql 语句到服务器执行, 并返回执行后果
String sql = “delete from account where name=’john'”;
int rows = stat.executeUpdate(sql);
// 处理结果
System.out.println(“ 影响行数: “+rows);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 通过 JdbcUtil 工具类中的 close 办法开释资源
JdbcUtil.close(conn, stat, rs);
}
}

单元测试补充

单元测试:不必创立新的类,也不必提供 main 函数,也不必创立类的实例,就能够间接执行一个办法

加了 @Test 注解的办法,能够通过单元测试(junit)框架测试该办法。底层会创立该办法所在类的实例,通过实例调用该办法。

@Test
public void testInsert() {
System.out.println(“TestPreparedStatement.testInsert()”);
}

可能应用 @Test 单元测试测试的办法必须满足如下几个条件:

(1)办法必须是公共的
(2) 办法必须是非动态的
(3) 办法必须是无返回值的
(4) 办法必须是无参数的
(5) 进行单元测试的办法或类,命名时不要命名为 Test/test

PreparedStatement

在下面的增删改查的操作中,应用的是 Statement 传输器对象,而在开发中咱们用的更多的传输器对象是 PreparedStatement 对象,PreparedStatement 是 Statement 的子接口,比 Statement 更加平安,并且可能进步程序执行的效率。

Statement 父对象

PreparedStatement 子对象

模仿用户登录案例

(1)筹备数据

use jt_db;
create table user(
  id int primary key auto_increment,
  username varchar(50),
  password varchar(50)
);
insert into user values(null,’ 张三 ’,’123′);
insert into user values(null,’ 李四 ’,’234′);

(2)创立 LoginUser 类,提供 main 办法 和 login 办法。

public static void main(String[] args) {
/* 1、提醒用户登录,提醒用户输出用户名并接管用户名

*  2、提醒用户输出明码并接管明码
*  3、依据用户名和明码查问用户信息
*/

// 1、提醒用户登录,提醒用户输出用户名并接管用户名
Scanner sc = new Scanner(System.in);
System.out.println(“ 请登录:”);
System.out.println(“ 请输出用户名:”);
String user = sc.nextLine();

// 2、提醒用户输出明码并接管明码
System.out.println(“ 请输出明码:”);
String pwd = sc.nextLine();

// 3、依据用户名和明码查问用户信息
login(user, pwd);
}
/**

  • 依据用户名和明码查问用户信息
  • @param user 用户名
  • @param pwd 明码

*/
private static void login(String user, String pwd) {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
//1. 注册驱动并获取连贯
conn = JdbcUtil.getConn();
//2. 获取传输器,执行 sql 并返回执行后果
stat = conn.createStatement();
String sql = “select * from user where username='”+user+”‘ and password='”+pwd+”‘”;
rs = stat.executeQuery(sql);
System.out.println(sql);
//3. 处理结果
if(rs.next() ) {// 有数据 — 用户名明码都正确
System.out.println(“ 祝贺您登录胜利!”);
}else {// 没数据 — 用户名或明码不正确
System.out.println(“ 登录失败, 用户名或明码不正确!”);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//4. 开释资源
JdbcUtil.close(conn, stat, rs);
}
}

执行时,输出:

请登录:
请输出用户名:
张飞 ’#’
请输出明码:

select * from user where username=’ 张飞 ’#” and password=”
祝贺您登录胜利了!

或输出

请登录:
请输出用户名:
张飞 ’ or ‘1=1
请输出明码:

select * from user where username=’ 张飞 ’ or ‘1=1’ and password=”
祝贺您登录胜利了!

或输出

请登录:
请输出用户名:

请输出明码:
‘ or ‘2=2
select * from user where username=” and password=” or ‘2=2’
祝贺您登录胜利了!

SQL 注入攻打

通过下面的案例,咱们发现在执行时,不输出明码只输出用户名也能够登陆胜利。这就是 SQL 注入攻打。

SQL 注入攻打产生的起因: 因为后盾执行的 SQL 语句是拼接而来的:

select * from user where username='”+user+”‘ and password='”+pwd+”‘

其中的参数是用户提交过去的,如果用户在提交参数时,在参数中掺杂了一些 SQL 关键字(比方 or)或者特殊符号(#、–、’ 等),就可能会导致 SQL 语句语义的变动,从而执行一些意外的操作(用户名或明码不正确也能登录胜利)!

避免 SQL 注入攻打

如何避免 SQL 注入攻打?

(1)应用正则表达式对用户提交的参数进行校验。如果参数中有(# — ‘ or 等)这些符号就间接完结程序,告诉用户输出的参数不非法

(2)应用 PreparedStatement 对象来代替 Statement 对象。

上面通过第二种形式解决 SQL 注入攻打:增加 loginByPreparedSatement 办法,在办法中,应用 PreparedStatement 来代替 Statement 作为传输器对象应用,代码示例:

/**

  • 依据用户名和明码查问用户信息
  • @param user 用户名
  • @param pwd 明码

*/
private static void login(String user, String pwd) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1. 注册驱动并获取连贯
conn = JdbcUtil.getConn();
//2. 获取传输器,执行 sql 并返回执行后果
String sql = “select * from user where username=? and password=?”;
ps = conn.prepareStatement(sql);
// 设置 SQL 语句中的参数
ps.setString(1 , user);
ps.setString(2 , pwd);
// 执行 SQL 语句
rs = ps.executeQuery();// 这里不要再传输 SQL 语句

//3. 处理结果
if(rs.next() ) {// 有数据 — 用户名明码都正确
System.out.println(“ 祝贺您登录胜利!”);
}else {// 没数据 — 用户名或明码不正确
System.out.println(“ 登录失败, 用户名或明码不正确!”);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//4. 开释资源
JdbcUtil.close(conn, ps, rs);
}
}

再次执行程序,依照下面的操作登录。此时,曾经胜利的避免了 SQL 注入攻打问题了。

PreparedStatement 对象是如何避免 SQL 注入攻打的:

应用 PreparedStatement 对象是先将 SQL 语句的骨架发送给服务器编译并确定下来,编译之后,SQL 语句的骨架和语义就不会再被扭转了,再将 SQL 语句中的参数发送给服务器,即便参数中再蕴含 SQL 关键字或者特殊符号,也不会导致 SQL 语句的骨架或语义被扭转,只会被当作一般的文原本解决!


应用 PreparedStatement 对象能够避免 SQL 注入攻打

而且通过办法设置参数更加的不便且不易出错!

还能够从某些方面进步程序执行的效率!

数据库连接池

什么是连接池

常量池 / 线程池 / 连接池

池:指内存中的一片空间(容器,比方数组、汇合)

连接池:就是将连贯寄存在容器中,供整个程序共享,能够实现连贯的复用,缩小连贯创立和敞开的次数,从而进步程序执行的效率!

为什么要应用连接池

1、传统形式操作数据库

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsed6ceb2908a9c2a66c3336a651056ce0.png?lastModify=1616081589)

Connection conn = DriverManager.getConnection(url, user, pwd); // 创立连贯对象
……
conn.close(); // 敞开连贯, 销毁连贯

在传统形式中,每次用户须要连贯拜访数据库时,都是 创立一个连贯 对象,基于这个连贯对象拜访数据库,用完连贯后,会将 连贯敞开(conn.close())。

因为每次创立连贯和敞开连贯十分的耗时间而且耗资源,因而会导致程序执行的效率低下。

2、应用连接池操作数据库

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsb7566731966b48e3d918bbd205d30030.jpg?lastModify=1616081589)

能够在程序一启动时,就创立一批连贯放在一个连接池中(容器),当用户须要连贯时,就从连接池中获取一个连贯对象,用完连贯后,不要敞开,而是将连贯再还回连接池中,这样一来,用来用去都是池中的这一批连贯,实现了连贯的复用,缩小了连贯创立和敞开的次数,从而进步了程序执行的效率!

如何应用 C3P0 连接池

dbcp/c3p0/druid

所有的连接池都要实现一个接口——DataSource(数据源),因而连接池也被叫做数据源!

应用 C3P0 连接池开发步骤:

1、导入开发包

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsd9837a8227847f3e22aa28a9e5bf6aa5.png?lastModify=1616081589)

2、创立数据库连接池(对象)

ComboPooledDataSource cpds = new ComboPooledDataSource();

3、设置连贯数据库的根本信息

1)形式一:(不举荐) 间接将参数通过 pool.setXxx 办法设置给 c3p0 程序

这种形式间接将参数写死在了程序中,前期一旦参数发生变化,就要批改程序,要从新编译我的项目、从新公布我的项目,十分麻烦。

// 设置连贯数据库的根本信息
pool.setDriverClass(“com.mysql.jdbc.Driver”);
pool.setJdbcUrl(“jdbc:mysql:///jt_db?characterEncoding=utf-8”);
pool.setUser(“root”);
pool.setPassword(“root”);

2)形式二:将连贯参数提取到 properties 文件中(举荐)

文件必须放在 src(源码根目录)目录下 !

文件名必须叫做 c3p0.properties !

在类目录下(开发时能够放在 src 或者相似的源码目录下),增加一个 c3p0.properties 文件,配置内容如下:

c3p0.driverClass=com.mysql.jdbc.Driver
c3p0.jdbcUrl=jdbc:mysql:///jt_db?characterEncoding=utf-8
c3p0.user=root
c3p0.password=root

这种形式因为是 c3p0 到指定的地位下寻找指定名称的 properties 文件,所以文件的地位必须是放在 src 或其余源码根目录下,文件名必须是 c3p0.properties。

3)形式三:将连贯参数提取到 xml 文件中(举荐)

文件必须放在 src(源码根目录)目录下 !

文件名必须叫做 c3p0-config.xml

在类目录下(开发时能够放在 src 或者相似的源码目录下),增加一个 c3p0-config.xml 文件,配置内容如下:

<?xml version=”1.0″ encoding=”UTF-8″?>
<c3p0-config>
   <default-config>
       <property name=”driverClass”>com.mysql.jdbc.Driver</property>
       <property name=”jdbcUrl”>jdbc:mysql:///jt_db?characterEncoding=utf-8</property>
       <property name=”user”>root</property>
       <property name=”password”>root</property>
   </default-config>
</c3p0-config>

这种形式因为是 c3p0 到指定的地位下寻找指定名称的 xml 文件,所以文件的地位必须是放在 src 或其余源码根目录下,文件名必须是 c3p0-config.xml。

4、从连接池中获取一个连贯对象并进行应用

Connection conn = pool.getConnection();

5、用完连贯后将连贯还回连接池中

conn.close()
/* 如果是本人创立的连贯对象,这个连贯对象没有通过任何的改变,调用

  • conn.close 办法,是将连贯对象敞开
  • 如果是从连接池中获取的连贯对象,该连贯对象在返回时就曾经被连接池
  • 革新了,将连贯对象的 close 办法改为了还连贯到连接池中

*/

扩大: 学生信息管理系统

通过 JDBC 实现学生信息管理系统

B 站视频链接:https://www.bilibili.com/video/BV1ka4y1x7M6

筹备数据

建库建表语句如下:

— 1、创立数据库 jt_db 数据库(如果不存在才创立)
create database if not exists jt_db charset utf8;
use jt_db; — 抉择 jt_db 数据库
— 2、在 jt_db 库中创立 stu 表(学生表)
drop table if exists stu;
create table stu(
  id int,
  name varchar(50),
  gender char(2),
  addr varchar(50),
  score double
);
— 3、往 stu 表中, 插入记录
insert into stu values(1001,’ 张三 ’,’ 男 ’, ‘ 北京 ’, 86);

性能实现

运行程序控制台提醒如下:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets4b9ad722dca6065b01a5202f85e2d89c.png?lastModify=1616081589)

输出 a:查问所有学生信息

输出 b:增加学生信息

输出 c:依据 id 批改学生信息

输出 d:依据 id 删除学生信息

查问所有学生信息

在控制台中输出操作代码 ”a”,成果如下:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets55a1ab054846de0d92525b6292f7b930.png?lastModify=1616081589)

增加学生信息

在控制台中输出操作代码 ”b”,成果如下:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assetsc9f2e18f2a41668cd39ddaccd712290c.png?lastModify=1616081589)

依据 id 批改学生信息

在控制台中输出操作代码 ”c”,成果如下:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets40de75c8a4f08fc4a4b581b358be6a87.png?lastModify=1616081589)

依据 id 删除学生信息

在控制台中输出操作代码 ”d”,成果如下:

%E7%AC%AC%E4%BA%8C%E9%98%B6%E6%AE%B5%E8%AE%B2%E4%B9%8903.assets9eda8bae1797eced35a1e4907009db83.png?lastModify=1616081589)

unit08-transaction

今日指标:

  • 理解事务的作用
  • 把握事务的四大个性(面试)
  • 理解事务的三个并发读问题
  • 把握 mysql 开启和完结事务
  • 理解事物的四个隔离级别

事务及四大个性

什么是事务

数据库事务(Database Transaction),是指作为单个逻辑工作单元执行的一系列操作,要么齐全地执行,要么齐全地不执行。

简略的说:事务就是将一堆的 SQL 语句 (通常是增删改操作) 绑定在一起执行,要么都执行胜利,要么都执行失败,即都执行胜利才算胜利,否则就会复原到这堆 SQL 执行之前的状态。

上面以银行转账为例,张三转 100 块到李四的账户,这至多须要两条 SQL 语句:

  • 给张三的账户减去 100 元;

    update 账户表 set money=money-100 where name=’ 张三 ’;

  • 给李四的账户加上 100 元。

    update 账户表 set money=money+100 where name=’ 李四 ’;

如果在第一条 SQL 语句执行胜利后,在执行第二条 SQL 语句之前,程序被中断了(可能是抛出了某个异样,也可能是其余什么起因),那么李四的账户没有加上 100 元,而张三却减去了 100 元,在现实生活中这必定是不容许的。

如果在转账过程中退出事务,则整个转账过程中执行的所有 SQL 语句会在一个事务中,而事务中的所有操作,要么全都胜利,要么全都失败,不可能存在胜利一半的状况。

也就是说给张三的账户减去 100 元如果胜利了,那么给李四的账户加上 100 元的操作也必须是胜利的;否则,给张三减去 100 元以及给李四加上 100 元都是失败的。

事务的四大个性

事务的四大个性 (ACID) 是:

1)原子性(Atomicity):一个事务(transaction)中的所有操作,要么全副实现,要么全副不实现,不会完结在两头某个环节。即便事务在执行过程中产生谬误,也会被回滚(Rollback)到事务开始前的状态,就像这个事务素来没有执行过一样。

即:事务中所有操作是不可再宰割的原子单位。事务中所有操作要么全副执行胜利,要么全副执行失败。

2)一致性(Consistency):在事务开始之前和事务完结当前,数据库的完整性没有被毁坏。这示意写入的材料必须完全符合所有的预设规定,这蕴含材料的精确度、串联性以及后续数据库能够自发性地实现预约的工作。

例如:转账业务,无论事务执行胜利与否,参加转账的两个账户金额之和在事务前后应该是放弃不变的。

张三:1000 1000-500=500 1000
李四:1000 1000+500=1500 1000

3)隔离性(Isolation):数据库容许多个并发事务同时对其数据进行读写和批改的能力,隔离性能够避免多个事务并发执行时因为穿插执行而导致数据的不统一。即:隔离性能够在事务并发时,让不同的事务隔离开来,一个事务看不到另一个事务正在进行中的状态。

例如:在 A 事务中,查看另一 B 事务 (正在批改张三的账户金额) 中张三的账户金额,要查看到 B 事务之前的张三的账户金额,要么查看到 B 事务之后张三的账户金额。

事务 1: 查问 A、B 账户金额之和
事务 2: A 转账给 B 500 元

    A - 500 = 500
    B + 500 = 1500

4)持久性(Durability):事务处理完结后,对数据的批改就是永恒的,即使系统故障也不会失落。因为事务一旦提交,事务中所有的数据操作都必须被长久化到数据库中,即便事务提交后,数据库马上解体,在数据库重启时,也必须能保障通过某种机制复原数据。

开启事务 —A 给 B 转账 500 元
A: 1000 – 500 = 500 (胜利了) — 在日志中记录, 事务胜利,A 账户金额更新为 500
B: 1000 + 500 = 1500 (胜利了) — 在日志中记录, 事务胜利,B 账户金额更新为 1500
完结事务 — 回滚 / 提交

MySQL 中的事务

mysql 中操作事务

在默认状况下,MySQL 每执行一条 SQL 语句,都是一个独自的事务。因为底层在执行 SQL 语句之前会主动开启事务,在 SQL 语句执行完后,会主动提交事务!

如果须要在一个事务中蕴含多条 SQL 语句,那么须要手动开启事务和完结事务。

  • 开启事务:begin / start transaction;

    begin 或 start transaction 显式地开启一个事务;

  • 提交事务:commit;

    commit 会提交事务,并使已对数据库进行的所有批改成为永久性的;

  • 回滚事务:rollback;

    rollback 会回滚事务,即撤销正在进行的所有未提交的批改。数据也会复原到事务开始前的状态,就像这个事务素来没有执行过一样。

上面演示事务在转账例子中的利用:

筹备数据:

— 1、创立数据库 jt_db 数据库(如果不存在才创立)
create database if not exists jt_db charset utf8;
use jt_db; — 抉择 jt_db 数据库
— 2、在 jt_db 库中创立 acc 表(银行账户表), 要求有 id(主键),name(姓名),money(账户金额)
drop table if exists acc;
create table acc(
  id int primary key auto_increment,
  name varchar(50),
  money double
);
— 3、往 acc 表中, 插入 2 条记录
insert into acc values(null,’A’,1000);
insert into acc values(null,’B’,1000);
— 查问 acc 表中的所有记录
select * from acc;

上面别离演示事务开启及执行一系列 SQL 之后,回滚事务、提交事务及中断操作的成果。

rollback(回滚事务)

— 查问 acc 账户表中 A 和 B 的金额
select * from acc;
— 开启事务
start transaction;
— 开始转账,A 账户减去 100 元
update acc set money=money-100 where name=’A’;
— 查问 acc 账户表中 A 和 B 的金额
select * from acc;
— B 账户减少 100 元
update acc set money=money+100 where name=’B’;
— 查问 acc 账户表中 A 和 B 的金额
select * from acc;
— 回滚事务
rollback;
— 再次查问 acc 账户表中 A 和 B 的金额
select * from acc;

commit(提交事务):将下面的操作再做一次,最初将 rollback 替换为 commit,即提交事务

commit;

中断操作:将下面的操作再做一次,最初将 rollback 替换为 quit,即中断操作

quit;

jdbc 中操作事务

在 JDBC 中实现转账例子

提醒:JDBC 中默认是主动提交事务,所以须要敞开主动提交,改为手动提交事务

也就是说, 敞开了主动提交后, 事务就主动开启, 然而执行完后须要手动提交或者回滚!!

(1)执行上面的程序,程序执行没有异样,转账胜利!A 账户减去 100 元,B 账户减少 100 元。

(2)将第 4 步、5 步两头的代码放开,再次执行程序,在转账过程中抛异样,转账失败!因为事务回滚,所以 A 和 B 账户金额不变。

public static void main(String[] args) throws SQLException {
  Connection conn = null;
  Statement stat = null;
  ResultSet rs = null;
  try {
      //1. 获取连贯
      Class.forName(“com.mysql.jdbc.Driver”);
  //2. 获取数据库连贯
  Connection conn = DriverManager.getConnection(
      “jdbc:mysql://localhost:3306/jt_db?characterEncoding=utf-8”,
      “root”, “root”);
      //2. 敞开 JDBC 主动提交事务(默认开启事务)
      conn.setAutoCommit(false);
      //3. 获取传输器
      stat = conn.createStatement();
       / A 给 B 转账 100 元 /
      //4.A 账户减去 100 元
      String sql = “update acc set money=money-100 where name=’A'”;
      stat.executeUpdate(sql);
      //int i = 1/0; // 让程序抛出异样,中断转账操作
      //5.B 账户加上 100 元
      sql = “update acc set money=money+100 where name=’B'”;
      stat.executeUpdate(sql);
      //6. 手动提交事务
      conn.commit();
      System.out.println(“ 执行胜利!提交事务 …”);
  } catch (Exception e) {
  e.printStackTrace();
  // 一旦其中一个操作出错都将回滚,使两个操作都不胜利
  conn.rollback();
  System.out.println(“ 执行失败!回滚事务 …”);
  } finally{
  rs.close();
  stat.close();
  conn.close();
  }
}

退出移动版