一.数据库概述¶
1️⃣什么是数据库¶
数据库最基本的定义是指相关信息的集合
数据库(Database)是按照 数据结构 来组织、存储和管理数据的仓库
在计算机科学中,“数据库”被定义为 以数据形式存储 在计算机系统上的信息的集合
数据库的分类
- 早期: 层次式数据库(
IBM IMS,TOTAL), 网络型数据库(CODASYL,IDMS) - 现在: 关系型数据库(
Oracle,MySQL,SQL Server,PostgreSQL,SQLite,MariaDB), 非关系型数据库(Redis,MongoDB) - 现在市面上大部分用的还是关系型数据库
 
2️⃣什么是关系型数据库¶
 底层以 二维表 的形式保存数据的库,就是关系型数据库  常见的关系型数据库有哪些?
- Oracle: 甲骨文公司提供的一款数据库产品,收费的,之前在Java中的市场份额超过50% 。主要适用于一些大型或者超大型应用系统。
 - SQL Server: 微软提供的一款数据库产品,收费,主要适用于一些中型或者大型的应用系统
 - MySQL: 瑞典的一个公司(MySQLAB)提供的一款数据库产品,特点是小巧轻量,简单易用,适用于一些小型或中型的应用系统,如果做mysql集群,也可以用于一些大型或者超大型系统。免费!mysql目前已经被甲骨文收购了
 - DB2: IBM公司提供的一款数据库产品, 用于金融/银行等系统较多, 收费!
 - SQLite: 迷你数据库, 用于嵌入式设备(手机/智能家居等产品)
 
3️⃣数据库相关名字解释¶
数据库服务器: 其实就是你安装的哪个mysql软件,将mysql安装在计算机上,那么这台计算机就可以作为数据库服务器使用,可以实现数据的存和取。一个数据库服务器中可以包含多个数据库。
比如:装好的mysql服务器中自带了四个数据库
数据库: 数据库就是存储数据的仓库,通常情况下一个网站(系统)中的所有数据会存放在一个数据库中 京东网站的所有数据 db_jd 淘宝网站的所有数据 db_taobao 百度网站的所有数据 db_baidu
表: 数据库中的数据是安装类型存放的, 一类数据往往存储在一张表中, 一个数据库中可以创建多张表! 京东网站的用户数据 tb_user 京东网站的商品数据 tb_product 京东网站的订单数据 tb_order
表记录: 一张表中可以包含多行表记录, 一张表中用于存储一类信息, 一行表记录就用于存储某一个具体的数据 数据库中的表 java中的类(student) 表记录 对象
4️⃣SQL语言¶
- SQL语言是一门操作关系型数据库的通用的语言(学会了SQL可以操作所有的关系型数据库)
 - SQL语言可以操作的有:
- 查看库、创建库、删除库、修改库
 - 创建表、删除表、修改表、查看表
 - 新增表记录、删除表记录、修改表记录、查询表记录(数据)
 - 存储过程/视图/索引等也可以操作
 
 - SQL语言是一门通用的操作关系型数据库的语言,但每个数据库厂商为了增强自己数据库的功能,都提供了少量的"方言"(独有的SQL语句),SQL语言通用,但方言不通用!
 
5️⃣如何连接mysql服务器(cmd窗口)¶
- 方式一: 
mysql -u用户名 -p密码mysql中默认有一个超级管理员(具有所有权限),用户名就是root - 方式二: 
mysql -u用户名 -p在下一行键入密码 - 
方式三:
mysql -u用户名 -p -h主机名或ip地址 -P端口- -h: 后面跟的是主机名或ip地址,如果不写-h,默认连接localhost(127.0.0.1)
 - -P: 后面跟的是端口, 如果不写-P,默认端口是3306
 
 - 
退出连接mysql服务器: exit quit 或者直接关闭窗口
 - 扩展内容: SQL注释
 
注意
Ctrl ➕ C 会取消当前sql语句的执行
二.数据库及表操作¶
1️⃣创建、删除、查看数据库¶
①查看mysql服务器中所有数据库¶
②进入某一数据库(进入数据库后,才能操作库中的表和表记录)¶
语法:USE 库名;
查看已进入的库
③查看当前数据库中的所有表¶
先进入某一个库,再查看当前库中的所有表
④删除mydb库¶
语法:DROP DATABASE 库名;
当删除的库不存在时,如何避免错误产生?
⑤重新创建mydb库,指定编码为utf8¶
语法:CREATE DATABASE 库名 CHARSET 编码;
-- 需要注意的是,mysql中不支持横杠(-),所以utf-8要写成utf8;
create database mydb charset utf8;
-- 如果不存在则创建mydb,如果已存在,则不执行创建操作,也就不会报错了!
create database if not exists mydb charset utf8;
⑥查看建库时的语句(并验证数据库库使用的编码)¶
语法:SHOW CREATE DATABASE 库名;
2️⃣创建、删除、查看表¶
⑦进入mydb库,删除stu学生表(如果存在)¶
语法:DROP TABLE 表名;
⑧创建stu学生表(编号[数值类型]、姓名、性别、出生年月、考试成绩[浮点型])¶
建表的语法:CREATE TABLE 表名(列名 数据类型, 列名 数据类型, ... 列名 数据类型);
创建stu表的SQL语句如下:
create table stu(
    id int,
    name varchar(50), -- 50表示最多存50个字符
    gender varchar(10), 
    birthday date,
    score double
);-- mysql中也有数据类型,这里先使用,后面再做说明!
⑨查看stu学生表结构¶
3️⃣新增、更新、删除表记录¶
⑩往学生表(stu)中插入记录(数据)¶
语法:INSERT INTO 表名(列名1,列名2,列名3...) VALUES(值1,值2,值3...);
注意事项
- 创建mydb库时一定要指定编码utf8,这样在库中创建的表也是utf8编码
 - 如果是在cmd中执行插入记录的语句,先 
set names gbk;再插入记录! - mysql中推荐使用单引号包裹字符串和日期(有些版本的数据库双引号包裹会报错!)
 - 一个cmd窗口只需要设置一次(set names gbk;)编码
 - 注意: 如果是要给表中的所有列都插入值,列名可以省略不写, 值的个数和顺序必须和表中创建的列的个数和顺序保持一致!
 - value后面只能跟一个括号,只能一次插入一条记录,values后面可以跟多个括号,用逗号分隔,一次可以插入多条数据!
 
