SQL的基础学习
结构化查询语言(Structured Query Language)简称SQL,意思就是说这个是mysql的执行语句,就相当是java语言一样有规范。只要是关系型数据库,都可以用sql语句,但是不同的数据库,sql语法稍微有些不同
在cmd里面
Sql可以执行多行语句,也可以执行单行语句,多行语句就要用分号结尾
-> show dateBases --> 执行(按一下回车,就代表你要执行多行,现在cmd就等你的;直到识别出;之后直接将所有执行过的结果在显示出 来)
-> ..... --(执行,但是还不显示)
-> ;
使用空格和缩进增强代码的可读性
sql语句是不区分大小写的,虽然在sqyog里面关键字直接就给你自动变成大写了
注释:
多行注释:/* 注释内容 */
单行注释: --
注释内容 或 #
注释内容(Mysql特有的)
DDL(数据库定义语言):用来定义数据库,表,表里面的属性 ----> 关键字: create drop alter show use等 就是这写关键字开头的
DML(数据库操作语言):用来对数据库中表进行增删改操作的—> **关键字:**insert delete update等 就是这写关键字开头的
DQL(数据库查询语言):用来查询表中的数据-----> 关键字: select where
DCL(数据库控制语言):用来控制用户的权限(就相当是写了一些权限修饰符)有些用户被修饰符修饰之后查询数据库的能力就被限制了
还有就是创建用户 ----> **关键字:**grant revoke等
如图解释五种类型语言所应用的场景:
操作数据库 CRUD
小知识点:latin1 是 ISO-8859-1这种编码集的别名
C:
-- DDL语句创建一个数据库
CREATE DATABASE IF NOT EXISTS `test`
-- DDL语句查看创建`test`表的语句
SHOW CREATE DATABASE `test`
-- 创建数据库如果已经存在数据库则创建失败
-- DDL创建一个数据库并且编码集是utf-8
CREATE DATABASE IF NOT EXISTS `test2` CHARSET utf8
R:
-- DDL查看所有的数据库
SHOW DATABASES
-- DDL操作查看一个创建数据库的操作语句和创建数据库的编码集
SHOW CREATE DATABASE `test`
U:
-- DDL操作修改test的编码集为gbk
ALTER DATABASE `test` CHAR SET gbk
D:
-- DDL删除一个数据库
DROP DATABASE IF EXISTS `test2`
额外还有一个:
-- DDL操作使用数据库
USE `test`
1. Create 创建表(DDL创建表)
学生表
学号 | 姓名 | 年龄 |
---|---|---|
CREATE FROM IF NOT EXISTS`student`(
每一列的名字(列名) 数据类型,
列名2 数据类型,
....... ,
列名last 数据类型 -- 不加逗号
);
注意事项:
1.DDL语句中在创建表的时候,每一行的语句后面都要加逗号但是最后一个语句不用加逗号
2.数据类型
数据类型
实例:
C: 创建表
CREATE TABLE IF NOT EXISTS `student`(
`id` INT COMMENT '学号',
`name` VARCHAR(2) COMMENT '姓名',
`score` DOUBLE(5,2) COMMENT '成绩',
`birthday` DATE COMMENT '日期',
`currenttime` TIMESTAMP DEFAULT NULL COMMENT '当前时间'
);
**R:**查询表
-- DDL展示表
SHOW TABLES
-- DDL查询表的创造语言的语句
SHOW CREATE TABLE `school`
**U:**修改表(修改表内的东西)
-- DDL修改表名
ALTER TABLE `stu` RENAME TO `student`
-- DDL 修改表的字符集
ALTER TABLE `stu` CHARACTER SET utf8
-- DDL 添加表中的某一个字段
ALTER TABLE `stu` ADD `index` INT COMMENT '索引'
-- DDL 修改表中的某一字段
ALTER TABLE `stu` MODIFY `index` VARCHAR(10) -- modify只能改字段的类型
ALTER TABLE `stu` CHANGE `index` `sex` VARCHAR(10) -- change不仅可以改变字段的名称,也可以更改字段的名字
-- DDL 删除表中的某一个字段
ALTER TABLE `stu` DROP `sex`
**D:**删除表(直接将表删除)
-- DDL 删除表
DROP TABLE IF EXISTS `stu`
-- DML 操做表中添加字段的属性
INSERT INTO `student` (id,`name`,score,birthday,currenttime) VALUES (1,'Fan',150.00,'2000-8-12',NULL)
-- 插入全部的字段的属性的话就可以剩去字段的名字
INSERT INTO `student` VALUES (1,'Fa',150.00,'2000-8-12',NULL)
-- DML 操作数据中添加字段属性的不同语法
1.一次插入多个
INSERT INTO `student` (id,`name`,score,birthday,currenttime) VALUES (1,'Fan',150.00,'2000-8-12',NULL)
,(2,'Fan',150.00,'2000-8-12',NULL)
2.一次插入不同个字段的属性
INSERT INTO `student` (id,score,birthday,currenttime) VALUES (1,150.00,'2000-8-12',NULL)
3.一次插入多个不同字段的属性
INSERT INTO `student` (id,score,birthday,currenttime) VALUES (1,150.00,'2000-8-12',NULL),(2,150.00,'2000-8-12',NULL)
注意事项:
1.字段名和属性要一 一对应
2.如果不加字段名则默认为全部字段名,并且按照顺序的字段名
3.除了数字数据类型之外,其他类型都要加引号
删除表中的数据—delete from…where
-- DML 删除表中的所有数据
DELETE FROM `student`
TRUNCATE `student`
-- DML 删除表中指定位置的数据
DELETE FROM `student` WHERE id = 1
TRUNCATE `student`
delete 和 truncate的区别
修改表中的数据 —> update … set
-- DML 改变表中的数据
UPDATE `student` SET id = 2;
-- DML 改变表中指定位置的一个数据
UPDATE `student` SET id = 1 WHERE `id` = 3
-- DML 该表表中指定位置的多个数据
UPDATE `student` SET id = 3, score = 100.0 WHERE `id` = 2
几个重要的关键字
select 字段名 from 表名列表 where 条件列表 group by 分组字段 having 分组之后的条件 order by 排序 limit 分页限定
①: 添加一张表
INSERT INTO `student1` VALUES
(1,'马云',18,'男','杭州',80,80),
(2,'马化腾',19,'男','深圳',75,60),
(3,'埃隆马斯克',31,'男','美国',76,93),
(4,'扎克伯格',27,'男','美国',65,NULL),
(5,'郎平',16,'女','上海',90,98),
(6,'姚明',32,'男','上海',80,81);
②:要求查询 student1表中所有的学生的年龄信息和姓名
SELECT `name`, `age` FROM `student1`
③:要求查询 student1表中的不重复的性别和地址(去重查询)
distinct—>去重
SELECT
DISTINCT `address` ,`sex` -- 意思就是将重复的address和sex合并在一起输出了(去重)
FROM
`student1`
④:要求查询student1表中的java和c的成绩以及他们的和成绩(求和查询)
-- DQL查询java和c成绩以及他们的总成绩
SELECT `javaScore`,`cScore`,(`javaScore` + `cScore`) AS '总成绩' FROM `student1`
注意: 起别名的时候可以不加 AS关键字 也可以不加单引号
⑤:要求如果缺考但是不能输出null(ifnull(‘null对应的列名’,‘出现null之后拿什么代替’)函数的使用)
-- ifnull('null对应的列名','出现null之后拿什么代替')
-- DQL查询java和c成绩以及他们的总成绩,但是不能出现null--> 运用函数ifnull();
SELECT `javaScore`,IFNULL(`cScore`,0),(`javaScore` + IFNULL(`cScore`,0)) AS 总成绩 FROM `student1`
运算符:
①: 查询年龄大于20岁的
查询年龄不等于27岁的
-- DQL查询所有人(年龄大于20)
SELECT * FROM `student1` WHERE age >= 20
-- DQL查询年龄不等于27岁的
SELECT * FROM `student1` WHERE age <> 27
SELECT * FROM `student1` WHERE age != 27
**②:**查询年龄在20到30岁之间的
查询年龄不在20到30岁之间的
查询年龄16,18,19岁
IN 和 BETWEEN…AND 的应用
-- DQL查询年龄在20到30之间的(between是包含左右两边的)
SELECT * FROM `student1` WHERE age BETWEEN 20 AND 30
-- DQL查询年龄不在20到30之间
SELECT * FROM `student1` WHERE NOT(age >=20 AND age <= 30)
-- DQL查询年龄16,18,19岁
SELECT * FROM `student1` WHERE age BETWEEN 16 AND 19 && age <> 17
SELECT * FROM `student1` WHERE age IN (16,18,19)
**③:**查询C语言缺考的学生
-- DQL查询C语言缺考的学生
SELECT * FROM `student1` WHERE cScore = NULL -- 不正确
SELECT * FROM `student1` WHERE cScore IS NULL
-- DQL查询有C语言成绩的
SELECT * FROM `student1` WHERE !(cScore IS NULL) -- 不正确
SELECT * FROM `student1` WHERE cScore IS NOT NULL
注意:模糊查询在项目中用的是非常之多
**①:**查询姓马的学生
查询第二个字是克的学生
查询名字中包含马的学生
查询名字是三个字的信息
-- 查询姓马的学生信息
SELECT * FROM `student1` WHERE `name` LIKE '马%'
-- 查询第二个字是克的学生
SELECT * FROM `student1` WHERE `name` LIKE '_克' -- 查不出来因为查到克之后就不查了么有叫什么克的
SELECT * FROM `student1` WHERE `name` LIKE '_克%'
-- 查询名字中包含马的学生
SELECT * FROM `student1` WHERE`name` LIKE '%马%'
-- 查询名字是三个字的信息
SELECT * FROM `student1` WHERE `name` LIKE '___'
具体的用法:
order by 字段1 排序方式1,字段2 排序方式2…(意思就是说先按照第一个排序,然后第一个出现一样的时候再按照第二个排序方式排)
排序方式:
ASC:升序(默认)
DESC:降序
**①:**查询按照java的成绩升序来排
查询按照java的成绩降序来排
查询java的成绩一样的时候用c成绩降序来排序
-- 查询按照java的成绩升序来排
SELECT * FROM `student1` ORDER BY `javaScore` -- 默认就是升序
-- 查询按照java的成绩降序来排
SELECT * FROM `student1` ORDER BY `javaScore` DESC
-- 查询java的成绩一样的时候用c成绩降序来排序
SELECT * FROM `student1` ORDER BY `javaScore` DESC, `cScore` DESC
概念: 将一列数据作为整体,进行纵向计算
格式:
select 聚合函数(字段名) [as 别名] from 表名
分类:
count: 统计个数
max: 求最大值
min: 求最小值
sum: 求和
avg: 求平均值
-- DQL 查询学生的id总个数
SELECT COUNT(id) AS 学生总数 FROM `student1`
SELECT COUNT(`cScore`) AS 学生总数 FROM `student1` -- 执行的是5
SELECT COUNT(IFNULL(`cScore`,0)) AS 学生总数 FROM `student1` -- 执行的是6
-- DQL查询java语言分数最高的
SELECT MAX(`javaScore`) AS java最高分 FROM `student1`
-- DQL查询java语言分数最低的
SELECT MIN(IFNULL(`cScore`,0)) AS java最低分 FROM `student1`
-- DQL查询所有学生成绩的总和
SELECT SUM(`javaScore`) AS java成绩总和 FROM `student1`
SELECT SUM(IFNULL(`cScore`,0)) AS c成绩总和 FROM `student1`
注意聚合函数在计算的时候会排除null,导致少值,所以我们一般查询的字段都是主键,因为主键不能为空
格式:
group by 分组字段
注意:
分组之后查询字段: 分组字段,聚合函数
-- 按照性别分组,分别查找男生和女生的平均分
SELECT AVG(`javaScore`) AS java平均分, sex FROM `student1`GROUP BY `sex`
-- 按照性别分组,分别查找男生和女生的平均分即相应性别对应的人数
SELECT AVG(`javaScore`) AS java平均分,COUNT(`id`) AS 总数,sex FROM `student1` GROUP BY `sex`
-- 按照性别分组,分别查找男生和女生的平均分即相应性别对应的人数
-- 并且c语言成绩<60 分不参与分组
SELECT AVG(`javaScore`) AS java平均分,COUNT(`id`) AS 总数, sex
FROM `student1`
WHERE `cScore` >= 60
GROUP BY `sex`
-- 按照性别分组,分别查找男生和女生的平均分即相应性别对应的人数
-- 并且c语言成绩<60 分不参与分组
-- 并且分完类之后人数小于两人的不查看
SELECT AVG(`javaScore`) AS java平均数,COUNT(`id`) AS 总数, sex
FROM `student1`
WHERE `cScore` >= 60
GROUP BY `sex`
HAVING COUNT(`id`) >= 1
ORDER BY AVG(`javaScore`) DESC
其实总的来说,select执行的语句顺序是 先看where之后的条件,将所有条件满足之后的值给对应的表,然后在选择要拿出来的东西
1.where和having的区别
where:对分组前进行条件筛选
having:是对分组之后的结果进行条件筛选
2.where后面不能加聚合函数
having后面可以加聚合函数
格式:
limit 开始的索引 每页有几个
SELECT *
FROM `student1`
LIMIT 0,2
分页公式: a1 + (n -1) *d
a1:第一个索引的
n: 第几页
d: 每页有几个
**注意事项:**分页操作是mysql的方言
查询用户:
-- 在mysql数据库下的一个user表里面
USE `mysql`
SELECT * FROM `user`
创建用户:
create user ‘用户名’@‘主机名’ IDENTIFIED BY '密码’
CREATE USER 'Fancywt'@'loaclhost' IDENTIFIED BY '123456'
CREATE USER 'Fancyzs' IDENTIFIED BY '123456'
创建的两个账户(一个是只能在本地登录另一个可以在任何机子上登录但是两个用户所用的权限只有登录权限么有其他权限)
删除用户:
drop user ‘用户名’@‘主机名’;
DROP USER 'Fancywt'@'loaclhost' --删除的必须指定主机
DROP USER 'Fancyzs' -- 删除么有指定主机的
修改用户密码:
set password ‘用户名’@‘主机名’ = '新密码’
SET PASSWORD FOR 'Fancywt'@'localhost'='456789'; -- 指定主机
SET PASSWORD FOR 'Fancyzs='456789'; -- 主机不指定
查看用户的权限
show grants for ‘用户名’@'主机名’
SHOW GRANTS FOR 'Fancy'@'localhost' -- 查看指定用户的权限
SHOW GRANTS FOR 'Fancyzs' -- 查看主机不指定账户的
设置用户的权限
grant 权限名 on 数据库名.表名 to ‘用户名’@'主机名’
GRANT SELECT ON `test`.`dep` TO 'Fancywt'@'localhost'
GRANT SELECT,DELETE,ALTER ON `test`.`*` TO 'Fancywt'@'localhost'
GRANT ALL ON `test`.`*` TO 'Fancywt'@'localhost' -- 给Fancywt用户所有权限
撤销权限
revoke 权限名 on 数据库名.表名 from ‘用户名’@'主机名’
REVOKE ALL ON `*`.`*` FROM 'Fancywt'@'localhost'
REVOKE SELECT ON `test`.`dep` FROM 'Fancywt'@'localhost'
REVOKE SELECT,DELETE ON `test`.`dep` FROM 'Fancywt'@'localhost'
就相当是给属性添加约束条件 ----> 保证数据的有效性,完整性
主键约束:PRIMARY KEY
非空约束: NOT NULL
唯一约束: UNIQUE
外键约束:FOREIGN KEY
主键约束(PRIMARY KEY):(就是表中唯一的标识)
在修改表的时候
删除和增加
DROP
MODIFY
-- 在创建表的时候
-- 第一种方法
CREATE TABLE IF NOT EXISTS `people`(
`name` VARCHAR(20) NOT NULL,
`age` INT ,
`idcard` BIGINT(18) PRIMARY KEY -- 给这边加上主键
);
-- 第二种方法
CREATE TABLE IF NOT EXISTS `people`(
`name` VARCHAR(20) NOT NULL,
`age` INT ,
`idcard` BIGINT(18),
PRIMARY KEY(`idcard`) -- 给这边加上主键
);
-- 在修改表的时候
-- 删除主键(DROP删除)
ALTER TABLE `people` DROP PRIMARY KEY
-- 添加主键(MODIFY增加)
ALTER TABLE `people` MODIFY `idcard` BIG
INT(18) PRIMARY KEY
非空约束(NOT NULL):
在修改表的时候
修改和添加
MODIFY MODIFY
-- 在创建表的时候
CREATE TABLE IF NOT EXISTS `people`(
`name` VARCHAR(20) NOT NULL, -- 加了not null字段
`age` INT
);
-- 再修改表的时候
-- 添加非空约束
ALTER TABLE `people` MODIFY `name` VARCHAR(20) NOT NULL
-- 删除非空约束
ALTER TABLE `people` MODIFY `name` VARCHAR(20)
唯一约束(UNIQUE)
在修改表的时候
删除和添加
DROP MODIFY
-- 在创建表的时候
CREATE TABLE IF NOT EXISTS `people`(
`name` VARCHAR(20) NOT NULL,
`age` INT UNIQUE --添加unique字段
);
-- 在修改表的时候
ALTER TABLE `people` DROP INDEX `age`
ALTER TABLE `people` MODIFY `age` INT UNIQUE
外键约束(FROEIGN KEY)
在创建表的时候的格式:
CONSTRAINT + “你起的外键名称(一般用两个表之间加_进行命名)” + FROEIGN KEY (从表的外键名字) + references + 主表的名字 + 主表对应的字段名
在修改表的时候
删除和添加:
DROP ADD
-- 在创建表的时候
CREATE TABLE IF NOT EXISTS emp1(
`id` INT PRIMARY KEY AUTO_INCREMENT COMMENT '员工编号',
`name` VARCHAR(20) NOT NULL COMMENT '员工姓名',
`age` INT COMMENT '性别',
`dep_id` INT COMMENT '部门编号',
CONSTRAINT emp_dep_dk FOREIGN KEY (dep_id) REFERENCES dep (`id`)
);
-- 在修改表的时候
-- 删除外键
ALTER TABLE `emp` DROP FOREGIN KEY emp_dep_dk
-- 添加外键
ALTER TABLE `emp` ADD CONSTRAINT emp_dep_fk FOREIGN KEY (dep_id) REFERENCES dep (`id`)
-- 添加外键(并且让外键有特殊的功能)
-- 级联操作
-- 功能一: 主表对应的属性更新的时候外键也跟着更新
ALTER TABLE `emp` ADD CONSTRAINT emp_dep_fk FOREIGN KEY (dep_id) REFERENCES dep (`id`) ON UPDATE CASCADE
-- 功能二: 主表对应的属性删除的时候外键的那一行全部删除
ALTER TABLE `emp` ADD CONSTRAINT emp_dep_fk FOREIGN KEY (dep_id) REFERENCES dep (`id`) ON DELETE CASCADE
自动填充约束(AUTO_INCREMENT)
删除和添加: modify,modify
-- 在创建表的时候
CREATE TABLE IF NOT EXISTS `people`(
`id` INT AUTO_INCREMENT, -- 添加auto_increment 字段
`name` VARCHAR(20) NOT NULL,
`age` INT UNIQUE --添加unique字段
);
-- 再修改表的时候
-- 删除自动填充约束
ALTER TABLE `people` MODIFY `id` INT
ALTER TABLE `people` MODIFY `id` INT AUTO_INCREMENT
在创建表的时候的格式
create table 表名(
……
[constraint 外键名称 ] foreign key (外键列名称) references 关联表名称(关联列名称)
);
概念:
外键是表的一个特殊字段经常和主键约束一起使用,外键对应的表叫做 从表(子表) 主键对应的表就叫做 主表
特点:
外键经常是让两张表有关系的,为两张表建立数据连接,约束两个表中的一致性,完整性
比如: 一张表是 部门表 一张表是 员工表
员工表
员工名称 | 员工年龄 | 员工所在部门 | 员工部门所在位置 | 部门序号 |
---|---|---|---|---|
张三 | 16 | 开发部 | 深圳 | 1 |
李四 | 20 | 研究部 | 北京 | 2 |
开发部都在深圳 研究部都在北京
部门表
部门编号 | 部门 | 部门所在位置 |
---|---|---|
1 | 开发部 | 深圳 |
2 | 研究部 | 北京 |
一致性:
如果么有外键: 有一天研究部倒闭了 —> 那么里面的人肯定也要被删除 —> 但是在么有外键的情况下就算删除部门表上的部门 员工还在员工表上,这样就不能达成一致了
如果有了外键: 和上述情况一样 但是在删除的时候就会提示你这个部门里还有员工,要删除先把员工表中的员工删除吧 —> 达成一致
完整性:
如果么有外键: 有一天一个员工来他是人才部的,但是么有这个部门,他只能现存上但是部门序号就不知道填什么就只能为非1,2的其他数字 这样这个员工的信息根本不确定不完整(但是还是添加上了 – 但是不知道在哪个部门) 从表有 但是主表么有,导致从表不完整(查不到人才部)
如果有了外键: 还是上述情况,你现在来之后想要添加部门序号3(人才部)现在部门表上么有就会给你提示,根本么有这个表,你就不能添加(根本添加不上,那么两个表都完整了,这个从主有从表就有)
概念:
主表更新数据从表就怎么更新数据
主表删除外键对应的属性,从表直接外键(和主表一致的)的那一行
一个学生一个学生证号码
一个班级对应多个学生,一个学生对应多个成绩
查询方法: 外键
一个学生对应多门课程,一门课程可以对应多个学生
查询方法: 再建一个中间表
-- 创建表 书籍表,书籍分类表,用户表
-- 书籍分类表 booksort
CREATE TABLE IF NOT EXISTS `booksort`(
`bsid` INT AUTO_INCREMENT PRIMARY KEY, -- 分类id
`bsname` VARCHAR(20)
);
INSERT INTO `booksort` (`bsid`,`bsname`) VALUES
()
-- 书籍表
CREATE TABLE IF NOT EXISTS `book`(
`bid` INT AUTO_INCREMENT PRIMARY KEY,
`bname` VARCHAR(20),
`bdate` DATE,
`sid` INT,
CONSTRAINT `bs_b_fk` FOREIGN KEY (`sid`) REFERENCES `booksort` (`bsid`)
);
INSERT INTO `book` (``)
-- 用户表
CREATE TABLE IF NOT EXISTS `user`(
`uid` INT AUTO_INCREMENT PRIMARY KEY,
`uname` VARCHAR(20) NOT NULL,
`upass` VARCHAR(20) NOT NULL
);
-- 用户表和数据表联系表
CREATE TABLE IF NOT EXISTS `userandbook`(
`uid1` INT,
`bid1` INT,
`borrowdate` TIMESTAMP DEFAULT time_,
PRIMARY KEY(`uid1`,`bid1`),
-- primary key(`uid1`)
-- references `user` (`uid`), 第二种建立外键的格式
CONSTRAINT `uab_u` FOREIGN KEY (`uid1`) REFERENCES `user` (`uid`),
CONSTRAINT `uab_b` FOREIGN KEY (`bid1`) REFERENCES `book` (`bid`)
);
**简而言之:**数据库的设计模式(给每张表创建定义规范)
不符合规范表:
符合规范表:
第二范式:
在第一范式的前提下,非码属性必须完全依赖于候选码
第三范式:
在第一第二范式的前提下,任何非主属性不依赖于其他非主属性
函数依赖: 完全函数依赖和部分函数依赖,传递函数依赖的总称
完全函数依赖:
如果属性组A(学号,课程)能推出属性B(成绩) 但是学号推不出来成绩,课程也推不出成绩 那么这时候的B就完全依赖于A
部份函数依赖:
如果属性组A(主任id,系名)能推出属性B(系主任) 并且主任id也可以推出系主任,系名也可以推出系主任, 这样就成B部份依赖于A
传递函数依赖:
如果属性A(学号)可以推出属性B(系名)但是属性B不可以推出属性A,属性B(系名)可以推出属性C(系主任)但是C推不出B,那么这样属性C就传递依赖A
候选码:
当一个属性或者一个属性组确定的情况下,其他的所有属性都能被用依赖推出来,那么就这个属性或者属性组为等候码
主属性和非主属性:
主属性: 候选码里面的全部属性(候选码中有几个就有几个主属性)
非主属性: 除候选码外的表中的其他属性
第二范式的详解:
分析:
候选码:(学号,课名)
非属性:(姓名,系名,系主任,成绩)
因为候选码里有两个属性,则就称 学号和课名联合主键,他们可以通过依赖推出所有的非属性.
这样将学号的所有部份依赖全部绘制到一张表上,主表杀那个就只有学号 课名 分数 ------ 消除部份依赖
画图看看:
那么就要画两张表
1:学号 和 姓名 系名 系主任
2:学号 课名 和 分数
第三范式的详解:任何非主属性不依赖于其他非主属性(消除传递依赖)
第一张表: 非主属性 (系名,姓名,系主任)
第二张表: 非主属性(分数)
分析:
第二张表显然非主属性只有一个不可能产生什么函数依赖
第一张表:非主属性的系主任依赖于非主属性系名,这样导致学号和系主任构成传递函数依赖,我们就是要消除它,所以创建第三张表
所以 最终一张表应该通过规范 变成三张表
最初的表:
经过第二范式:
经过第三范式:
备份:
mysqldump -u用户名 -p密码 数据库名 > 保存的路径
C:\Users\Fancy>mysql -uroot -p123456 jdbcstudy > D:\\jdbc.sql
还原:
mysql -uroot -p123456
create database jdbcstudy;
use jdbcstudy;
source D:\\jdbc1.sql;
先要选中
再讲之前补充一个下问题(乱码问题)
我们的数据库不仅有编码格式 数据库中的表也有编码格式 表中的每一列也有编码格式 我们可以更改 数据库的编码和数据库表的编码但是怎么查看每一列的编码呢
/*
解决中文乱码问题
*/
SELECT * FROM `dep`
SHOW FULL COLUMNS FROM `dep` -- 查看每一列的编码
ALTER TABLE `dep` CONVERT TO CHARACTER SET utf8; -- 将每一列编码改成utf8
隐式内连接:使用where
SELECT
e.`id`,e.`name`,e.`gender`,d.`id`,d.`name`
FROM
`emp` AS e,`dep` AS d
WHERE
e.`dep_id` = d.`id` -- 按照id的顺序排序(默认排序法)
ORDER BY e.`id`
语法:
select 字段列表 from 表名1 [inner] join 表名2 on 加入条件
SELECT
e.`id`,e.`name`,e.`gender`,d.`id`,d.`name`
FROM
`dep` AS d
INNER JOIN -- inner 可以省略
`emp` AS e
ON
e.`dep_id` = d.`id`
查询出来的排序方式以主表外键对应的顺序往下排序
格式: select 字段名字 from 表名 left/right [outer] join 表名 on 判断条件
如图左上角的那一幅图
-- 左连接
SELECT
e.`dep_id`,e.`name`,e.`gender`,d.`id`,d.`name`,`salary`
FROM
`emp` e
LEFT OUTER JOIN
`dep` d
ON
e.`dep_id` = d.`id`
注意: emp表里面的dep_id是1,2,3,4 dep表里面的id是1,2,3 那么因为dep表里面的id么有4那么就查询的结果就是e.dep_id=4的时候,后面的值全是null ,而不是将dep_id=4删除
**右表连接:**两张相连接查询右表和(右表和左表的交叉的部分)
如图右上角的那一幅图
-- 右外连接
SELECT
e.`id`,e.`name`,e.`gender`,d.`id`,d.`name`
FROM
`emp` e
RIGHT OUTER JOIN
`dep` d
ON
e.`id` = d.`id`
注意:dep表里面的id只有1,2,3所以查出来的表里面e的id只能是1,2,3其他的4,5,6都会被删除
子连接
概念:
将自己表中的某些东西先查出来当作一个查询结果,将这个查询结果在应用到第二次自己表查询自己表的时候
分类:
当第一次查询表的结果是 一行一列(就相当是是一个值) 子查询结果可以作为运算条件与运算符一起使用
-- 查询员工的工资在平均工资之上的员工信息
SELECT * FROM `emp` WHERE `salary` >= (SELECT AVG(`salary`) FROM `emp`)
-- 第一次查询结果就是salary的平均值 相当是一个值
当第一次查询表的结果是 多行单列的(相当是好多个值)(就是一个字段下面的多个属性值,这些值代表的都是id或者都是地址) 子查询的结果可以作为in里面的集合使用
-- 查询部门在市场部和研发部的员工信息
SELECT * FROM `emp` WHERE `dep_id` IN ((SELECT id FROM `dep` WHERE `dep`.`name` IN ('市场部','研发部')))
-- 第一查询的是市场部和研究部门的id(这是两个id所以是多行单列) 查到id之后在查部门的id和员共表里面的部门id和查出来的部门表的id条件符合的员工的信息
当第一次查询结果是 多行多列(这就相当是一张表了) 子查询结果可以作为一张表来使用
-- 查询入职日期在 2015-05-28 查询的员工信息以及这些员工所在的部门
SELECT
t1.*,t2.`name`
FROM
(SELECT * FROM `emp` WHERE `emp`.`join_date` > '2015-05-28') t1,
`dep` t2
WHERE
t1.dep_id = t2.id
-- 第一查询的是入职日期在2015-05-28之前的所有员工的信息,这就相当是一张新查出来的员工表,将这个表和部门表尽心隐式内连接在查询一张新表
你做一件事情(事务)的步骤(事务里面的sql语句),这件事情就叫做事务,事情里面的步骤就叫做sql执行语句,这件事情如果办砸–里面的步骤就相当是白做(要么全部成功,要么全部失败),而失败和成功都取决于这件事情(管理sql执行语句的容器是否被提交)是否办成功
举个形象的列子:
现在我要转账给小明:
①:我脑子里面要知道我要办这件事情 — > 开启事务
②:我的账户减少500给小明 ----> 我的用户 - 500;
③:小明账户加500 ---- > 小明的用户 + 500;
④:事情办完了小明确定无误,我也同意要转 — > 提交这个事情
⑤:事情办完了但是小明的账户上么有添加前 — > 出现错误回滚到原来的数据
官方概述:如果一个包含多个步骤的业务操作,被事务管理,那么这些操作要么同时成功,要么同时失败
根据上图:
-- 操作成功
USE `test`
SELECT * FROM `test`
-- 开启事务
START TRANSACTION;
-- 操作业务
UPDATE `test` SET `money` = `money` - 500 WHERE `name` = '张三';
UPDATE `test` SET `money` = `money` + 500 WHERE `name` = '李四';
-- 事务操作成功
COMMIT; -- 提交
-- 操作步骤中出现失败操作
USE `test`
SELECT * FROM `test`
-- 开启事务
START TRANSACTION;
-- 操作业务
UPDATE `test` SET `money` = `money` - 500 WHERE `name` = '张三';
出现错误了 。。。
UPDATE `test` SET `money` = `money` + 500 WHERE `name` = '李四';
-- 事务操作失败
ROLLBACK; -- 回滚
-- 出现错误: 张三转了钱但是里斯么有接收到
在事务么有被提交或者回滚前数据都会被更改(只是当前的会话),但是当事务被提交数据就被永久更改,在数据回滚的之后数据就会恢复到原来的值(在么有被提交前或者回滚前更改都是存在于内存中的)
当出现两个会话窗口的时候,两个会话同时开启事务一个会话改变账户的金额不会影响另一个窗口的,但是一旦提交就会影响到
注意: MySQL的默认提交方式是自动提交
事务的提交方式:
查看当前的事务提交方式
-- 查看当前会话的提交方式
SELECT @@autocommit
修改当前会话的事务提交方式
-- 修改当前会话的提交方式
SET @@autocommit = 0;
注意: 修改完之后要关闭一sqlyog在查询才可以查看出来(对于以前的老版本)
修改完之后再查就改变了(新版本)
问题: 那么执行 start transaction 是不是就相当是开启了手动提交呢? 开启之后查看提交方式会不会@@autocommit=0?
解: 相当是开启了手动提交 但是查询的时候autocommit还是等于1;
原子性: (Atomicity)
意思就是开启事务之后的每一条sql语句要么都执行成功提交,要么执行失败回滚,不能出现sql出现错误之后还提交,那就会变成一个废弃事务一直存在,废弃事务的数量如果很多会让数据库的性能降低
一致性:(Consistency)
意思就是开启事务之后执行的每一条sql语句虽然都执行成功了,但是不能改变原来规则,比如总共5000元在相互转账,你不能执行了个sql变成3000元相互转账把人家的2000元无缘无故给减没有了
隔离性:(Isolation)
意思就是每一个事务在执行的时候不会影响其他事务执行,每个事务的执行都是独立的
永久性(Durability)
意思就是每一个提交了的数据都会被永久保存,就算数据库发生故障,提交的数据也不会发生改变
概述:
多个事务之间是相互隔离的,是相互独立的。但是如果多个事务(开启不同的会话,一个会话种不会让你开启两个事务)操作同一批数据,则会引发一些问题,设置值不同的隔离级别就可以解决这些问题
多个事务操作同一数据存在的问题:
虚读和幻读的区别:
隔离级别:
read uncommitted: (可以读到别人未提交的数据)
出现问题: 脏读, 不可重复读, 幻读
read committed: (提交后才可以读到)
出现问题: 不可重复读,幻读
repeatable read : 可重复读(两个会话保持独立,你干你的事情我干我的事情) — > 当两个事务都在开启状态下(还么提交)对同一个数据进行改变(事务1先给张三 -500,然后事务2给张三 -500,但是事务1可以提交,事务2现在不能提交也不能干其他事情,事务1提交完之后,事务2直接被提交),先进行改变的那个提交之后,第二个改变数据直接提交就不用手动提交了
出现问题: 幻读
serializable : 串行化
可以解决任何问题
注意:
隔离级别从1-4安全性越来越高,但是效率越来越低
设置隔离级别
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ; -- 这个是对所有的会话
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; -- 这个是对当前的会话
查看隔离级别
SELECT @@transaction_isolation
演示: (初始值: 张三和李四的money都是2000)
第一个: read uncommited 读未提交
SET GLOBAL TRANSACTION ISOLATION LEVEL READ UNCOMMITED;
创建新的会话框:
第一个窗口(第一个会话框)
SELECT * FROM `test`
-- 模拟转账
START TRANSACTION; -- 开启事务
-- 操作业务
UPDATE test SET test.`money`=test.`money`-500 WHERE `name` = '张三';
-- 出错了.....
UPDATE test SET test.`money`=test.`money`+500 WHERE `name` = '李四';
-- 成功 提交
COMMIT
-- 失败 回滚
ROLLBACK; -- 回滚
第二个窗口(第二个会话框)
SELECT * FROM `test`
-- 开启事务
START TRANSACTION;
-- 操作业务
UPDATE `test` SET `money` = `money` - 500 WHERE `name` = '张三';
UPDATE `test` SET `money` = `money` + 500 WHERE `name` = '李四';
-- 事务操作成功
COMMIT; -- 提交
-- 事务操作失败
ROLLBACK; -- 回滚
1.首先同时开启两个事务
2.使用会话框1的操作业务sql语句
3.然后查看会话1和会话2的select结果
4.看结果还么提交的时候,会话2就已经变了-----> 脏读,不可重复读,幻读
5.执行回滚返回原来的值
第二个 read committed: 读已提交
先执行设置语句让他变成read committed
SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED
第一个会话:
SELECT * FROM `test`
-- 模拟转账
START TRANSACTION; -- 开启事务
-- 操作业务
UPDATE test SET test.`money`=test.`money`-500 WHERE `name` = '张三';
-- 出错了.....
UPDATE test SET test.`money`=test.`money`+500 WHERE `name` = '李四';
-- 成功 提交
COMMIT
-- 失败 回滚
ROLLBACK; -- 回滚
第二个会话:
SELECT * FROM `test`
START TRANSACTION; -- 开启事务
-- 操作业务
UPDATE test SET test.`money`=test.`money`-500 WHERE `name` = '张三';
-- 出错了.....
UPDATE test SET test.`money`=test.`money`+500 WHERE `name` = '李四';
-- 成功 提交
COMMIT
-- 失败 回滚
ROLLBACK; -- 回滚
1.同时开启两个事务
2.执行窗口1的开启事务低下的sql语句
3.执行窗口1和窗口2的select语句
这样删除了脏读的效果但是
4.将窗口1的事务提交在执行窗口2的select语句
5.这样还是会把窗口2的数据改变,所以执行两次select语句会得到不同的结果
么有解决 可重复读和幻读
第三个: repeatable read 可重复读
先设值隔离级别为 repeatable read
SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ
第一个会话:
SELECT * FROM `test`
-- 模拟转账
START TRANSACTION; -- 开启事务
-- 操作业务
UPDATE test SET test.`money`=test.`money`-500 WHERE `name` = '张三';
-- 出错了.....
UPDATE test SET test.`money`=test.`money`+500 WHERE `name` = '李四';
-- 成功 提交
COMMIT
-- 失败 回滚
ROLLBACK; -- 回滚
第二个会话:
SELECT * FROM `test`
START TRANSACTION; -- 开启事务
-- 操作业务
UPDATE test SET test.`money`=test.`money`-500 WHERE `name` = '张三';
-- 出错了.....
UPDATE test SET test.`money`=test.`money`+500 WHERE `name` = '李四';
-- 成功 提交
COMMIT
-- 失败 回滚
ROLLBACK; -- 回滚
1.同时开启两个事务
2.执行窗口1的开启事务下面的sql语句
3.执行两个窗口的select语句
4.提交窗口1的事务
5.执行两个窗口的select语句
即解决了脏读和不可重复读但是解决幻读了吗?
– 么有解决
我们来看看幻读
第一个会话:
SELECT * FROM `test`
-- 模拟转账
START TRANSACTION; -- 开启事务
-- 操作业务
UPDATE test SET test.`money`=test.`money`-500 WHERE `name` = '张三';
-- 出错了.....
UPDATE test SET test.`money`=test.`money`+500 WHERE `name` = '李四';
-- 成功 提交
COMMIT
-- 失败 回滚
ROLLBACK; -- 回滚
第二个会话:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t49swp7J-1609173456514)(%E5%B9%BB%E8%AF%BB.png)] SELECT * FROM `test`
START TRANSACTION; -- 开启事务
-- 操作业务
UPDATE test SET test.`money`=test.`money`-500 WHERE `name` = '张三';
-- 出错了.....
UPDATE test SET test.`money`=test.`money`+500 WHERE `name` = '李四';
-- 成功 提交
COMMIT
-- 失败 回滚
ROLLBACK; -- 回滚
1.同时开启两个事务
2.执行窗口1的开启事务下面的sql语句
3.执行两个窗口的select语句
4.这时候不要提交窗口1,先执行窗口2的开启事务底下的sql语句
窗口2:会出现
过一段时间就会报错误
因为出现了幻读----> 在两个窗口同时修改同一个数据,而且都么提交 — > 系统就报错误
这时候只有提交窗口1的数据窗口2才能被提交 并且连带窗口2的事务也提交了
幻读举例2:(刚开始只有张三和李四并且id为1,隔离等级为repeatable read)
刚开始的表窗口1和窗口2都是:
窗口1:
START TRANSACTION; -- 开启事务
INSERT INTO `test` (`name`,`money`,`id`) VALUES ('王五',2000,1)
窗口2:
START TRANSACTION; -- 开启事务
-- 改变 id = 1 的变成 id =2
UPDATE `test` SET `id` = 2 WHERE `id` = 1;
同时开启事务
执行窗口2开启事务下面的sql语句
查看窗口1和窗口2的select语句
然后执行窗口1的开启事务低下的sql语句
查看窗口1和窗口2的select语句
等一会儿直接报错
6.在窗口1等待的时候(么有停止的时候),提交窗口2的事务
7.这时候窗口1就执行成功了!!!
8.查看窗口1和窗口2的select语句
9.提交窗口1的事务
10.查看窗口1和窗口2的select语句
这样窗口2明显把id=1改成id=2,又产生了幻觉为什么突然还多出来了王五id=1呢? --> 幻读么有解决
幻读和重读的区别:
幻读是两个用户在么提交之前同时更改数据
虚读是两个用户其中一个修改完之后并且提交了导致了另一个用户在么有提交前第二次查询和第一次查询(就是第一个用户修改了但是么提交的时候,第二个用户就查的这一次成为第一次)结果不一样