数据库的基本概念 | 英文:DataBase 简称 :DB
定义:用于存储和管理数据的仓库
特点:
·持久化存储数据(到硬盘中),速度快。其实数据库就是一个文件系统
·方便存储和管理数据
·使用了统一的方式操作数据库 -- SQL |
MySQL基本使用 | 【卸载】
·控制面板 - 卸载 - 找到MySQL卸载
·找到MySQL安装路径并删除剩余文件
·找到MySQL数据路径C:\ProgramData\MySQL并删除剩余文件
·删除注册表,[cmd] : regedit 打开注册表 MACHINE - SYSTEM - CONTROLSET001 - SERVICES - EVENLOG - APPLICATION - MYSQL
【(了解)启动、关闭服务】(cmd以管理员权限启动)
·[cmd] : services.msc 找到MySQL服务手动启动、关闭
·[cmd] : net start mysql 启动服务
·[cmd] : net stop mysql 关闭服务
【登录】
·[cmd] : mysql -uroot -p密码
·[cmd] : mysql -hip -uroot -p连接目标的密码 (h指host,ip是ip地址,本机ip为127.0.0.1)
·[cmd] : mysql --host=ip --user=root --password=连接目标的密码
【退出】
·[cmd] : exit
·[cmd] : quit
【目录结构】
1. MySQL安装目录:basedir="E:/develop/MySQL/"
* 在配置文件 my.ini 中可以查看到
2. MySQL数据目录:datadir="C:/ProgramData/MySQL/MySQL Server 5.5/Data/"
* 数据库:文件夹
* 表:文件夹中的文件
* 数据:文件中的数据 |
SQL | SQL 全称:Structured Query Language:结构化查询语言
就是定义了操作所有[关系型]数据库的规则。每一种数据库操作的方式存在不一样的地方,称为“方言”。
【SQL 通用语法】
·SQL 语句可以单行或多行书写,以分号 ; 结尾。
·可使用空格和缩进来增强语句的可读性。
·MySQL 数据库的 SQL 语句不区分大小写,关键字建议使用大写。
·[3种注释方式]
* 单行注释: -- 注释内容 (注意中间有空格,建议使用此方法)
# 注释内容 (中间可以没有空壳,是mysql 特有)
* 多行注释: /* 注释内容 */
【SQL 四种分类】##【面试重点】
1) DDL (Data Definition Language) 数据定义语言
增删改查数据库,表,列(等数据库对象)。 关键字:create(增加), drop(删除),alter(修改),show(查询)等
2) DML (Data Manipulation Language) 数据操作语言
增删改数据库中的表的数据。 关键字:insert(增加), delete(删除), update(修改) 等
3) DQL (Data Query Language) 数据查询语言
查询数据库中的表的数据。 关键字:select(查询), where(条件查询) 等
4) DCL (Data Control Language) 数据控制语言(了解)
定义数据库的访问权限和安全级别,创建、删除用户。 关键字:GRANT(授权), REVOKE(撤销权限) 等 |
DDL操作数据库CRUD | 【C(Create):创建】
·create database 数据库名称; -- 创建数据库 【重点掌握】
·create database if not exists 数据库名称; -- 创建数据库,判断不存在,再创建(仅了解)
·create database 数据库名称 character set 字符集名; -- 创建数据库,并指定字符集(仅了解)
·[练习]:创建db4数据库,判断是否存在,并制定字符集为gbk。
create database if not exists db4 character set gbk;
【R(Retrieve):查询】
·show databases; -- 查询所有数据库的名称:
·show create database 数据库名称; -- 查询某个数据库的字符集:查询某个数据库的创建语句
【U(Update):修改】
·alter database 数据库名称 character set 字符集名称; -- 修改数据库的字符集
【D(Delete):删除】(危险,谨慎操作)
·drop database 数据库名称; -- 删除数据库
·drop database if exists 数据库名称; -- 判断数据库存在,存在再删除
【使用数据库】
·select database(); -- 查询当前正在使用的数据库名称
·use 数据库名称; -- 使用数据库 |
数据库常用数据类型 | 数据类型:
- int 整数类型
·使用举例: age int,
- double 小数类型
·使用举例: score double(5,2) -- 总共有5位,其中小数占2位,整数占5-2=3位;
·注意:可以不指定()内的内容
- varchar:字符串类型
·使用举例: name varchar(20) -- 姓名最大20个字符,如 zhangsan 是8个字符,张三 是2个字符
·注意:必须指定()内的长度
·与char的区别:varchar是可变字符串,更省空间,char是不可变字符串
- date:日期,只包含年月日
·格式: yyyy-MM-dd
- datetime:日期,包含年月日时分秒
·格式: yyyy-MM-dd HH:mm:ss
- timestamp:时间蹉类型 包含年月日时分秒
格式: yyyy-MM-dd HH:mm:ss
·与datetime的区别:如果将来不给这个字段赋值,或赋值为null,则默认使用当前的系统时间,来自动赋值 |
DDL操作表 | 【C(Create):创建】【重点】
·[语法]: ·[使用举例 - 创建学生表]
create table 表名( create table student(
字段名1 数据类型1 约束, id int,
字段名2 数据类型2 约束, name varchar(32) NOT NULL,
... ... age int ,
字段名n 数据类型n 约束 score double(4,1),
); birthday date,
- 注意:最后一列不需要加逗号 , insert_time timestamp
);
·[复制表]:
create table 新建表名 like 被复制的表名;
【R(Retrieve):查询】
·show tables; -- 查询某个数据库中所有的表名称
·desc 表名; -- 查询表结构
【U(Update):修改】
·alter table 表名 rename to 新的表名; -- 修改表名
·alter table 表名 character set 字符集名称; -- 修改表的字符集
·alter table 表名 add 列名 数据类型; -- 添加一列
·alter table 表名 change 列名 新列名 新数据类型; -- 修改列名称、类型
·alter table 表名 modify 列名 新数据类型; -- 修改列类型
·alter table 表名 drop 列名; -- 删除列
【D(Delete):删除】
·drop table 表名; -- 删除一个表
·drop table if exists 表名 -- 判断一个表如果存在就删除 |
DML增删改表中数据 | 【添加数据】
·[语法]:
insert into 表名(列名1,列名2,...列名n) values(值1,值2,...值n);
·注意:
- 列名和值的数量和类型必须一致,不赋值的字段给null值
- 如果表名后,不定义列名,则默认给所有列添加值
简化格式:insert into 表名 values(值1,值2,...值n);
- 批量插入:insert into 表名 values(值1,值2,...值n),(值1,值2,...值n),(值1,值2,...值n)...
- 除了数字类型,其他类型需要使用引号(单双都可以)引起来
【删除数据】
·[语法]:
delete from 表名 where 条件
·注意:
- 如果不加条件,则删除表中所有记录。
- 如果要删除所有记录
- delete from 表名; -- 不推荐使用。有多少条记录就会执行多少次删除操作
- TRUNCATE TABLE 表名; -- 推荐使用。先删除表,然后再创建一张一样的表。只执行删增两次操作,效率更高,
【修改数据】
·[语法]:
update 表名 set 列名1 = 值1, 列名2 = 值2,... where 条件;
·注意:
- 如果不加任何条件,则会将表中所有记录全部修改。 |
DQL查询表中数据 | ·select * from 表名; -- 查询一张表所有的信息
·[语法]:
select
字段列表
from
表名列表
where
条件列表
group by
分组字段
having
分组之后的条件
order by
排序
limit
分页限定 |
DQL基础查询 | ·查询多字段
select 字段名1,字段名2... from 表名;
注意:如果查询所有字段,则可以使用*来替代字段列表。
字段名可以通过表名.出来
·去除重复(一般只针对一个字段)
select distinct 字段名 from 表名;
·计算列
一般针对数字列(int、double)使用四则运算计算值
ifnull(表达式1,表达式2):如果表达式1为null,就把表达式1替换为表达式2,。null参与的运算,计算结果都为null
·起别名:
字段名、表名 as 新名称 -- as可以省略,字段名+空格+新名称(新名称若为汉字,不需要用引号,直接使用)
-- 同一张表在一个查询中可以起多个别名,用于自关联映射 |
DQL条件查询 | ·[语法]:
where 条件
- 运算符
· > < <= >= = != <>
· BETWEEN...AND -- 例: BETWEEN 20 AND 30 -- [20,30] 左右都包括,小的数必须放在前面
· IN(集合) -- 例: IN(18,20,22) -- 18 or 20 or 22,即存在宇这个集合中,可以简化多个or语句
· IS NULL / IS NOT NULL -- 判断是否为null,null值不能使用 = 和 != 来判断
· and / && [与] or / || [或] not / ! [非]
· LIKE:模糊查询 -- 配合占位符使用,如查询所有带某个字的姓名
两种占位符: · 下划线 _ -- 表示单个任意字符
· 百分号 % -- 表示多个任意字符 |
——DQL条件查询练习 | · 查询年龄大于20岁
SELECT * FROM student WHERE age > 20;
-- 查询年龄大于等于20岁
SELECT * FROM student WHERE age >= 20;
-- 查询年龄等于20岁
SELECT * FROM student WHERE age = 20;
-- 查询年龄不等于20岁
SELECT * FROM student WHERE age != 20;
SELECT * FROM student WHERE age <> 20;
-- 查询年龄大于等于20并且小于等于30
SELECT * FROM student WHERE age >= 20 && age <=30;
SELECT * FROM student WHERE age >= 20 AND age <=30;
SELECT * FROM student WHERE age BETWEEN 20 AND 30;
-- 查询年龄22岁,18岁,25岁的信息
SELECT * FROM student WHERE age = 22 OR age = 18 OR age = 25
SELECT * FROM student WHERE age IN (22,18,25);
-- 查询英语成绩为null
SELECT * FROM student WHERE english = NULL; -- 这种写法错误!null值不能使用 = 和 != 来判断
SELECT * FROM student WHERE english IS NULL;
-- 查询英语成绩不为null
SELECT * FROM student WHERE english IS NOT NULL;
-- 查询姓马的有哪些? like
SELECT * FROM student WHERE NAME LIKE '马%';
-- 查询姓名第二个字是化的人
SELECT * FROM student WHERE NAME LIKE "_化%";
-- 查询姓名是3个字的人
SELECT * FROM student WHERE NAME LIKE '___';
-- 查询姓名中包含德的人
SELECT * FROM student WHERE NAME LIKE '%德%'; |
DQL排序查询 | ·[语法]:
order by 排序字段1 排序方式1,排序字段2 排序方式2...
·排序方式:
- ASC:升序,默认的。
- DESC:降序。
·注意:
如果有多个排序条件,则当前边的条件值一样时,才会判断第二条件 |
DQL聚合函数 | ·[语法]:
SELECT 函数名(字段名) FROM 表名;
聚合函数:将一列数据作为一个整体,进行纵向的计算
·count:计算个数
一般选择非空的列:主键(如学号,编号等)
count(*) *表示,一行数据中只要有一个不为null,那么这行就算一条数据,一般不使用此方法
·max:计算最大值
·min:计算最小值
·sum:计算和
·avg:计算平均值
·注意:聚合函数的计算,要排除null值。
解决方案:
- 选择不包含空的列进行计算
- 使用IFNULL函数 |
DQL分组查询 | ·[语法]:
SELECT 分组字段,聚合函数1,聚合函数2... FROM 表名 WHERE 分组前的条件 GROUP BY 分组字段 HAVING 分组后的条件;
·注意:
- 分组之后查询的字段:分组字段、聚合函数,聚合函数可以起别名使用
- where 和 having 的区别? 【面试重点】
- where 在分组之前进行限定,如果不满足条件,则不参与分组。having在分组之后进行限定,如果不满足结果,则不会被查询出来
- where 后不可以跟聚合函数,having可以进行聚合函数的判断。
·练习:
-- 按照性别分组。分别查询男、女同学的平均分
SELECT sex , AVG(math) FROM student GROUP BY sex;
-- 按照性别分组。分别查询男、女同学的平均分,人数
SELECT sex , AVG(math),COUNT(id) FROM student GROUP BY sex;
-- 按照性别分组。分别查询男、女同学的平均分,人数 要求:分数低于70分的人,不参与分组
SELECT sex , AVG(math),COUNT(id) FROM student WHERE math > 70 GROUP BY sex;
-- 按照性别分组。分别查询男、女同学的平均分,人数 要求:分数低于70分的人,不参与分组,分组之后。人数要大于2个人
SELECT sex , AVG(math),COUNT(id) FROM student WHERE math > 70 GROUP BY sex HAVING COUNT(id) > 2;
SELECT sex , AVG(math),COUNT(id) 人数 FROM student WHERE math > 70 GROUP BY sex HAVING 人数 > 2; |
DQL分页查询 | ·[语法]:
limit 开始的索引,每页查询的条数;
注意:开始的索引可以省略,省略不写则默认为0
·公式:开始的索引 = (当前的页码 - 1) * 每页显示的条数
举例:每页显示3条记录
SELECT * FROM student LIMIT 0,3; -- 第1页
SELECT * FROM student LIMIT 3,3; -- 第2页
SELECT * FROM student LIMIT 6,3; -- 第3页
·limit 是一个MySQL"方言" |
DCL—管理用户(了解) | DCL:管理用户,授权,在公司中由DBA(数据库管理员)来管理; 本机主机名:localhost;
·添加用户:
- 语法:CREATE USER '用户名账号'@'主机名/电脑IP地址' IDENTIFIED BY '密码';
·删除用户:
- 语法:DROP USER '用户名'@'主机名';
·修改用户密码:
- UPDATE USER SET PASSWORD = PASSWORD('新密码') WHERE USER = '用户名';
- 例: UPDATE USER SET PASSWORD = PASSWORD('abc') WHERE USER = 'lisi';
- SET PASSWORD FOR '用户名'@'主机名' = PASSWORD('新密码');
- 例: SET PASSWORD FOR 'root'@'localhost' = PASSWORD('123');
- mysql中忘记了root用户的密码?
1. cmd -- > net stop mysql 停止mysql服务 (* 需要管理员运行该cmd)
2. 使用无验证方式启动mysql服务: mysqld --skip-grant-tables
3. 打开新的cmd窗口,直接输入mysql命令,敲回车。就可以登录成功
4. use mysql;
5. update user set password = password('你的新密码') where user = 'root';
6. 关闭两个窗口
7. 打开任务管理器,手动结束mysqld.exe 的进程
8. 启动mysql服务
9. 使用新密码登录。
·查询用户:
- 1. 切换到mysql数据库
USE myql;
- 2. 查询user表
SELECT * FROM USER; |
DCL—权限管理(了解) | 通配符: % 表示可以在任意主机使用用户登录数据库
1. 查询权限:
-- 查询权限
SHOW GRANTS FOR '用户名'@'主机名/IP地址';
SHOW GRANTS FOR 'lisi'@'%';
2. 授予权限:
-- 授予权限
grant 权限列表 on 数据库名.表名 to '用户名'@'主机名/IP地址';
-- 给张三用户授予所有权限,在任意数据库任意表上
GRANT ALL ON *.* TO 'zhangsan'@'localhost';
3. 撤销权限:
-- 撤销权限:
revoke 权限列表 on 数据库名.表名 from '用户名'@'主机名/IP地址';
REVOKE UPDATE ON db3.`account` FROM 'lisi'@'%'; |
约束 | ·概念: 对表中的数据进行限定,保证数据的正确性、有效性和完整性。
·分类:
·主键约束:primary key
·非空约束:not null
·唯一约束:unique
·外键约束:foreign key |
约束——非空约束 | ·非空约束:not null,某一列的值不能为null
·创建表时添加约束
CREATE TABLE stu(
id INT,
NAME VARCHAR(20) NOT NULL -- name为非空
);
·创建表完后,添加非空约束
ALTER TABLE stu MODIFY NAME VARCHAR(20) NOT NULL;
·删除name的非空约束
ALTER TABLE stu MODIFY NAME VARCHAR(20); |
约束——唯一约束 | ·唯一约束:unique,某一列的值不能重复,对null值不会唯一约束,即可以有多个null值,又叫索引唯一约束
·注意:
唯一约束可以有NULL值,但是只能有一条记录为null
·在创建表时,添加唯一约束
CREATE TABLE stu(
id INT,
phone_number VARCHAR(20) UNIQUE -- 手机号唯一
);
·在表创建完后,添加唯一约束
ALTER TABLE stu MODIFY phone_number VARCHAR(20) UNIQUE;
·删除唯一约束
ALTER TABLE stu DROP INDEX phone_number; |
约束——主键约束 | ·主键约束:primary key。
·注意:
- 含义:非空且唯一 - 一张表只能有一个主键 - 主键可以一个或多个字段(联合主键)
- 主键可以用整数类型:一般和auto_increment结合,缺点:有可能超过整数范围(21亿+)
- 主键可以用字符串类型:随机生成一个永远不会重复的字符串
·在创建表时,添加主键约束
create table stu(
id int primary key, -- 给id添加主键约束
name varchar(20)
);
·创建完表后,添加主键
ALTER TABLE stu MODIFY id INT PRIMARY KEY;
·删除主键
ALTER TABLE stu DROP PRIMARY KEY;
·自动增长:
- 概念:如果某一列是数值类型的,使用 auto_increment 可以来完成值得自动增长
- 在创建表时,添加主键约束,并且完成主键自增长
create table stu(
id int primary key auto_increment,-- 给id添加主键约束
name varchar(20)
);
- 添加自动增长
ALTER TABLE stu MODIFY id INT AUTO_INCREMENT;
- 删除自动增长
ALTER TABLE stu MODIFY id INT; |
约束——外键约束 | ·外键约束:foreign key,让表于表产生关系,从而保证数据的正确性。
·在创建表时,可以添加外键
语法:
create table 表名(
....
外键列
constraint 外键名称 foreign key (外键列名称) references 主表名称(主表列名称)
);
·创建表之后,添加外键
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键列名称) REFERENCES 主表名称(主表列名称);
·删除外键
ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;
·constraint 外键名称 这个添加外键名称的操作可以省略,此时系统会自动生成一个随机的名字,如果需要做删除等操作使用到名字
可以查看:show
·级联操作(了解即可)
- 添加/删除级联操作
- 语法:ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名称) REFERENCES 主表名称(主表列名称)
ON UPDATE CASCADE / ON DELETE CASCADE;
- 分类:
- 级联更新:ON UPDATE CASCADE
- 级联删除:ON DELETE CASCADE |
数据库设计 | |
多表之间的关系 | ·分类:
· 一对一(了解即可,开发中很少遇到,万一有需求,一般就合成一张表):
如:人和身份证
分析:一个人只有一个身份证,一个身份证只能对应一个人
实现方式:一对一关系实现,可以在任意一方添加唯一外键指向另一方的主键。
· 一对多(多对一):
如:部门和员工
分析:一个部门有多个员工,一个员工只能对应一个部门
实现方式:在“多”的一方建立外键,指向“一”的一方的主键。
· 多对多:
如:学生和课程
分析:一个学生可以选择很多门课程,一个课程也可以被很多学生选择
实现方式:多对多关系实现需要借助第三张中间表。中间表至少包含两个字段,这两个字段作为第三张表的外键,分别指向两张表的主键,这两个主键组合起来作为中间表的联合主键 |
范式(重复内容拆分) | ·概念:设计数据库时,需要遵循的一些规范。要遵循后边的范式要求,必须先遵循前边的所有范式要求
设计关系数据库时,遵从不同的规范要求,设计出合理的关系型数据库,这些不同的规范要求被称为不同的范式,各种范式呈递次规范,越高的范式数据库冗余越小。
目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。
·分类:
1. 第一范式(1NF):每一列都是不可分割的原子数据项(列表头不能合并拆分,一列数据只有一个表头)
第一范式存在的问题:
1.存在非常严重的数据冗余(重复):如姓名、系名、系主任
2.数据添加存在问题:如添加新开设的系和系主任时,没有学生学号等信息,数据不合法
3.数据删除存在问题:如张无忌同学毕业了,删除数据,会将系的数据一起删除。
2. 第二范式(2NF):在1NF的基础上,非码属性必须完全依赖于码(在1NF基础上消除非主属性对主码的部分函数依赖)
·相关概念:
1. 函数依赖:A-->B,如果通过A属性(属性组)的值,可以确定唯一B属性的值。则称B依赖于A
例如: 学号 --> 姓名。
2. 完全函数依赖:A-->B, 如果A是一个属性组,则B属性值得确定需要依赖于A属性组中所有的属性值。
例如:(学号,课程名称) --> 分数
3. 部分函数依赖:A-->B, 如果A是一个属性组,则B属性值得确定只需要依赖于A属性组中某一些值即可。
例如:(学号,课程名称) --> 姓名
4. 传递函数依赖:A-->B, B -- >C . 如果通过A属性(属性组)的值,可以确定唯一B属性的值,在通过B属性(属性组)的值可以确定唯一C属性的值,则称 C 传递函数依赖于A
例如: 学号-->系名,系名-->系主任
5. 码:如果在一张表中,一个属性或属性组,被其他所有属性所完全依赖,则称这个属性(属性组)为该表的码
例如:该表中码为:(学号,课程名称)
- 主属性:码属性组中的所有属性
- 非主属性:码属性组之外的属性
第二范式存在的问题:
2.数据添加存在问题:如添加新开设的系和系主任时,没有学生学号等信息,数据不合法
3.数据删除存在问题:如张无忌同学毕业了,删除数据,会将系的数据一起删除。
3. 第三范式(3NF):在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖) |
数据库的备份和还原 | ·cmd命令行:
·[语法]:
- 备份: mysqldump -u用户名 -p密码 数据库名称 > 保存的路径
- 还原:
1. 登录数据库 mysqldump -u用户名 -p密码
2. 创建数据库
3. 使用数据库
4. 执行文件。source 文件路径
·图形化工具:
右键 - 备份/导出 - 备份数据库 - 选择路径并导出
右键 - 执行SQL脚本 - 选择备份文件导入 |
多表查询 | 概述:
select * from 表1,表2;
会出现表1、2组合的所有结果(笛卡尔积)
多表查询需要消除其中的无用数据 |
多表查询—内连接查询 | ·内连接查询:
·隐式内连接:配合where(将来用这个)
- 使用举例:select * from 表A,表B where 表A.主键名 = 表B.主键名
·显式内连接:(了解,将来不用这个)
- 语法: select 字段列表 from 表名1 [inner] join 表名2 on 条件 -- inner []表示可省略
·内连接查询的思路:
- 从哪些表中查询数据
- 条件是什么
- 查询哪些字段 |
多表查询—外连接查询 | ·外链接查询:
·左外连接:(将来用这个)
- 语法:select 字段列表 from 表1 left [outer] join 表2 on 条件;
- 查询的是左表所有数据以及其交集部分。
- 注意:left join 左右两侧各只能有一张表
- 例子:
- 查询所有员工信息,如果员工有部门,则查询部门名称,没有部门,则不显示部门名称
SELECT t1.*,t2.`name` FROM emp t1 LEFT JOIN dept t2 ON t1.`dept_id` = t2.`id`;
·右外连接:(了解,将来不用这个)
- 语法:select 字段列表 from 表1 right [outer] join 表2 on 条件;
- 查询的是右表所有数据以及其交集部分。
- 例子:
SELECT * FROM dept t2 RIGHT JOIN emp t1 ON t1.`dept_id` = t2.`id`; |
多表查询—子查询 | ·子查询的结果是单行单列的:
- 子查询可以作为条件,使用运算符去判断。 运算符: > >= < <= = != <>
- 例子: 查询员工工资小于平均工资的人
SELECT * FROM emp WHERE emp.salary < (SELECT AVG(salary) FROM emp);
·子查询的结果是多行单列的:
- 查询可以作为条件,使用运算符in来判断
- 例子: 查询'财务部'和'市场部'所有的员工信息
SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE NAME = '财务部' OR NAME = '市场部');
-- 建议使用普通内连接
SELECT * FROM emp,dept WHERE emp.`dept_id` = dept.`id` and dept.`name` = '财务部' OR NAME = '市场部';
·子查询的结果是多行多列的:
* 子查询可以作为一张虚拟表参与查询
-例子: 查询员工入职日期是2011-11-11日之后的员工信息和部门信息
-- 子查询
SELECT * FROM dept t1 ,(SELECT * FROM emp WHERE emp.`join_date` > '2011-11-11') t2 WHERE t1.id = t2.dept_id;
-- 建议使用普通内连接
SELECT * FROM emp t1,dept t2 WHERE t1.`dept_id` = t2.`id` AND t1.`join_date` > '2011-11-11' |
事务 | 基本介绍:
·概念:
如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败。
·操作(手动提交):
- 开启事务: start transaction; -- 保证一些操作成功
- 回滚: rollback; -- 在java中,如果发生异常,在catch语句中来回滚
- 提交: commit; -- 没有发生问题时,再提交
·MySQL默认自动提交:
一条DML(数据增删改)语句会自动提交一次事务
(Oracle 数据库默认是手动提交事务)
·查看事务的默认提交方式:SELECT @@autocommit; -- 结果为 1 代表自动提交 0 代表手动提交
·(了解)修改默认提交方式: set @@autocommit = 0; -- 把默认自动提交修改为默认手动提交 |
事务的四大特征(面试) | 1. 原子性:是不可分割的最小操作单位,要么同时成功,要么同时失败。
2. 持久性:当事务提交或回滚后,数据库会持久化的保存数据。
3. 隔离性:多个事务之间。相互独立。
4. 一致性:事务操作前后,数据总量不变 |
事务的隔离级别(了解) | ·概念:多个事务之间隔离的,相互独立的。但是如果多个事务操作同一批数据,则会引发一些问题,设置不同的隔离级别就可以解决这些问题。
·存在问题:
- 脏读:一个事务,读取到另一个事务中没有提交的数据
- 不可重复读(虚读):在同一个事务中,两次读取到的数据不一样。
- 幻读:一个事务操作(DML)数据表中所有记录,另一个事务添加了一条数据,则第一个事务查询不到自己的修改。
·隔离级别:
- read uncommitted:读未提交 - 产生的问题:脏读、不可重复读、幻读,脏读问题很大不建议使用
- read committed:读已提交 (Oracle默认) - 产生的问题:不可重复读、幻读,可以使用此方法
- repeatable read:可重复读 (MySQL默认) - 产生的问题:幻读,可以使用此方法
- serializable:串行化 - 可以解决所有的问题,但是效率低下,不建议使用
- 注意:隔离级别从小到大安全性越来越高,但是效率越来越低
- 数据库查询隔离级别: select @@tx_isolation;
- 数据库设置隔离级别: set global transaction isolation level 级别字符串;
* 演示:
set global transaction isolation level read uncommitted;
start transaction;
-- 转账操作
update account set balance = balance - 500 where id = 1;
update account set balance = balance + 500 where id = 2; |
JDBC概念 | Java DataBase Connectivity Java数据库连接 / Java语言操作数据库
JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。
JDBC作用:平复数据库数据的差异 |
快速入门 | //1.导入驱动jar包
复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下
libs右键-->Add As Library
//2.注册驱动 //告诉程序该使用哪一个数据库驱动jar
Class.forName("com.mysql.jdbc.Driver");
//3.通过 【 DriverManager 驱动管理对象 】 获取 【 Connection 数据库连接对象 】 //搭桥,获取数据库连接
Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db3", "root", "root");
//4.获取 【 Statement 执行sql的对象 】
Statement stmt = conn.createStatement();
//5.执行sql
int count = stmt.executeUpdate(sql语句);
//6.处理结果
System.out.println(count);
//7.释放资源
stmt.close();
conn.close(); |
DriverManager | 驱动管理对象 【功能】 1. 注册驱动 2. 获取数据库连接
1. 注册驱动:告诉程序该使用哪一个数据库驱动jar
方法:static void registerDriver(Driver driver) :注册与给定的驱动程序 DriverManager 。
【掌握】代码实现: Class.forName("com.mysql.jdbc.Driver");
通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
注意:mysql5之后的驱动jar包可以省略注册驱动的步骤,但是依然建议手写注册,方便兼容低版本的MySQL。
2. 获取数据库连接:
【掌握】方法:static Connection getConnection(String url, String user, String password)
参数:url:指定连接的路径 user:用户名 password:密码
url语法: jdbc:mysql://ip地址(域名):端口号/数据库名称
url例子: jdbc:mysql://localhost:3306/db3
url细节: 如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称 |
Connection | 数据库连接对象 【功能】 1. 获取执行 sql 的对象 2. 管理事务
1. 获取执行 sql 的对象
方法:
- Statement createStatement()
【掌握】 - PreparedStatement prepareStatement(String sql)
2. 管理事务
方法:
- 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,把自动提交关闭(只针对这一次),即开启事务
- 回滚事务:rollback()
- 提交事务:commit() |
Statement(了解) | 执行sql的对象 【功能】:执行sql
·SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题,所以以后不使用这个,而使用PreparedStatement
- 输入用户随便,输入密码:a' or 'a' = 'a
- sql:select * from user where username = 'fhdsjkf' and password = 'a' or 'a' = 'a'
方法:
· 【掌握】 int executeUpdate(String sql) :执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句(DDL不常用,了解)
- 返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功 返回值>0的则执行成功,反之,则失败。
· 【掌握】 ResultSet executeQuery(String sql) :执行DQL(select)语句
- 返回值:ResultSet 这个对象封装了所有结果 (详情见表格下一行)
· boolean execute(String sql) :可以执行任意的sql,返回true表示执行的是DQL查询,返回false表示执行的是DML增删改 (了解)
由于返回的是布尔值,对于我们查询需要得到的结果没有意义,所以不使用 |
ResultSet | 结果集对象 【功能】:封装查询结果
·【掌握】方法:
·boolean next(): 游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true
·getXxx(参数):获取数据
- Xxx:代表数据类型 如: int getInt() , String getString()
- 参数:
1. int:代表列的编号,从1开始 如: getString(1),获取第1列 id列的值
(建议)2. String:代表列名称。 如: getDouble("balance") 获取列名是balance的列的值
·使用步骤:
1. 游标向下移动一行
2. 判断是否有数据
3. 获取数据
//循环判断游标是否是最后一行末尾,若不是,获取并打印所有信息。
【掌握】
while(rs.next()){
int id = rs.getInt("id");
String name = rs.getString("name");
double balance = rs.getDouble("balance");
System.out.println(id + "," + name + "," + balance);
} |
PreparedStatement | 执行sql的对象 【功能】:执行sql
【方法】
·执行DML int excuteUpdate();//这里不能传入sql语句了
·执行DQL ResultSet excuteQuery();//这里不能传入sql语句了
·SQL注入问题:在拼接sql时,有一些sql的特殊关键字参与字符串的拼接。会造成安全性问题
- 输入用户随便,输入密码:a' or 'a' = 'a
- sql:select * from user where username = 'fhdsjkf' and password = 'a' or 'a' = 'a'
·解决sql注入问题:使用PreparedStatement对象来解决
·预编译的SQL:参数使用?作为占位符
·步骤:
1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
2. 注册驱动
3. 获取数据库连接对象 Connection
4. 定义sql
注意:sql的参数使用?作为占位符。 如:select * from user where username = ? and password = ?;
5. 获取执行sql语句的对象 PreparedStatement 通过方法:connection.prepareStatement(String sql)
6. 给?赋值:
方法: setXxx(参数1,参数2) 如:preparedStatement.setString(1,username);
参数1:?的位置编号 从1 开始 preparedStatement.setString(2,password);
参数2:?的值
7. 执行sql,接受返回结果,无参数不能传递sql语句 如:rs = preparedStatement.executeQuery();
8. 处理结果,如:return rs.next();
9. 释放资源
·注意:后期都会使用PreparedStatement来完成增删改查的所有操作 - 可以防止SQL注入 - 效率更高 |
JDBCUtils | 抽取JDBC工具类 目的:简化书写
--注册驱动,获取连接 --释放资源
import java.io.FileReader;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
public class JDBCUtils {
private static String url;
private static String user;
private static String password;
private static String driverClass;
static{
try {
ClassLoader classLoader = JDBCUtils.class.getClassLoader();
String path = classLoader.getResource("mysql.properties").getPath();
Properties prop = new Properties();
prop.load(new FileReader(path));
url = prop.getProperty("url");
user = prop.getProperty("user");
password = prop.getProperty("password");
driverClass = prop.getProperty("driverClass");
Class.forName(driverClass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,user,password);
}
//释放资源
public static void close(){}
public static void close(Statement stmt, Connection conn){
close(null,stmt,conn);
}
public static void close(ResultSet rs, Statement stmt, Connection conn){
if (rs!=null){
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (stmt!=null){
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
} |
JDBC控制事务 | ·事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。
·使用Connection对象来管理事务
- 开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
* 在执行sql之前开启事务
conn.setAutoCommit(false);
- 提交事务:commit()
* 当所有sql都执行完提交事务
conn.commit();
- 回滚事务:rollback()
* 在catch中回滚事务
try {
if(conn != null) {
conn.rollback();
}
} catch (SQLException e1) {
e1.printStackTrace();
} |