insert into stu(id, name, gender, birthday, score) value(1, 'tom', 'male', '1985-10-11', 86);
insert into stu values(2, '马谈尹', '男', '1978-10-12', 75);
insert into stu values(3, '程余地', '女', '2000-6-12', 80);
⑪查询stu表所有学生的信息¶
语法:SELECT 列名 | * FROM 表名;>
⑫修改stu表中所有学生的成绩,加10分特长分¶
语法: UPDATE 表名 SET 列=值,列=值,列=值...[WHERE子句];
⑬修改stu表中编号为1的学生成绩,将成绩改为83分。¶
update stu set score=83 where id=1; -- 只修改编号为1的学生的成绩
update stu set id=22, name='小老板', gender='女', birthday='1985-7-7', score=88 where id=2;
提示
where子句用于对记录进行筛选过滤,保留符合条件的记录,将不符合条件的记录剔除。
⑭删除stu表中所有的记录¶
删除记录语法: DELETE FROM 表名 [where子句];
仅删除符合条件的
4️⃣单表查询¶
准备数据: 以下练习将使用db10库中的表及表记录,请先进入db10数据库!!
------------------------------------
-- 创建db10库、emp表并插入记录      --
------------------------------------
-- 删除db10库(如果存在)
drop database if exists db10;
-- 重新创建db10库
create database db10 charset utf8;
-- 选择db10库
use db10;
-- 删除员工表(如果存在)
drop table if exists emp;
-- 创建员工表
create table emp(
    id int primary key auto_increment,  -- 员工编号
    name varchar(50),                   -- 员工姓名
    gender char(1),                     -- 员工性别
    birthday date,                      -- 员工生日
    dept varchar(50),                   -- 所属部门
    job varchar(50),                    -- 所任职位
    sal double,                         -- 薪资
    bonus double                        -- 奖金
);
-- 往员工表中插入记录
INSERT INTO `emp` VALUES (null, '思想聚焦', '男', '1995-03-25', '计科一部', '讲师', 1800, 400);
INSERT INTO `emp` VALUES (null, '马伊琍', '男', '1994-04-06', '计科一部', '讲师', 2500, 700);
INSERT INTO `emp` VALUES (null, '郭艾伦', '女', '1996-06-14', '计科一部', '讲师', 1400, 400);
INSERT INTO `emp` VALUES (null, '赵三江', '男', '1991-05-18', '计科一部', '总监', 4500, 600);
INSERT INTO `emp` VALUES (null, '牛刘殴', '男', '1993-11-18', '计科一部', '讲师', 2600, 600);
INSERT INTO `emp` VALUES (null, '张正', '女', '1998-07-18', '就业部', '讲师', 3700, 700);
INSERT INTO `emp` VALUES (null, '苍蒹葭', '男', '1995-08-18', '就业部', '校长', 4850, 500);
INSERT INTO `emp` VALUES (null, '范不找', '男', '1999-09-18', '就业部', '讲师', 3200, 700);
INSERT INTO `emp` VALUES (null, '钱涛', '男', '1990-10-18', '就业部', '讲师', 2700, 500);
INSERT INTO `emp` VALUES (null, '马化腾', '男', '1980-12-18', NULL, 'CEO', 5000, null);
INSERT INTO `emp` VALUES (null, '五花马', '男', '1988-02-05', '计科一部', '讲师', 3200, 300);
INSERT INTO `emp` VALUES (null, '李虎', '男', '1989-01-11', '计科一部', '讲师', 4200, 500);
drop database if exists db10;
create database db10 charset utf8;
use db10;
drop table if exists emp;
create table emp(
    id int primary key auto_increment,
    name varchar(50),
    gender char(1),
    birthday date,
    dept varchar(50),
    job varchar(50),
    sal double,
    bonus double
);
INSERT INTO `emp` VALUES (null, '思想聚焦', '男', '1995-03-25', '计科一部', '讲师', 1800, 400);
INSERT INTO `emp` VALUES (null, '马伊琍', '男', '1994-04-06', '计科一部', '讲师', 2500, 700);
INSERT INTO `emp` VALUES (null, '郭艾伦', '女', '1996-06-14', '计科一部', '讲师', 1400, 400);
INSERT INTO `emp` VALUES (null, '赵三江', '男', '1991-05-18', '计科一部', '总监', 4500, 600);
INSERT INTO `emp` VALUES (null, '牛刘殴', '男', '1993-11-18', '计科一部', '讲师', 2600, 600);
INSERT INTO `emp` VALUES (null, '张正', '女', '1998-07-18', '就业部', '讲师', 3700, 700);
INSERT INTO `emp` VALUES (null, '苍蒹葭', '男', '1995-08-18', '就业部', '校长', 4850, 500);
INSERT INTO `emp` VALUES (null, '范不找', '男', '1999-09-18', '就业部', '讲师', 3200, 700);
INSERT INTO `emp` VALUES (null, '钱涛', '男', '1990-10-18', '就业部', '讲师', 2700, 500);
INSERT INTO `emp` VALUES (null, '马化腾', '男', '1980-12-18', NULL, 'CEO', 5000, null);
INSERT INTO `emp` VALUES (null, '五花马', '男', '1988-02-05', '计科一部', '讲师', 3200, 300);
INSERT INTO `emp` VALUES (null, '李虎', '男', '1989-01-11', '计科一部', '讲师', 4200, 500);
↓↓↓基础查询↓↓↓
⑮查询emp表中的所有员工,显示姓名,薪资,奖金¶
⑯查询emp表中的所有部门和职位¶
select dept, job from emp;-- 上面的查询结果中有很多重复的记录,如何剔除重复记录,只保留一条?
select distinct dept, job from emp;-- distinct用于去除重复记录,只保留一行!
↓↓↓WHERE子句查询↓↓↓
⑰查询emp表中【薪资大于3000】的所有员工,显示员工姓名、薪资¶
⑱查询emp表中【总薪资(薪资+奖金)大于3500】的所有员工,显示员工姓名、总薪资¶
方式一: 将所有的奖金为null的列值更新为0(一般不动原数据)¶
方式二: 在查询时将null值看做0来处理(这种方式对表中的数据不产生任何影响)¶
ifnull(列名, 值)函数: 判断指定的列是否包含null值,如果有null值,用第二个值替换null值
注意查看上面查询结果中的表头,如何将表头中的 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;
⑲查询emp表中【薪资在3000和4500之间】的员工,显示员工姓名和薪资¶
select name,sal from emp where sal>3000 and sal<4500;
-- and是并的意思(相当于java中的&),要求表中的记录要同时满足and两边的条件才算满足条件!
select name,sal from emp where sal>=3000 and sal<=4500;
-- 也可以使用`between 值1 and 值2` 来完成, 表示判断某个列的值是否在值1和值2之间(包含值1也包含值2)
select name,sal from emp where sal between 3000 and 4500;
⑳查询emp表中【薪资为 1400、1600、1800】的员工,显示员工姓名和薪资¶
select name,sal from emp where sal=1400 or sal=1600 or sal=1800;
-- sal in(1400,1600,1800),意思时只要员工的薪资等于in括号里面的任何一个值就算满足条件
select name,sal from emp where sal in(1400,1600,1800);
㉑查询薪资不为1400、1600、1800的员工,显示员工姓名和薪资¶
select name,sal from emp where !(sal=1400 or sal=1600 or sal=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);
㉒(练习题) 查询emp表中薪资大于4000和薪资小于2000的员工,显示员工姓名、薪资。¶
㉓(练习题) 查询emp表中薪资大于3000并且奖金小于600的员工,显示员工姓名、薪资、奖金。¶
答案
㉔查询没有部门的员工(即部门列为null值)¶
思考:如何查询有部门的员工(即部门列不为null值)
↓↓↓模糊查询(like)↓↓↓
like可以用于做模糊查询(可以按照指定的模式进行搜索),需要结合%和_使用
知识点
在通过like进行模糊查询时,如果不配合 % 以及 _ 使用,和 = 作用相同
% : 是通配符,可以表示 0个或多个任意字符
_ : 是通配符,只能表示 1个任意字符
㉕查询emp表中姓名中以"刘"字开头的员工,显示员工姓名。¶
㉖查询emp表中姓名中包含"涛"字的员工,显示员工姓名。¶
%刘%,可以匹配以下三种情况
- 当第一个%匹配0个字符时,表示以'刘'开头
 - 第二个%匹配0个字符时,表示以'刘'结尾
 - 当前后两个%至少匹配一个字符时,包含刘在中间的某一个位置(既不在开头,也不再结尾的情况)
 
