DQL查询数据(最重点)
DQL
(Data Query LANGUAGE:数据查询语言)
- 所有的查询操作都用它 Select
- 简单的查询,复杂的查询它都能做
- 数据库中最核心的语言,最重要的语句
- 使用频率最高的语句
Select完整的语法:
SELECT语法
SELECT [ALL|DISTINCT] {*|table.*|[table.field1[as alias1][,table.field2[as alias2]][,...]]} FROM table_name [as table_alias] [left | right | inner join table_name2] -- 联合查询 [WHERE ...] -- 指定结果需满足的条件 [GROUP BY ...] -- 指定结果按照哪几个字段来分组 [HAVING] -- 过滤分组的记录必须满足的次要条件 [ORDER BY ...] -- 指定查询记录按一个或多个条件排序 [LIMIT {[offset,]row_count | row_countOFFSET offset}]; -- 指定查询的记录从哪条至哪条
注意:[]括号代表可选的,{}括号代表必选的
指定查询字段
-- 查询全部的学生 SELECT 字段 FROM 表 SELECT * FROM student -- 查询指定字段 SELECT `StudentNo`,`StudentName` FROM student -- 别名,给结果起一个名字 AS 可以给字段起别名,也可以给表起别名 SELECT `StudentNo` AS '学号',`StudentName` AS '学生姓名' FROM `student` AS `s` -- 函数 Concat(a,b) SELECT CONCAT('姓名:',`StudentName`) AS '新名字' FROM `student`
语法:SELECT 字段,... FROM 表
有的时候,列名字不是那么的见名知意,我们起别名 AS 字段名 as 别名 表名 as 别名
去重 distinct
作用:去除SELECT查询出来的结果中重复的数据,重复的数据只查询一条
-- 查询一下有哪些同学参加了考试,成绩 SELECT * FROM `result` -- 查询全部的考试成绩 -- 查询有哪些同学参加了考试 SELECT `StudentNo` FROM `result` -- 发现重复数据,去重 SELECT DISTINCT `StudentNo` FROM `result`
数据库的列(表达式)
SELECT VERSION() -- 查询系统版本 SELECT 100*3-1 AS `计算结果` -- 用来计算 SELECT @@auto_increment_increment -- 查询自增的步长(变量) -- 学院考试成绩 + 1分查看 SELECT `StudentNo`,`StudentResult`+1 AS '提分后' FROM `result`
数据库中的表达式:文本值,列,Null,函数,计算表达式,系统变量
SELECT 表达式 FROM 表
where条件子句
作用:检索数据中符合条件的值
搜索的条件由一个或者多个表达式组成!结果 布尔值
逻辑运算符
运算符 | 语法 | 描述 |
and && | a and b a&&b | 逻辑与,两个都为真,结果为真 |
or || | a or b a||b | 逻辑或,其中一个为真,则结果为真 |
Not ! | not a !a | 逻辑非,真为假,假为真! |
尽量使用英文字母
-- ============================================== where ======================================= SELECT `StudentNo`,`StudentResult` FROM `result` -- 查询考试成绩在 95-100 分之间 SELECT `StudentNo`,`StudentResult` FROM `result` WHERE `StudentResult`>=95 AND `StudentResult`<=100 -- and && SELECT `StudentNo`,`StudentResult` FROM `result` WHERE `StudentResult`>=95 && `StudentResult`<=100 -- 模糊查询(区间) SELECT `StudentNo`,`StudentResult` FROM result WHERE `StudentResult` BETWEEN 95 AND 100 -- 除了900号学生之外的同学的成绩 SELECT `StudentNo`,`StudentResult` FROM result WHERE `StudentNo`!=900 -- != not SELECT `StudentNo`,`StudentResult` FROM result WHERE NOT `StudentNo` = 900
模糊查询:比较运算符
运算符 | 语法 | 描述 |
IS NULL | a is null | 如果操作符为NULL,结果为真 |
IS NOT NULL | a is not null | 如果操作符为not null,结果为真 |
BETWEEN | a between b and c | 若a在b和c之间,则结果为真 |
Like | a like b | SQL匹配,如果a匹配b,则结果为真 |
In | a in(a1,a2,a3...) | 假设a在a1,或者a2...其中的某一个值中,结果为真 |
-- ===================== 模糊查询 ============================ -- 查询姓刘的同学 -- like结合 %(代表0到任意个字符) _(一个字符) SELECT `StudentNo`,`StudentName` FROM `student` WHERE `StudentName` LIKE '刘%' -- 查询姓刘的同学,名字后面只有一个字的 SELECT `StudentNo`,`StudentName` FROM `student` WHERE `StudentName` LIKE '刘_' -- 查询姓刘的同学,名字后面只有两个字的 SELECT `StudentNo`,`StudentName` FROM `student` WHERE `StudentName` LIKE '刘__' -- 查询名字中间有嘉字的同学 SELECT `StudentNo`,`StudentName` FROM student WHERE `StudentName` LIKE '%嘉%' -- ====================== in(具体的一个或者多个值) ==================== -- 查询 1001,1002,1003号学员 SELECT `StudentNo`,`StudentName` FROM `student` WHERE StudentNo IN (1001,1002,1003) -- 查询在北京的学生 SELECT `StudentNo`,`StudentName` FROM `student` WHERE `Address` IN ('安徽','河南洛阳') -- ============== null not null ======================= -- 查询地址为空的学生 null '' SELECT `StudentNo`,`StudentName` FROM `student` WHERE `Address`='' OR `Address` IS NULL -- 查询有出生日期的同学 不为空 SELECT `StudentNo`,`StudentName` FROM `student` WHERE `BornDate` IS NOT NULL -- 查询没有出生日期的同学 为空 SELECT `StudentNo`,`StudentName` FROM `student` WHERE `BornDate` IS NULL
联表查询
JOIN 对比
操作 | 描述 |
inner join | 如果表中至少有一个匹配,就返回行 |
left join | 会从左表中返回所有的值,即使右表中没有匹配 |
right join | 会从右表中返回所有的值,即使左表中没有匹配 |
-- ================== 联表查询 ========================== -- 查询参加了考试的同学(学号,姓名,科目编号,分数) SELECT * FROM student SELECT * FROM result /* 思路 1. 分析需求,分析查询的字段来自那些类,(连接查询) 2. 确定使用哪种连接查询? 7种 确定交叉点(这两个表中那个数据是相同的) 判断的条件:学生表中的 StudentNo = 成绩表中的 StudentNo */ -- join(连接的表) on(判断的条件) 连接查询 -- where 等值查询 SELECT s.`StudentNo`,`StudentName`,`SubjectNo`,`StudentResult` FROM student AS s INNER JOIN result AS r WHERE s.StudentNo = r.StudentNo -- Right Join SELECT s.`StudentNo`,`StudentName`,`SubjectNo`,`StudentResult` FROM student s RIGHT JOIN result r ON s.StudentNo = r.StudentNo -- Left Join SELECT s.`StudentNo`,`StudentName`,`SubjectNo`,`StudentResult` FROM student s LEFT JOIN result r ON s.StudentNo = r.StudentNo -- 查询缺考的同学 SELECT s.`StudentNo`,`StudentName`,`SubjectNo`,`StudentResult` FROM student s LEFT JOIN result r ON s.StudentNo = r.StudentNo WHERE `Studentresult` IS NULL -- 思考题(查询参加了考试的同学信息:学号,学生姓名,科目名,分数) SELECT s.`StudentNo`,`StudentName`,`SubjectName`,`StudentResult` FROM student s LEFT JOIN result r ON s.StudentNo = r.StudentNo RIGHT JOIN `subject` su ON r.SubjectNo = su.SubjectNo -- 我要查询哪些数据 select ... -- 从哪几个表中查 FROM 表 XXX Join 连接的表 on 交叉条件 -- 假设存在一种多张表查询,慢慢来,先查询两张表然后再慢慢增加 -- From a left join b -- From a right join b
自连接 (了解)
自己的表和自己的表连接,核心:一张表拆为两张一样的表即可
父类
categoryid | categoryName |
2 | 信息技术 |
3 | 软件开发 |
5 | 美术设计 |
子类
pid | categoryid | categoryName |
3 | 4 | 数据库 |
2 | 8 | 办公信息 |
3 | 6 | web开发 |
5 | 7 | ps技术 |
操作:查询父类对应的子类关系
父类 | 子类 |
信息技术 | 办公信息 |
软件开发 | 数据库 |
软件开发 | web开发 |
美术设计 | ps技术 |
-- ================== 自连接 ============================= CREATE TABLE `category` ( `categoryid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '主题id', `pid` INT(10) NOT NULL COMMENT '父id', `categoryName` VARCHAR(50) NOT NULL COMMENT '主题名字', PRIMARY KEY (`categoryid`) )ENGINE = INNODB AUTO_INCREMENT = 9 DEFAULT CHARSET = utf8 INSERT INTO `category` (`categoryid`,`pid`,`categoryName`) VALUES ('2','1','信息技术'), ('3','1','软件开发'), ('4','3','数据库'), ('5','1','美术设计'), ('6','3','web开发'), ('7','5','ps技术'), ('8','2','办公信息'); -- 查询父子信息:把一张表看为两个一模一样的表 SELECT a.`categoryName` AS '父栏目',b.`categoryName` AS '子栏目' FROM `category` AS a,`category` AS b WHERE a.`categoryid` = b.`pid` -- 查询学员所属的年级(学号,学生的姓名,年级名称) SELECT StudentNo,StudentName,GradeName FROM student s INNER JOIN grade g ON s.GradeId = g.GradeId -- 查询参加了数据库结构-1考试的同学信息:学号,学生姓名,科目名,分数 SELECT s.`StudentNo`,s.`StudentName`,`SubjectName`,`StudentResult` FROM `student` s INNER JOIN `subject` sub ON s.GradeId = sub.GradeId INNER JOIN `result` r ON s.StudentNo = r.StudentNo WHERE SubjectName = '数据库结构-1'
分页和排序
排序
-- ========================== 分页 limit 和排序 order by ===================================== -- 排序:升序ASC,降序DESC -- ORDER BY 通过哪个字段排序,怎么排 -- 查询的结果根据 成绩降序 排序 SELECT s.`StudentNo`,s.`StudentName`,`SubjectName`,`StudentResult` FROM `student` s INNER JOIN `subject` sub ON s.GradeId = sub.GradeId INNER JOIN `result` r ON s.StudentNo = r.StudentNo WHERE SubjectName = '数据库结构-1' ORDER BY StudentResult ASC
分页
-- 100万 -- 为什么分页 -- 缓解数据库压力,给人的体验更好,瀑布流 -- 网页应用: -- 分页,每页只显示五条数据 -- 语法:limit 起始值,页面的大小 -- LIMIT 0,5 1~5 -- LIMIT 1,5 2~6 -- LIMIT 6,5 SELECT s.`StudentNo`,s.`StudentName`,`SubjectName`,`StudentResult` FROM `student` s INNER JOIN `subject` sub ON s.GradeId = sub.GradeId INNER JOIN `result` r ON s.StudentNo = r.StudentNo WHERE SubjectName = '数据库结构-1' ORDER BY StudentResult ASC LIMIT 0,5 -- 第一页 limit 0,5 (1-1)*5 -- 第二页 limit 5,5 (2-1)*5 -- 第三页 limit 10,5 (3-1)*5 -- 第N页 limit 0,5 (n-1)*pageSize,pageSize -- 【pageSize:页面大小,n 当前页】 -- 【(n-1)*pageSize起始值】 -- 【n:当前页】 -- 【数据总数/页面大小 = 总页数】
语法:limit (查询起始下标,pageSize)
子查询
where(这个值是计算出来的)
本质:在where语句中嵌套一个子查询语句
where(select*from)
CREATE DATABASE IF NOT EXISTS `school`; -- 创建一个school数据库 USE `school`; -- 创建年级表 DROP TABLE IF EXISTS `grade`; CREATE TABLE `grade`( `GradeID` INT(11) NOT NULL AUTO_INCREMENT COMMENT '年级编号', `GradeName` VARCHAR(50) NOT NULL COMMENT '年级名称', PRIMARY KEY (`GradeID`) ) ENGINE=INNODB AUTO_INCREMENT = 6 DEFAULT CHARSET = utf8; -- 插入年级数据 INSERT INTO `grade` (`GradeID`,`GradeName`) VALUES (1,'大一'),(2,'大二'),(3,'大三'),(4,'大四'),(5,'预科班'); -- 创建成绩表 DROP TABLE IF EXISTS `result`; CREATE TABLE `result`( `StudentNo` INT(4) NOT NULL COMMENT '学号', `SubjectNo` INT(4) NOT NULL COMMENT '课程编号', `ExamDate` DATETIME NOT NULL COMMENT '考试日期', `StudentResult` INT(4) NOT NULL COMMENT '考试成绩', KEY `SubjectNo` (`SubjectNo`) )ENGINE = INNODB DEFAULT CHARSET = utf8; -- 插入成绩数据 这里仅插入了一组,其余自行添加 INSERT INTO `result`(`StudentNo`,`SubjectNo`,`ExamDate`,`StudentResult`) VALUES (1000,1,'2013-11-11 16:00:00',85), (1000,2,'2013-11-12 16:00:00',70), (1000,3,'2013-11-11 09:00:00',68), (1000,4,'2013-11-13 16:00:00',98), (1000,5,'2013-11-14 16:00:00',58); -- 创建学生表 DROP TABLE IF EXISTS `student`; CREATE TABLE `student`( `StudentNo` INT(4) NOT NULL COMMENT '学号', `LoginPwd` VARCHAR(20) DEFAULT NULL, `StudentName` VARCHAR(20) DEFAULT NULL COMMENT '学生姓名', `Sex` TINYINT(1) DEFAULT NULL COMMENT '性别,0或1', `GradeId` INT(11) DEFAULT NULL COMMENT '年级编号', `Phone` VARCHAR(50) NOT NULL COMMENT '联系电话,允许为空', `Address` VARCHAR(255) NOT NULL COMMENT '地址,允许为空', `BornDate` DATETIME DEFAULT NULL COMMENT '出生时间', `Email` VARCHAR (50) NOT NULL COMMENT '邮箱账号允许为空', `IdentityCard` VARCHAR(18) DEFAULT NULL COMMENT '身份证号', PRIMARY KEY (`StudentNo`), UNIQUE KEY `IdentityCard`(`IdentityCard`), KEY `Email` (`Email`) )ENGINE=MYISAM DEFAULT CHARSET=utf8; -- 插入学生数据 其余自行添加 这里只添加了2行 INSERT INTO `student` (`StudentNo`,`LoginPwd`,`StudentName`,`Sex`,`GradeId`,`Phone`,`Address`,`BornDate`,`Email`,`IdentityCard`) VALUES (1000,'123456','张伟',0,2,'13800001234','北京朝阳','1980-1-1','text123@qq.com','123456198001011234'), (1001,'123456','赵强',1,3,'13800002222','广东深圳','1990-1-1','text111@qq.com','123456199001011233'); -- 创建科目表 DROP TABLE IF EXISTS `subject`; CREATE TABLE `subject`( `SubjectNo` INT(11) NOT NULL AUTO_INCREMENT COMMENT '课程编号', `SubjectName` VARCHAR(50) DEFAULT NULL COMMENT '课程名称', `ClassHour` INT(4) DEFAULT NULL COMMENT '学时', `GradeID` INT(4) DEFAULT NULL COMMENT '年级编号', PRIMARY KEY (`SubjectNo`) )ENGINE = INNODB AUTO_INCREMENT = 18 DEFAULT CHARSET = utf8; -- 插入科目数据 INSERT INTO `subject`(`SubjectNo`,`SubjectName`,`ClassHour`,`GradeID`) VALUES (1,'高等数学-1',110,1), (2,'高等数学-2',110,2), (3,'高等数学-3',100,3), (4,'高等数学-4',130,4), (5,'C语言-1',110,1), (6,'C语言-2',110,2), (7,'C语言-3',100,3), (8,'C语言-4',130,4), (9,'Java程序设计-1',110,1), (10,'Java程序设计-2',110,2), (11,'Java程序设计-3',100,3), (12,'Java程序设计-4',130,4), (13,'数据库结构-1',110,1), (14,'数据库结构-2',110,2), (15,'数据库结构-3',100,3), (16,'数据库结构-4',130,4), (17,'C基础',130,1)
-- ===================== where ============================ -- 1、查询数据库结构-1的所有考试结果(学号,科目编号,成绩),降序排列 -- 方式一:使用连接查询 SELECT `StudentNo`,r.`SubjectNo`,`StudentResult` FROM result r INNER JOIN `subject` sub ON r.`SubjectNo`=sub.`SubjectNo` WHERE `SubjectName` = '数据库结构-1' -- 方式二:使用子查询(由里及外) -- 查询所有数据库结构-1的学生学号 SELECT `StudentNo`,`SubjectNo`,`StudentResult` FROM result WHERE StudentNo = ( SELECT SubjectNo FROM `subject` WHERE SubjectName = '数据库结构-1' ) ORDER BY StudentResult DESC -- 查询课程为 高等数学-2 且分数不小于80的同学的学号和姓名 SELECT r.StudentNo,StudentName FROM student s INNER JOIN result r ON s.`StudentNo`=r.`StudentNo` INNER JOIN `subject` sub ON sub.`SubjectNo`=r.`SubjectNo` WHERE `SubjectName`='高等数学-2' AND `StudentResult`>=80 -- 分数不小于80分的学生的学号和姓名 SELECT DISTINCT r.`StudentNo`,`StudentName` FROM student s INNER JOIN result r ON r.`StudentNo` = s.`StudentNo` WHERE `StudentResult`>=80 -- 在这个基础上增加一个科目,高等数学-2 -- 查询 高等数学-2 的编号 SELECT DISTINCT r.`StudentNo`,`StudentName` FROM student s INNER JOIN result r ON r.`StudentNo` = s.`StudentNo` WHERE `StudentResult`>=80 AND `SubjectNo` = ( SELECT SubjectNo FROM `subject` WHERE `SubjectName` = '高等数学-2' ) -- 再改造(由里及外) SELECT `StudentNo`,`StudentName`FROM `student`WHERE `StudentNo` IN ( SELECT `StudentNo` FROM result WHERE `StudentResult`>=80 AND `SubjectNo` = ( SELECT SubjectNo FROM `subject` WHERE `SubjectName` = '高等数学-2' ) ) -- 练习:查询C语言-1前5名同学的成绩的信息(学号,姓名,分数) -- 使用子查询~
MySQL函数
官网:https://dev.mysql.com/doc/refman/5.7/en/built-in-function-reference.html
常用函数
-- =================== 常用函数 ============================ -- 数学运算 SELECT ABS(-8) -- 绝对值 SELECT CEILING(9.4) -- 向上取整 SELECT FLOOR(9.4) -- 向下取整 SELECT RAND() -- 返回一个0~1之间的随机数 SELECT SIGN(10) -- 判断一个数的符号 0-0 负数返回-1 正数返回1 -- 字符串函数 SELECT CHAR_LENGTH('即使再小的帆也能远航') -- 字符串长度 SELECT CONCAT('我','爱','你们') -- 拼接字符串 SELECT INSERT('我爱编程helloworld',1,2,'超级热爱') -- 插入,从某个位置开始替换某个长度 SELECT LOWER('KuangShen') -- 小写字母 SELECT UPPER('KuangShen') -- 大写字母 SELECT INSTR('kuangshen','h') -- 返回第一次出现的子串的索引 SELECT REPLACE('狂神说坚持就能成功','坚持','努力') -- 替换出现的指定字符串 SELECT SUBSTR('狂神说坚持就能成功',4,6) -- 返回指定的子字符串(源字符串,截取的位置,截取的长度) SELECT REVERSE('清晨我上马') -- 反转 -- 查询姓 周的同学,名字 邹 SELECT REPLACE(studentname,'周','邹') FROM student WHERE studentname LIKE '周%' -- 时间和日期函数(记住) SELECT CURRENT_DATE() -- 获取当前日期 SELECT CURDATE() -- 获取当前日期 SELECT NOW() -- 获取当前的时间 SELECT LOCALTIME() -- 本地时间 SELECT SYSDATE() -- 系统时间 SELECT YEAR(NOW()) SELECT MONTH(NOW()) SELECT DAY(NOW()) SELECT HOUR(NOW()) SELECT MINUTE(NOW()) SELECT SECOND(NOW()) -- 系统 SELECT SYSTEM_USER() SELECT USER() SELECT VERSION()
聚合函数(常用)
函数名称 | 描述 |
COUNT() | 计数 |
SUM() | 求和 |
AVG() | 平均值 |
MAX() | 最大值 |
MIN() | 最小值 |
... | ... |
-- ============================= 聚合函数 ========================= -- 都能够统计表中的数据(想查询一个表中有多少个记录,就是用这个count()) SELECT COUNT(studentname) FROM student; -- COUNT(指定列),会忽略null值 SELECT COUNT(*) FROM student; -- COUNT(*),不会忽略null值 本质 计算行数 SELECT COUNT(1) FROM result; -- COUNT(1),不会忽略null值 本质 计算行数 SELECT SUM(`studentresult`) AS 总和 FROM result SELECT AVG(`studentresult`) AS 平均分 FROM result SELECT MAX(`studentresult`) AS 最高分 FROM result SELECT MIN(`studentresult`) AS 最低分 FROM result -- 查询不同课程的平均分,最高分,最低分 -- 核心:(根据不同的课程分组) SELECT SubjectName, AVG(studentresult) AS 平均分,MAX(studentresult) AS 最高分,MIN(studentresult) AS 最低分 FROM result r INNER JOIN `subject` sub ON r.`SubjectNo`=sub.`SubjectNo` -- 通过什么字段来分组 GROUP BY r.`SubjectNo` HAVING 平均分>80
数据库级别的MD5加密(拓展)
什么是MD5?
主要增强算法复杂度和不可逆性
MD5不可逆,具体的值的md5是一样的
MD5破解网站的原理,背后有一个字典,MD5加密后的值 加密的值
-- ============================= 测试MD5 加密 ================================= CREATE TABLE `testmd5`( `id` INT(4) NOT NULL, `name` VARCHAR(20) NOT NULL, `pwd` VARCHAR(50) NOT NULL, PRIMARY KEY(`id`) )ENGINE=INNODB DEFAULT CHARSET=utf8 -- 明文密码 INSERT INTO testmd5 VALUES(1,'zhangsan','123456'),(2,'lisi','123456'),(3,'wangwu','123456') -- 加密 UPDATE testmd5 SET pwd=MD5(pwd) WHERE id = 1 UPDATE testmd5 SET pwd=MD5(pwd) -- 加密全部的密码 -- 插入的时候加密 INSERT INTO testmd5 VALUES(4,'xiaoming',MD5('123456')) -- 如何校验:将用户传递进来的密码,进行md5加密,然后比对加密后的值 SELECT * FROM testmd5 WHERE `name`='xiaoming' AND pwd=MD5('123456')
事务
什么是事务
要么都成功,要么都失败
————————————————
- SQL执行 A给B转账 A 1000 -->200 B 200
- SQL执行 B收到A的钱 A 800 --> B 400
————————————————
将一组SQL放在一个批次中去执行~
事务原则:ACID原则 原子性,一致性,隔离性,持久性 (脏读,幻读...)
原子性(Atomicity)
要么都成功,要么都失败
一致性(Consistency)
事务前后的数据完整性要保证一致
持久性(Durability)
事务一旦提交则不可逆,被持久化到数据库中
隔离性(Isolation)
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,事务之间要相互隔离
隔离所导致的一些问题
脏读:
指一个事务读取了另外一个事务未提交的数据
不可重复读:
在一个事务内读取表中的某一行数据,多次读取结果不同。(这个不一定是错误,只是某些场合不对)
虚读(幻读):
是指再一个事务内读取到了别的事务插入的数据,导致前后读取不一致
执行事务
-- ============================ 事务 =============================== -- mysql 是默认开启事务自动提交的 SET autocommit = 0 /* 关闭 */ SET autocommit = 1 /* 开启(默认的) */ -- 手动处理事务 -- 事务开启 START TRANSACTION -- 标记一个事务的开始,从这个之后的sql都在同一个事务内 INSERT xx INSERT xx -- 提交:持久化(成功!) COMMIT -- 回滚:回到原来的样子(失败!) ROLLBACK -- 事务结束 SET autcommit = 1 -- 开启自动提交 -- 了解 SAVEPOINT 保存点名 -- 设置一个事务的保存点 ROLLBACK TO SAVEPOINT 保存点名 -- 回滚到保存点 RELEASE SAVEPOINT 保存点名 -- 撤消保存点
模拟场景
-- 转账 CREATE DATABASE shop CHARACTER SET utf8 COLLATE utf8_general_ci USE shop CREATE TABLE `account`( `id` INT(3) NOT NULL AUTO_INCREMENT, `name` VARCHAR(30) NOT NULL, `money` DECIMAL(9,2) NOT NULL, PRIMARY KEY(`id`) )ENGINE=INNODB DEFAULT CHARSET=utf8 INSERT INTO account(`name`,`money`) VALUES ('A',2000.00),('B',10000.00) -- 模拟转账:事务 SET autocommit=0 -- 关闭自动提交 START TRANSACTION -- 开启一个事务 UPDATE account SET money=money-500 WHERE `name`='A' -- A减500 UPDATE account SET money=money+500 WHERE `name`='B' -- B加500 COMMIT -- 提交事务 ROLLBACK -- 回滚 SET autocommit=1 -- 恢复默认值
索引
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。
提取句子主干,就可以得到索引的本质:索引就是数据结构
索引的分类
在一个表中,主键索引只能有一个,唯一索引可以有多个
- 主键索引(PRIMARY KEY)
- 唯一的标识,主键不可重复,只能有一个列作为主键
- 唯一索引(UNIQUE KEY)
- 避免重复的列出现,唯一索引可以重复,多个列都可以标识为 唯一索引
- 常规索引(KEY/INDEX)
- 默认的,index/key关键字来设置
- 全文索引(FullText)
- 在特定的数据库引擎下才有,MyISAM
- 快速定位数据
基础语法
-- 索引的使用 -- 1、在创建表的时候给字段增加索引 -- 2、创建完毕后,增加索引 -- 显示所有的索引信息 USE school -- 增加一个索引 (索引名) 列名 ALTER TABLE school.`student` ADD FULLTEXT INDEX `studentname`(`studentname`) -- EXPLAIN 分析sql执行的状况 EXPLAIN SELECT * FROM student -- 非全文索引 EXPLAIN SELECT * FROM student WHERE MATCH(studentname) AGAINST('刘')
测试索引
CREATE TABLE `app_user`( `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(50) DEFAULT '' COMMENT '用户昵称', `email` VARCHAR(50) NOT NULL COMMENT '用户邮箱', `phone` VARCHAR(20) DEFAULT '' COMMENT '手机号', `gender` TINYINT(4) UNSIGNED DEFAULT '0' COMMENT '性别(0:男;1:女)', `password` VARCHAR(100) NOT NULL COMMENT '密码', `age` TINYINT(4) DEFAULT '0' COMMENT '年龄', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, `update_time` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY(`id`) )ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='app用户表' -- 插入100万条数据 DELIMITER $$ -- 写函数之前必须要写,标志 CREATE FUNCTION mock_data() RETURNS INT BEGIN DECLARE num INT DEFAULT 1000000; DECLARE i INT DEFAULT 0; WHILE i<num DO INSERT INTO app_user(`name`,`email`,`phone`,`gender`,`password`,`age`) VALUES(CONCAT('用户',i),'24736743@qq.com',CONCAT('18',FLOOR(RAND()*999999999)),FLOOR(RAND()+1),UUID(),FLOOR(RAND()*100)); SET i = i+1; END WHILE; RETURN i; END; SELECT mock_data(); SELECT * FROM app_user WHERE `name` = '用户9999'; -- 0.362 sec SELECT * FROM app_user WHERE `name` = '用户9999'; -- 0.341 sec EXPLAIN SELECT * FROM app_user WHERE `name` = '用户9999'; -- rows:993158 SELECT * FROM student -- id_表名_字段名 -- CREATE INDEX 索引名 on 表(字段) CREATE INDEX id_app_user_name ON app_user(`name`); SELECT * FROM app_user WHERE `name` = '用户9999'; -- 0 sec EXPLAIN SELECT * FROM app_user WHERE `name` = '用户9999'; -- rows:1
索引在小数据量的时候,用处不大,但是在大数据的时候,区别十分明显
索引原则
- 索引不是越多越好
- 不要对经常变动的数据加索引
- 小数据量的表不需要加索引
- 索引一般加在常用来查询的字段上
索引的数据结构
Hash类型的索引
Btree:InnoDB的默认数据类型
阅读:http://blog.codinglabs.org/articles/theory-of-mysql-index.html
权限管理和备份
用户管理
SQL yog可视化管理
SQL命令操作
用户表:mysql.user
本质:对这张表进行增删改查
-- 创建用户 CREATE USER 用户名 IDENTIFIED BY '密码' CREATE USER kuangshen IDENTIFIED BY '123456' -- 修改密码(修改当前用户密码) SET PASSWORD = PASSWORD('123456') -- 修改密码(修改指定用户密码) SET PASSWORD FOR kuangshen = PASSWORD('123456') -- 重命名 RENAME USER 原来名字 TO 新的名字 RENAME USER kuangshen TO kuangshen2 -- 用户授权 ALL PRIVILEGES 全部的权限, 库.表 -- ALL PRIVILEGES 除了给别人授权,其他都能干 GRANT ALL PRIVILEGES ON *.* TO kuangshen2 -- 查询权限 SHOW GRANTS FOR kuangshen2 -- 查看指定用户的权限 SHOW GRANTS FOR root@localhost -- root用户的权限:GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION -- 撤销权限 REVOKE 哪些权限,在哪个库撤销,给谁撤销 REVOKE ALL PRIVILEGES ON *.* FROM kuangshen2 -- 删除用户 DROP USER kuangshen
MySQL备份
为什么要备份:
- 保证重要的数据不丢失
- 数据转移
MySQL数据库备份的方式
- 直接拷贝物理文件
- 在Sqlyog这种可视化工具中手动导出
- 在想要导出的表或者库中,右键,选择备份或导出
- 使用命令行导出 mysqldump 命令行使用
# mysqldump -h主机 -u用户名 -p密码 数据库 表名 > 物理磁盘位置/文件名 C:\Users\wangyudong>mysqldump -hlocalhost -uroot -p123456 school student >D:/a.sql # mysqldump -h主机 -u用户名 -p密码 数据库 表1 表2 表3 > 物理磁盘位置/文件名 C:\Users\wangyudong>mysqldump -hlocalhost -uroot -p123456 school student result >D:/b.sql # mysqldump -h主机 -u用户名 -p密码 数据库> 物理磁盘位置/文件名 C:\Users\wangyudong>mysqldump -hlocalhost -uroot -p123456 school >D:/c.sql # 导入 # 登陆的情况下,(切换到指定的数据库) # source 备份文件 source d:/a.sql mysql -u用户名 -p密码 库名< 备份文件
假设你要备份数据库,防止数据丢失。
把数据库给朋友!sql文件给别人即可!
规范数据库设计
为什么需要设计
当数据库比较复杂的时候,我们就需要设计了
糟糕的数据库设计:
- 数据冗余,浪费空间
- 数据库插入和删除都会麻烦、异常【屏蔽使用物理外键】
- 程序的性能差
良好的数据库设计:
- 节省内存空间
- 保证数据库的完整性
- 方便我们开发系统
软件开发中,关于数据库的设计
- 分析需求:分析业务和需要处理的数据库需求
- 概要设计:设计关系图E-R图
设计数据库的步骤:(个人博客)
- 收集信息,分析需求
- 用户表(用户登录注销,用户的个人信息,写博客,创建分类)
- 分类表(文章分类,谁创建的)
- 文章表(文章的信息)
- 评论表
- 友链表(友链信息)
- 自定义表(系统信息,某个关键的字,或者一些主题) key:value
- 说说表(发表心情 .. id...content...create_time)
- 标识实体(把需求落地到每个字段)
- 标识实体之间的关系
- 写博客:user --> blog
- 创建分类:user --> category
- 关注:user --> user
- 友链:links
- 评论:user-user-blog
三大范式
为什么需要数据规范化?
- 信息重复
- 更新异常
- 插入异常
- 无法正常显示信息
- 删除异常
- 丢失有效的信息
三大范式(了解)
第一范式(1NF)
原子性:保证每一列不可再分
第二范式(2NF)
前提:满足第一范式
每张表只描述一件事情
第三范式(3NF)
前提:满足第一范式和第二范式
第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关
(规范数据库的设计)
规范性和性能的问题
关联查询的表不得超过三张表
- 考虑商业化的需求和目标,(成本,用户体验!)数据库的性能更加重要
- 在规范性能的问题的时候,需要适当的考虑一下规范性!
- 故意给某些表增加一些冗余的字段。(从多表查询中变为单表查询)
- 故意增加一些计算列(从大数据量降低为小数据量的查询:索引)
数据库驱动与JDBC
驱动:声卡,显卡,数据库
我们的程序会通过数据库驱动,和数据库打交道!
SUN公司为了简化开发人员的(对数据库的统一)操作,提供了一个(Java操作数据库的)规范,俗称JDBC
这些规范的实现由具体的厂商去做~
对于开发人员来说,我们只需要掌握JDBC接口的操作即可!
java.sql
javax.sql
还需要导入一个数据库驱动包 mysql-connector-java-5.1.47.jar