㉗查询emp表中姓名以"刘"开头,并且姓名为两个字的员工,显示员工姓名。¶
select name from emp where name like '刘_';
-- 刘_,可以匹配姓名中以'刘'开头,并且刘的后面只能有一个字符的情况
select name from emp where name like '刘__';
-- 刘__,可以匹配姓名中以'刘'开头,并且刘的后面只能有两个字符的情况
↓↓↓多行函数查询↓↓↓
知识点
多行函数也叫做聚合函数(聚集函数), 常见的多行函数有(多行函数会默认过滤null值,即不统计null值):
count(列名): 表示统计当前列的值有多少个(不统计null值)count(*): 以行为单位,统计查询结果中有多少行记录max(列名): 表示统计当前这一列中所有值中的最大值min(列名): 表示统计当前这一列中所有值中的最小值sum(列名): 表示统计当前这一列中所有值的和(也就是说会将这一列中所有的值加在一起返回)avg(列名): 表示统计当前这一列中所有值的平均值(这一列中所有值的和 / 不是null值的个数 )
㉘统计[emp表中薪资大于3000的员工]个数¶
select count(*) from emp where sal>3000;
select count(*), name from emp where sal>3000;-- 在进行统计时,不要添加额外的列,因为没有任何意义(这里的name只会将第一行的name显示出来)
㉙求emp表中的最高薪资¶
select max(sal) from emp;
select min(sal) from emp;
-- 在进行统计时,不要添加额外的列,因为没有任何意义(这里的name也是只会将第一行的name显示出来)
-- 这里的最高薪资和姓名没有任何关系, 薪资是所有薪资中的最大值,但name就是结果中的第一行的name
select max(sal), name from emp;
㉚统计emp表中所有员工的薪资总和(不包含奖金)¶
select sum(sal) from emp; -- 所有员工薪资总和
select sum(bonus) from emp; -- 所有员工奖金总和
-- 虽然bonus列中有null值,但是在通过多行函数统计时,遇到null会直接剔除,不会参与统计!
㉛统计emp表员工的平均薪资(不包含奖金)¶
select avg(sal) from emp; -- 所有薪资的平均值
select avg(bonus) from emp; -- 所有奖金的平均值
select sum(bonus) / count(bonus) from emp; -- 所有奖金的平均值
(练习题)统计emp表员工的平均总薪资(包含奖金)¶
答案
不分组使用多行函数 和 分了组使用多行函数 的区别?
-- 如果没有分组,其实在使用count统计时会默认将整个查询结果当成一个组,
-- 这样的话,统计这一个组的人数,返回的就是一个数值;
select count(*) from emp;
-- 如果分了组(按照部门分了3组), 再使用count统计时会根据每个组来进行统计,有多少个组,
-- 就会统计出多少个结果(现有3个组,因此会统计出三个组的人数)
select count(*) from emp group by dept;
-- 如果没有分组, 再通过多行函数进行统计时, 不要显示额外的列, 
-- 因为没有任何意义(比如,上面的dept只会将第一行的dept显示出来, 和前面统计的人数没有任何关系)
select count(*), dept from emp
-- 如果分了组, 再通过多行函数进行统计时, 可以将进行分组的列和多行函数一起显示
-- 比如:按照dept分了组(分成了3组,此时每组中的dept是相同), 可以将dept这一列的值显示出来, 
-- 因为是根据这个列分的组,每组中的这个列的值也是相同的,所以显示这个组中的dept列的任意一个值都是一样的。
select count(*), dept from emp group by dept;
↓↓↓分组查询↓↓↓
语法:SELECT 列 | * FROM 表名 [WHERE子句] GROUP BY 列;
㉜对emp表,按照部门对员工进行分组,查看分组后效果。¶
select id,name,dept from emp group by dept;
-- 如何证明上面查询的结果是三组,而不是三条记录?
-- 可以通过多行函数对分组后的数据进行统计,分成几组,就会统计出几个结果。
select count(*) from emp group by dept;
㉝对emp表按照职位进行分组,并统计每个职位的人数,显示职位和对应人数¶
+----+----------+------+
| id | name     | job  |
+----+----------+------+
| 10 | 马化腾   | CEO   |
|  4 | 赵三江   | 总监   |
|  7 | 苍蒹葭   | 校长   |
|  1 | 思想聚焦 | 讲师   |
+----+----------+------+
显示职位和对应人数
+----------+------+
| count(*) | job  |
+----------+------+
|        1 | CEO  |
|        1 | 总监 |
|        1 | 校长 |
|        9 | 讲师 |
+----------+------+
㉞对emp表按照部门进行分组,求每个部门的最高薪资(不包含奖金),显示部门名称和最高薪资¶
+----+----------+------+----------+
| id | name     | sal  | dept    |
+----+----------+------+----------+
| 10 | 马化腾   | 5000 | NULL     |
|  6 | 张正     | 3700 | 就业部    |
|  1 | 思想聚焦 | 1800 | 计科一部   |
+----+----------+------+----------+
显示部门名称和最高薪资
+----------+----------+
| max(sal) | dept     |
+----------+----------+
|     5000 | NULL     |
|     4850 | 就业部   |
|     4500 | 计科一部 |
+----------+----------+
↓↓↓排序查询↓↓↓
语法:SELECT 列名 FROM 表名 [where子句] [group by 列] ORDER BY 列名 [ASC|DESC] ASC(默认)升序,即从低到高;DESC 降序,即从高到低。
㉟对emp表中所有员工的薪资进行升序(从低到高)排序,显示员工姓名、薪资。¶
select name, sal from emp order by sal;     -- 默认是asc,就是升序
select name, sal from emp order by sal asc; -- 默认是asc,就是升序
㊱对emp表中所有员工的奖金进行降序(从高到低)排序,显示员工姓名、奖金。¶
select name, bonus from emp order by bonus desc; -- 降序必须写desc
-- 按照奖金降序排序,如果奖金相同,再按照薪资降序排序
select name, bonus, sal from emp order by bonus desc, sal desc; 
↓↓↓分页查询↓↓↓
在mysql中,通过limit进行分页查询,查询公式为:¶
㊲查询emp表中的所有记录,分页显示:每页显示3条记录,返回所有页的数据¶
-- 查询emp表中的记录,每页3条,查询第1页
select * from emp limit 0, 3;
-- 查询emp表中的记录,每页3条,查询第2页
select * from emp limit 3, 3;
-- 查询emp表中的记录,每页3条,查询第3页
select * from emp limit 6, 3;
-- 查询emp表中的记录,每页3条,查询第4页
select * from emp limit 9, 3;
㊳求emp表中薪资最高的前3名员工的信息,显示姓名和薪资¶
-- 根据薪资降序排序(从高到低), 第一条就是薪资最高的记录
select name, sal from emp order by sal desc;
-- 在上面查询的基础上, 将第一条取出来(每页显示1条,查询第1页)
select name, sal from emp order by sal desc limit 0,1;
-- 根据薪资降序排序, 每页显示3条, 查询第一页就是薪资最高的前3名
select name, sal from emp order by sal desc limit 0,3;
↓↓↓其他函数↓↓↓
curdate() -- 获取当前日期: 年月日
curtime() -- 获取当前时间: 时分秒
sysdate()/now() -- 获取当前日期+时间, 年月日 时分秒
year('2020-8-10'): 返回日期中的年份, 2020
month('2020-8-10'): 返回日期中的月份, 8
day('2020-8-10'): 返回日期中的天数, 10
hour('2020-8-10 12:34:56'): 返回时间中的小时, 12
minute('2020-8-10 12:34:56'): 返回时间中的分钟数, 34
second('2020-8-10 12:34:56'): 返回时间中的秒值, 56
concat(s1,s2,...sn): 将 s1、s2、...sn 拼接在一起返回
例如: name('王二小'), birthday('1995-03-25'), sal(2450)
concat_ws(x,s1,s2,...sn): 
将 s1、s2、...sn 拼接在一起,并且每两个拼接时会通过x作为分隔符进行拼接,再返回
㊴查询emp表中所有【在1993和1995年之间出生】的员工,显示姓名、出生日期。¶
-- 错误,由于birthday是日期类型(年月日格式),而1993和1995都是数值,没法比较
select name, birthday from emp where birthday between 1993 and 1995;
-- 方式一: 将1993和1995两个数值转成日期格式,再和birthday进行比较!
select name, birthday from emp where birthday between '1993-1-1' and '1995-12-31';
-- 方式二: 将birthday中的年份用year函数提取出来,再和1993以及1995进行比较
select name, birthday from emp where year(birthday) between 1993 and 1995;
㊵查询emp表中本月过生日的所有员工¶
-- 首先通过month函数从当前日期中获取本月是几月: month( now() )
-- 再通过month函数从员工的birthday中获取出生月份: month( birthday )
select * from emp where month( now() ) = month( birthday );
㊶查询emp表中员工的姓名和薪资(薪资格式为: xxx(元) )¶
补充练习:查询emp表中员工的姓名和薪资(薪资格式为: xxx/元 )¶
补充内容1: mysql的数据类型¶
1.1:数值类型¶
mysql中提供了多种数值类型,其中包括: tinyint、smallint、int、bigint、float、double、decimal等,其中较为常用的就是 int、double
1.2.字符串类型¶
1️⃣char类型: 定长字符串, char(n), n的范围是: 0~255个字符
char类型之所以叫做定长字符串,是因为一旦确定了n的最大字符数,不管存的数据是多少,该数据占用的空间就是n个字符。
例如:name char(10), 存入'张三丰', 存入了3个字符,剩余的空间会用空格补全.
因此char类型可能会浪费空间!
所以 char类型适合存储长度固定的数据 , 比如
student_id char(11), 用这个列存储所有学生的编号.
idcard char(18), 用这个列存储所有人的身份证号.
char类型相比varchar类型速度要快一些,因为char类型只需要判断一个数据是否能存入该列中,而不需要将剩余的空间留给别的数据使用!
2️⃣varchar类型: 变长字符串, varchar(n), n的范围是: 0~?个字符
varchar类型之所以叫变长字符串,是因为n只是限制该列中最多能存的字符数, 如果你实际存的数据量小于n,
剩余的空间还可以留给别的数据使用。例如:name char(10),  存入'张三丰', 存入了3个字符,剩余的7个空间会留给别的数据使用!
因此varchar类型不会浪费空间!
所以 varchar类型适合存储长度不固定的数据(长度固定的数据我们会使用char类型)
varchar类型最大能存的数据量是 0~65535个字节
3️⃣编码
latin1编码中,1个字符对应1个字节, n的最大值约是 65535/1 (max = 65532)
gbk编码中,1个字符对应2个字节, n的最大值约是 65535/2 (max = 32766)
utf8编码中,1个字符对应3个字节, n的最大值约是 65535/3 (max = 21844)
面试题: char和varchar有什么区别?
char和varchar存的数量是不同的, char类型最多能存255个字符,varchar类型最多能存65535个字节char类型如果存的数据量小于最大长度, 剩余的空间会使用空格填充, 因此可能会浪费空间,所以char类型适合存储长度固定的数据, 这样既不会浪费空间, 效率还比varchar略高varchar类型如果存的数据量小于最大长度, 剩余的空间会留给别的数据使用,所以varchar类型适合存储长度不固定的数据, 这样虽然没有char存储效率高, 但至少不会浪费空间。
1.3: 日期类型¶
date: 日期类型, 格式是: 年月日time: 时间类型, 格式是: 时分秒datetime: 日期+时间,格式是: 年月日 时分秒timestamp: 时间戳, 格式和datetime相同, 也是: 年月日 时分秒, 和datetime不同的是:- 1) 范围上: 
datetime范围是: 1000~9999(年份),timestamp范围是: 1970到2038年 - 2) 实际存的数据: 
datetime实际存的就是一个年月日 时分秒格式的日期+时间,而timestamp实际存储的是这个从1970年1月1日到这个日期+时间的时间毫秒值 - 3) 在使用上: 
timestamp可以设置自动获取当前时间作为值插入到表中, 而datetime不可以. 
- 1) 范围上: 
 
补充内容2: mysql的字段约束¶
测试用SQL
use mydb;
drop table if exists stu;
create table stu(
    id int primary key, -- id是主键,值不能为空且不能重复
    name varchar(50),   -- 50表示最多存50个字符
    gender varchar(10), 
    birthday date,
    score double
);
insert into stu value('a', '张飞', '男', '1980-1-2', 80);
insert into stu value('b', '刘备', '男', '1981-2-2', 90);
insert into stu value('c', '关羽', '男', '1982-3-2', 85);
2.1: 主键约束¶
如果一个列添加了主键约束, 那么这个列的值就必须是 非空的且不能重复 主键通常用于唯一的表示一行表记录(就像人的身份证号一样) 一张表中通常都会有且只有一个主键
添加主键约束的格式:
如果id是主键并且是数值类型,为了方便维护,可以设置 主键自增策略 ,设置方法:
在设置完主键自增之后,表中会维护一个AUTO_INCREMENT的值,这个值从1开始,如果插入主键时没有给主键赋值,就会从AUTO_INCREMENT这里获取一个值再作为主键插入到表中。再用完之后,会自动将AUTO_INCREMENT的值加1
2.2: 非空约束¶
如果一个列添加了非空约束, 那么这个列的值就 不能为空(null), 但可以重复
添加非空约束的格式:
2.3:唯一约束¶
如果一个列添加了唯一约束, 那么这个列的值就 不能重复, 但可以为空(null) 比如: 网站绑定的邮箱, 前期可以不绑定, 即可以为null, 但一旦绑定, 这个邮箱是不能和其他账号的邮箱重复的。
添加唯一约束的格式:
create table stu(
    ...
    email varchar(50) unique,
    ...
);
create table stu(
    ...
    username varchar(50) unique not null, -- 用户名既不能重复,也不能为空
    ...
);
主键约束 和 (非空+唯一约束) 有什么区别?
主键约束 和 非空+唯一 特点是相同的, 都是不能为空且不能重复
主键约束除了非空且不能重复之外, 还可以表示唯一一行表记录, 即作为表记录的唯一标识。
补充内容3: mysql的外键约束¶
外键约束不同于主键、非空、唯一约束,外键约束是用于表示两张表的对应关系
1、如何保存部门(dept) 和 员工(emp)的对应关系?¶
      可以在员工表中添加一个列(比如: dept_id)用于保存部门的编号, 就可以保存员工和部门的对应关系(可以将dept_id设置为外键, 当然也可以不这样做)
2、添加外键和不添加外键有什么区别?¶
- 如果不添加外键:对于数据库来说,
dept_id这个列就是一个普通的列,数据库也不会知道 dept 和 emp两张表存在任何关系, 自然也不会帮我们去维护这层关系。假如,现在要删除dept表中的某一个部门(4号部门), 删除后就会造成emp表中的赵六和刘能找不到部门,而赵六和刘能后面对应的dept_id值为4, 这个数据也变成了冗余数据。 这样会破坏数据库中数据的完整性和一致性! - 如果将
dept_id添加为外键:将dept_id添加为外键就等同于:通知数据库 dept 和 emp 两张表存在对应关系, 而且emp表中的dept_id列要参考dept表中的id列。这样数据库就会帮我们维护这两张表的对应关系, 如果此时删除dept表中的某一个部门(4号部门),数据库会首先检查这个4号部门在emp表中还有没有对应的员工, 如果有,数据库就会阻止我们删除!这样就保证了数据的完整性和一致性。如果非要删除, 可以将4号部门里面的员工记录转移到其他部门或者删除,只要保证所删除部门中没有对应的员工即可! 
补充内容4: 表关系¶
- 1对多(多对1): 在这种关系中,往往会在多的一方添加列,保存一的一方的主键(可以设置外键,当然也可以不设置,看需求),比如:
部门表(1) 员工表(), 在员工表()中添加列(dept_id)保存部门的编号
班级表(1) 学生表(), 在学生表()中添加列(class_id)保存班级的编号 - 1对1: 在这种关系中,可以在任意一方添加列保存另一方的主键(可以设置外键,当然也可以不设置,看需求), 比如:
班级表(1) 教室表(1), 在班级表(1)添加列(room_id)来保存教室的编号
班级表(1) 教室表(1), 在教室表(1)添加列(class_id)来保存班级的编号 - 多对多: 在这种关系中,在任何一方添加列保存另一方的主键都不合适
此时可以再创建一张表,在这种表中分别添加两个列(stuid,teaid), 分别用于保存学生表的主键和教师表的主键, 以此来保存学生和教师的对应关系! 
5️⃣多表查询¶
准备数据: 以下练习将使用db30库中的表及表记录,请先进入db30数据库!!!
---------------------------------------
-- 创建db30库、dept表、emp表并插入记录 --
---------------------------------------
-- 删除db30库(如果存在)
drop database if exists db30;
-- 重新创建db30库
create database db30 charset utf8;
-- 选择db30库
use db30;
-- 删除部门表, 如果存在
drop table if exists dept;
-- 重新创建部门表, 要求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, '销售部');
-- 删除员工表, 如果存在
drop table if exists emp;
-- 创建员工表(员工编号、员工姓名、所在部门编号)
create table emp(
    id int primary key auto_increment,  -- 员工编号
    name varchar(20),                   -- 员工姓名
    dept_id int                         -- 部门编号
);
-- 往员工表中插入记录
insert into emp values(null, '张三', 1);
insert into emp values(null, '李四', 2);
insert into emp values(null, '老王', 3);
insert into emp values(null, '赵六', 5);
drop database if exists db30;
create database db30 charset utf8;
use db30;
drop table if exists dept;
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, '销售部');
drop table if exists emp;
create table emp(
    id int primary key auto_increment,
    name varchar(20),
    dept_id int
);
insert into emp values(null, '张三', 1);
insert into emp values(null, '李四', 2);
insert into emp values(null, '老王', 3);
insert into emp values(null, '赵六', 5);
↓↓↓内连接查询↓↓↓
㊷查询部门和部门对应的员工信息¶
以上查询有一个名字叫做笛卡尔积查询
笛卡尔积查询
其实就是同时查询两张表,其中一张表有m条记录,另外一张表有n条记录,查询的结果是 m*n 条,
但这种查询结果中包含大量错误的数据,所以我们一般不会直接使用这种查询。
在笛卡尔积查询的基础上,通过where子句将错误的数据剔除,只保留正确的数据,这就是 连接查询。
这条是内连接查询:
↓↓↓外连接查询↓↓↓
㊸查询【所有部门】及部门对应的员工,如果某个部门下没有员工,员工显示为null¶
如果两张表在连接查询时,要求查询出其中一张表的所有数据,此时可以使用左外连接查询或者右外连接查询。
-- 如果要查询部门表(dept)中的所有数据,而部门表(dept)在左边,
-- 此时可以使用左外连接查询,就可以查询出所有的部门信息(而员工信息只显示和部门对应的)
select * from dept left join emp on emp.dept_id = dept.id;
左外连接查询
可以将左边表中的所有记录都查询出来,右边表只显示和左边相对应的数据,如果左边表中某些记录在右边没有对应的数据,右边显示为null即可。
㊹查询【所有员工】及员工所属部门,如果某个员工没有所属部门,部门显示为null¶
右外连接查询
可以将右边表中的所有记录都查询出来,左边表只显示和右边相对应的数据,如果右边表中某些记录在左边没有对应的数据,可以显示为null。
查询所有部门以及所有员工, 如果部门没有对应员工,可以对应null,如果员工没有对应部门,也可以对应null,这种情况应使用 全外连接查询。
但,mysql不支持全外连接查询!但可以通过union来模拟这种查询!
union关键字是用于 将两个查询结果上下合并在一起显示,并且会去除重复记录。
union all关键字是用于 将两个查询结果上下合并在一起显示,不会去除重复记录。
能够使用union和union all合并结果的查询语句,必须符合:
- 两条SQL语句查询的结果列数必须相同
 - 两条SQL语句查询的结果列名必须相同(低版本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;
select * from dept left join emp on emp.dept_id=dept.id
union all
select * from dept right join emp on emp.dept_id=dept.id;
Attention!¶
db30库中有两张表:dept和emp表,两者有外键约束
🚫🚫🚫认真比较一下下面六个查询语句和查询结果的对应关系🚫🚫🚫
+----+--------+------+------+---------+
| id | name  | id   | name | dept_id |
+----+--------+------+------+---------+
|  1 | 财务部 |    1 | 张三 |       1 |
|  2 | 人事部 |    2 | 李四 |       2 |
|  3 | 科技部 |    3 | 老王 |       3 |
|  4 | 销售部 | NULL | NULL |    NULL |
+----+--------+------+------+---------+
+----+------+---------+------+--------+
| id | name | dept_id | id   | name  |
+----+------+---------+------+--------+
|  1 | 张三 |       1 |    1 | 财务部 |
|  2 | 李四 |       2 |    2 | 人事部 |
|  3 | 老王 |       3 |    3 | 科技部 |
|  4 | 赵六 |       5 | NULL | NULL  |
+----+------+---------+------+--------+
+------+--------+----+------+---------+
| id   | name  | id | name | dept_id |
+------+--------+----+------+---------+
|    1 | 财务部 |  1 | 张三 |       1 |
|    2 | 人事部 |  2 | 李四 |       2 |
|    3 | 科技部 |  3 | 老王 |       3 |
| NULL | NULL  |  4 | 赵六 |       5 |
+------+--------+----+------+---------+
+------+------+---------+----+--------+
| id   | name | dept_id | id | name  |
+------+------+---------+----+--------+
|    1 | 张三 |       1 |  1 | 财务部 |
|    2 | 李四 |       2 |  2 | 人事部 |
|    3 | 老王 |       3 |  3 | 科技部 |
| NULL | NULL |    NULL |  4 | 销售部 |
+------+------+---------+----+--------+
准备数据:以下练习将使用db40库中的表及表记录,请先进入db40数据库!!!
-- -----------------------------------
-- 创建db40库、dept表、emp表并插入记录
-- -----------------------------------
-- 删除db40库(如果存在)
drop database if exists db40;
-- 重新创建db40库
create database db40 charset utf8;
-- 选择db40库
use db40;
-- 创建部门表
create table dept(              -- 创建部门表
    id int primary key,         -- 部门编号
    name varchar(50),           -- 部门名称
    loc varchar(50)             -- 部门位置
);
-- 创建员工表
create table emp(               -- 创建员工表
    id int primary key,         -- 员工编号
    name varchar(50),           -- 员工姓名
    job varchar(50),            -- 职位
    topid int,                  -- 直属上级
    hdate date,                 -- 受雇日期
    sal int,                    -- 薪资
    bonus int,                  -- 奖金
    dept_id int,                -- 所在部门编号
    foreign key(dept_id) references dept(id)
);
-- 往部门表中插入记录
insert into dept values ('10', '计科一部', '北京');
insert into dept values ('20', '软科二部', '上海');
insert into dept values ('30', '大数据部', '广州');
insert into dept values ('40', '通信部', '深圳');
-- 往员工表中插入记录
insert into emp values ('1001', '王老五', '办事员', '1007', '1990-12-17', '800', 500, '20');
insert into emp values ('1003', '李白', '分析员', '1011', '1991-02-20', '1900', '300', '10');
insert into emp values ('1005', '杜甫', '推销员', '1011', '1991-02-22', '2450', '600', '10');
insert into emp values ('1007', '刘青松', '经理', '1017', '1991-04-02', '3675', 700, '20');
insert into emp values ('1009', '郭沫若', '推销员', '1011', '1991-09-28', '1250', '1400', '10');
insert into emp values ('1011', '陈羊', '经理', '1017', '1991-05-01', '3450', 400, '10');
insert into emp values ('1013', '红军', '办事员', '1011', '1991-06-09', '1250', 800, '10');
insert into emp values ('1015', '钱德', '分析员', '1007', '1997-04-19', '3000', 1000, '20');
insert into emp values ('1017', '马多云', '董事长', null, '1991-11-17', '5000', null, null);
insert into emp values ('1019', '刘牛殴', '推销员', '1011', '1991-09-08', '1500', 500, '10');
insert into emp values ('1021', '方不找', '办事员', '1007', '1997-05-23', '1100', 1000, '20');
insert into emp values ('1023', '赵匡胤', '经理', '1017', '1991-12-03', '950', null, '30');
insert into emp values ('1025', '白朴', '分析员', '1023', '1991-12-03', '3000', 600, '30');
insert into emp values ('1027', '叶子叶', '办事员', '1023', '1992-01-23', '1300', 400, '30');
drop database if exists db40;
create database db40 charset utf8;
use db40;
create table dept(
    id int primary key,
    name varchar(50),
    loc varchar(50)
);
create table emp(
    id int primary key,
    name varchar(50),
    job varchar(50),
    topid int,
    hdate date,
    sal int,
    bonus int,
    dept_id int,
    foreign key(dept_id) references dept(id)
);
insert into dept values ('10', '计科一部', '北京');
insert into dept values ('20', '软科二部', '上海');
insert into dept values ('30', '大数据部', '广州');
insert into dept values ('40', '通信部', '深圳');
insert into emp values ('1001', '王老五', '办事员', '1007', '1990-12-17', '800', 500, '20');
insert into emp values ('1003', '李白', '分析员', '1011', '1991-02-20', '1900', '300', '10');
insert into emp values ('1005', '杜甫', '推销员', '1011', '1991-02-22', '2450', '600', '10');
insert into emp values ('1007', '刘青松', '经理', '1017', '1991-04-02', '3675', 700, '20');
insert into emp values ('1009', '郭沫若', '推销员', '1011', '1991-09-28', '1250', '1400', '10');
insert into emp values ('1011', '陈羊', '经理', '1017', '1991-05-01', '3450', 400, '10');
insert into emp values ('1013', '红军', '办事员', '1011', '1991-06-09', '1250', 800, '10');
insert into emp values ('1015', '钱德', '分析员', '1007', '1997-04-19', '3000', 1000, '20');
insert into emp values ('1017', '马多云', '董事长', null, '1991-11-17', '5000', null, null);
insert into emp values ('1019', '刘牛殴', '推销员', '1011', '1991-09-08', '1500', 500, '10');
insert into emp values ('1021', '方不找', '办事员', '1007', '1997-05-23', '1100', 1000, '20');
insert into emp values ('1023', '赵匡胤', '经理', '1017', '1991-12-03', '950', null, '30');
insert into emp values ('1025', '白朴', '分析员', '1023', '1991-12-03', '3000', 600, '30');
insert into emp values ('1027', '叶子叶', '办事员', '1023', '1992-01-23', '1300', 400, '30');
↓↓↓子查询练习↓↓↓
将一个SQL语句的执行结果作为另外一条SQL语句的条件来执行, 这就是子查询!
㊺列出薪资比'刘牛殴'的薪资高的所有员工,显示姓名、薪资¶
-- 求出薪资比'刘牛殴'的薪资还高的所有员工
select name, sal from emp where sal > (select sal from emp where name='刘牛殴');
㊻列出与'小老板'从事相同职位的所有员工,显示姓名、职位¶
-- 求出'刘牛殴'从事的职位
select job from emp where name='刘牛殴'; -- 推销员
-- 求出和'刘牛殴'具有相同职位的员工
select name, job from emp where job = (select job from emp where name='刘牛殴');
↓↓↓多表查询练习↓↓↓
㊼列出在'计科一部'任职的员工,假定不知道'计科一部'的部门编号,显示部门名称,员工名称。¶
/*
查询的列:select dept.name, emp.name
查询的表:from dept, emp
筛选条件:where emp.dept_id=dept.id and dept.name='计科一部'
*/
SELECT dept.name, emp.name
FROM dept, emp
WHERE emp.dept_id=dept.id AND dept.name='培优部';
------------------------------------------------------
SELECT dept.name, emp.name
FROM dept INNER JOIN emp
ON emp.dept_id=dept.id
WHERE dept.name='计科一部';
㊽(❗自查询❗)列出所有员工及其直接上级,显示员工姓名、上级编号,上级姓名¶
/* emp e1(员工表) emp e2(上级表)
查询的列: SELECT e1.name, e1.topid, e2.name
查询的表: FROM emp e1, emp e2
筛选条件: WHERE e1.topid=e2.id
*/
SELECT e1.name, e1.topid, e2.name
FROM emp e1, emp e2
WHERE e1.topid=e2.id;
㊾列出最低薪资大于1500的各种职位,显示职位和该职位的最低薪资¶
列出职位: 
1)求出各种职位的最低薪资 先按照职位进行 分组 ,职位相同为一组,再用 min(sal) 求每组中的最低薪资,也就是每种职位的最低薪资¶
2)求出有哪些职位的最低薪资是大于1500的¶
where应在放在from子句后,group by子句前where中不能使用多行函数(列别名也不能用在where中)where是在分组之前之前,先过滤掉一些记录,再基于剩余的记录进行分组, 而本地是先分组,再过滤,所以不能使用where,应该用having
select job, min(sal) from emp group by job where min(sal)>1500; -- 错误写法!
select job, min(sal) from emp group by job having min(sal)>1500;-- 正确写法
3)where和having的区别?¶
where和having都是用于 对表中的记录进行筛选过滤
where用于在分组之前对记录进行筛选过滤,而having用于对分组之后的记录进行筛选过滤
where子句中不能使用多行函数 和 列别名,但可以使用表别名!
having子句中可以使用多行函数 和 列别名 以及 表别名!
㊿列出在每个部门就职的员工数量、平均工资。显示部门编号、员工数量,平均薪资。¶
select dept_id, count(*), avg(sal) from emp group by dept_id;
select dept_id 部门编号, count(*) 员工数量, avg(sal) 平均薪资 from emp group by dept_id;
51.列出受雇日期早于直接上级的所有员工,显示员工编号、员工姓名、部门名称、上级编号、上级姓名。¶
/* 
emp e1(员工表), emp e2(上级表)
查询的列:SELECT e1.id, e1.name, d.name, e2.id, e2.name
查询的表:FROM emp e1, emp e2, dept d
连接条件:WHERE e1.topid=e2.id AND e1.dept_id=d.id
筛选条件:AND e1.hdate < e2.hdate
*/
SELECT e1.id, e1.name, d.name, e2.id, e2.name
FROM emp e1, emp e2, dept d
WHERE e1.topid=e2.id
AND e1.dept_id=d.id
AND e1.hdate < e2.hdate;
补充内容5: set names gbk;¶
(这个命令是用在cmd窗口中)用来通知数据库服务器, 当前cmd窗口发送给服务器的数据的GBK的,那么服务器就会按照GBK编码来接收 cmd窗口发送过来的数据, 再将GBK的数据转换成utf8编码的数据存入数据库中!
这个命令只能用在cmd窗口! 而且是每次新开一个cmd窗口都需要重新设置一次!
像Navicat/SQLYog等工具不需要设置该命令,因为这些工具底层已经设置过编码了!
补充内容6: 数据库的备份和恢复¶
↓↓↓备份数据库↓↓↓
①备份单个数据库¶
在cmd窗口(未登录、未连接到mysql服务器的界面)中,可以通过如下命令对指定的数据库进行备份:
示例1: 对db40库中的数据(表,表记录)进行备份,备份到 d:/db40.sql 文件中
②备份多个数据库(比如备份两个数据库)¶
在cmd窗口中(已登录的状态下),可以通过source命令来执行指定位置的sql文件中的sql语句:
示例2: 将 d:/db40.sql 文件中的数据恢复到 db80 库中
1) 先创建db80库, 并进入到db80库
2) 再通过source命令执行 d:/db40.sql 文件中的sql语句
三、JDBC¶
1.1 什么是JDBC¶
JDBC(Java DataBase Connectivity) Java数据库连接
其实就是利用Java语言(Java程序)连接并访问数据库的一门技术
1.2 为什么要学习JDBC¶
之前我们可以通过 CMD窗口 或者 通过 Navicat/Sqlyog等软件 连接数据库, 对数据库中的数据进行增删改查操作.
但是,将来我们在企业开发中,更多的是通过程序来连接数据库, 而我们学的是Java开发,通过Java程序连接数据就必须用到JDBC这门技术!
像 Mybatis/hibernate/DBUtils/Spring JdbcTemplate 等框架底层也是在通过JDBC来连接数据库
1.3 如何通过JDBC连接数据库¶
概述¶
- 早期, 不同的数据库厂商提供的 数据库驱动包 (jar)是各不相同, 开发人员在操作不同 的数据库时需要学习该数据库对应的数据库驱动(jar)包, 这对于开发人员的学习成本太高了!
 - 后来SUN公司就规定了JDBC这套规范(其实其中包含了大量的接口), 要求不同的数据库厂商提供的驱动都来实现这套接口, 这样一来, 开发人员只需要学会这套接口, 所有的数据库就都会操作了!
 - JDBC中主要包含两个包(java.sql和javax.sql), 并且java中已经包含这两个包了,但除了JDBC的包之外, 我们在操作数据库时还需要导入该数据库对应的驱动包(jar包)
 
1.4 JDBC快速入门案例¶
1、准备所需要的数据库、表、表记录¶
drop database if exists db_demo;
create database db_demo charset utf8;
use db_demo;
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);
2、导入mysql驱动包(mysql-connector-java-8.0.11.jar)¶
找到 mysql-connector-java-8.0.11.jar 文件 并复制,将这个文件复制到项目/lib目录下, 注意,这里只是将jar文件复制过来了,但并没有添加到项目中, 所有程序中还是无法使用这个jar包,还需要在当前项目中引用这个jar包(IDEA和Eclipse操作不一,可以自行百度)
3、创建com.example.JdbcDemo类,在这个类中通过六个步骤完成连接数据库操作¶
/**
* JDBC的快速入门程序
*   查询jt_db库中account表中的所有记录
*   select * from account;
*/
public class JdbcDemo1 {
    public static void main(String[] args) throws Exception {
        //1.注册数据库驱动(可以省略,但建议加上)
        Class.forName( "com.mysql.cj.jdbc.Driver" );
        //2.获取数据库连接
        /*
        Connection conn = DriverManager.getConnection(
            "jdbc:mysql://localhost:3306/db_demo?characterEncoding=utf-  8&serverTimezone=Asia/Shanghai", "root", "root"); */
        Connection conn = DriverManager.getConnection(
                "jdbc:mysql:///db_demo?characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false", "root", "root");
        //3.获取传输器
        Statement stat = conn.createStatement();
        //4.发送sql到数据库执行,并返回执行结果
        ResultSet rs = stat.executeQuery( "select * from account" );
        System.out.println( rs );
        //5.处理结果(将查询的结果一行行输出到控制台)
        //rs.next 返回true表示下一行有数据, 就会进入循环获取下一行数据
        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();
    }
}
4、如何通过JDBC增删改数据¶
/* 新增表记录: 往account表中添加一条记录: null 'hellen' 3500 */
    @Test
    public void testAdd() throws Exception {
        //注册驱动
        Class.forName( "com.mysql.cj.jdbc.Driver" );
        //获取连接
        Connection conn = DriverManager.getConnection( 
            "jdbc:mysql:///db_demo?characterEncoding=utf-8&serverTimezone=Asia/Shanghai&useSSL=false", 
            "root", "root" );
        //获取传输器
        Statement stat = conn.createStatement();
        //执行SQL语句, 返回执行结果
        String sql = "insert into account value(null, 'hellen', 3500 )";
        int rows = stat.executeUpdate(sql); 
        //处理结果
        System.out.println( "影响的行数为: "+rows );
        //释放资源
        stat.close();
        conn.close();
    }
    --------------------------------------------
    /* 修改: 将name为'hellen'的金额修改为5000 */
    @Test
    public void testUpdate() {
    }
    --------------------------------------------
    /* 修改: 将name为'tom'的记录删除! */
    @Test
    public void testDelete() {
    }
5、JDBC连接数据库的优点和缺点¶
- 优点:使用
JDBC连接并访问数据库 相比使用第三方的框架连接访问数据库速度要快一些!因为这是最为传统,最为底层的方法。 - 缺点:
JDBC中包含大量重复的代码(比如每次连接数据库都需要 注册驱动、获取连接、获取传输器、处理结果、释放资源等),后期难以维护JDBC自身没有连接池,而框架(mybatis自带连接池)自带连接池,当需要连接直接从连接池中获取,用完连接不用关闭,再还回连接池中,这样可以提高执行效率!JDBC中执行select查询语句的结果需要开发人员自己手动处理, 如果是非常复杂的数据(比如查询的结果中列数非常多或者查询的数据来自多张表)处理起来是非常麻烦的,但框架可以帮我们处理!
 
6、连接池概念¶
- 如果不使用连接池: 用户每次需要连接访问数据库时, 都需要创建一个连接(Connection), 基于这个连接去访问数据库中的数据, 用完连接(Connection)后会将连接关闭(close), 其实每次创建连接和关闭连接(相比使用连接)需要消耗大量的时间和资源,导致程序的执行效率低下(特别是高并发的时候,比如京东618)
 - 如果使用连接池: 可以在程序一启动之后,就创建一批连接放在一个池中(容器), 当用户需要连接时, 不用自己创建, 而是从连接池中获取一个连接对象, 再基于这个连接对象去访问数据库, 用完连接后, 不用将连接关闭, 而是还回连接池中。这样一来,用户使用的都是池中的这一批连接,可以减少连接创建和关闭的次数,提高程序的执行效率!
 
1.5:数据库事务¶
1、什么是事务¶
DataBase Transaction(数据库事务) 简单的说: 事务就是将一堆的SQL语句绑定在一起执行, 执行结果是: 所有SQL都执行成功了才算成功, 但凡有一条失败, 就按全失败来处理(比如即使执行成功的语句,也会进行回滚,就是撤销当前的执行)。
以转账为例: A、B账户各有1000元,A给B转账100元 A账户减去100元:
B账户加上100元:
如果上面两条语句执行时,没有事务,如果第一条成功,但第二条失败了!
反之,如果第一条失败了,但第二条成功了!
所以如果想保证上面的两条SQL语句同时成功或者同时失败,可以将这两条SQL添加到一个事务中。
2、事务的四大特征(面试题)¶
- 原子性:原子曾被认为是最小单位,不能被分割, 这里的原子性其实是指:事务中的所有SQL是一个整体,不能被分割。不存在一部分SQL执行成功,而另一部分SQL执行失败,都执行成功才算成功,有一条失败就算失败!
 - 
一致性:在事务执行前后(不管事务最后是提交还是回滚)的业务数据之和是保持一致的!
 - 
隔离性:在事务并发时, 一个事务理论上看不到另外一个事务的状态, 也就是说事务之间是相互隔离开来的!
 - 
持久性:一旦事务提交之后, 事务中对数据的更新操作会持久的保存到数据库中(最终是更新到硬盘的数据文件里)。反过来说, 在事务提交之前, 对数据的更新操作只是一个临时的数据, 没有真正的去修改数据库。
 
