0.学习资料
参考书籍《深入浅出MySQL数据库开发、优化与管理维护》
参考视频《传智播客MySQL》
1.高级数据插入:
1)主键冲突:
- 插入的基本语法:insert into 表名[(字段列表)] values(值列表);
- 若数据插入时主键对应的值已经存在,则一定会插入失败。
 当主键出现冲突的时(Duplicate Key),可与选择性的处理:更新或者替换。
- 更新操作:
 注意update后不需要set。Inset into 表名[(字段列表:包含主键)] values(值列表) on duplicate key update 字段 = 新值;
 创建一个te_student表:
 插入三条数据:CREATE TABLE IF NOT EXISTS te_stu1( id INT auto_increment PRIMARY KEY COMMENT '主键', code CHAR(10) NOT NULL COMMENT '学号', name VARCHAR(20) NOT NULL COMMENT '姓名' )ENGINE=INNODB DEFAULT CHARSET utf8;
 如若要更新第二条数据,一般情况下会失败,需要这样写:INSERT INTO te_stu1 VALUES(1,"1111111111","学生1"); INSERT INTO te_stu1 VALUES(2,"1111111112","学生2"); INSERT INTO te_stu1 VALUES(3,"1111111113","学生3");
 会显示有两行受影响,插入一行,更新一行。但是只会更新一个字段。INSERT INTO te_stu1 VALUES(2,"2222222222","学生22") ON DUPLICATE KEY UPDATE code='2222222222';
 但是如果有几个字段需要修改,用and连接我测试是无效的,可以使用替换,或者,还是直接更新吧。
- 替换:
 先删除后插入:
 若主键冲突,则替换,不冲突则直接插入。replace into 表名[(字段列表:包含主键)] values(值列表);
 如执行:
 结果如下:REPLACE INTO te_stu1 VALUES(2,"2222222222","学生22"); REPLACE INTO te_stu1 VALUES(4,"4444444444","学生4"); 
2)蠕虫复制:
从已有的数据中去获取数据,然后将数据进行新增操作,数据成倍的增加。
- 表创建的高级操作:从已有的表创建新表(复制表结构)
 如复制一个te_stu1:Create table 表名 like 数据库.表名; -- 同一个数据库不用加数据库名
 只会复制表结构,但是没有数据。CREATE TABLE te_stu1_copy LIKE te_stu1;
- 蠕虫复制:
 先查出数据,然后将查出的数据新增一遍:
 如将te_stu1的数据搬到te_stu1_copy:Insert into 表名[(字段列表)] select 字段列表[*] from 数据表名;
 te_stu1_copy中已经有数据了:INSERT INTO te_stu1_copy SELECT * from te_stu1; 
 如果没有复制主键的话,一直执行蠕虫复制就会是数据成倍增加(复制自身):INSERT INTO te_stu1_copy(code,name) SELECT code,name from te_stu1_copy;
- 蠕虫复制的意义:- 从已有表中拷贝数据到新表中
- 可以迅速的让表中的数据膨胀,膨胀到一定的数量级来测试表的压力
 
2.高级更新与删除数据
1)更新数据:
- 基本语法:Update 表名 set 字段 = 值 [where 条件];
- 高级新增(限制记录条数)
 使用上述蠕虫复制多复制几条记录,如要更新前5个学生的学号为0000000000:Update 表名 set 字段 = 值 [where 条件][limit 更新数量];
 结果如下:UPDATE te_stu1_copy SET code = '0000000000' LIMIT 5; 
2)删除数据:
删除数据的限制与更新类似:(通过limit限制数据)
- 语法:delete from 表名 [where 条件][limit 数量]
- 案例,删除te_stu1_copy的前3个学生1
 结果:DELETE FROM te_stu1_copy WHERE name='学生1' LIMIT 3; 
3)删除并还原自增长:
- 完全删除数据后自增长auto_increment表选项仍为之前的自增长,可以使用:
 来修改,但是显得太麻烦了。可以直接删除表后再创建。ALTER TABLE 表名 auto_increment=1;
- 表重建:
 先删除该表,再创建该表(先清空表再重置自增长)如:Tuncate 表名;
 运行结果:TRUNCATE te_stu1_copy; 
3.数据查询(select表选项,字段别名,数据源)
简单查询:Select 字段列表/* from 表名 [where 条件];
完整查询:
select[select选项] 字段列表[字段别名]/* from 数据源 [where 条件子句] [group by子句][having 子句][order by子句][limit 子句]
只要有这些子句顺序必须得是这样。
1)select选项:
- select对查出来的结果的处理方式,有俩选项:
 All:默认的,保留所有信息
 Distict:去重(所有字段都相同才算重复)
- 案例:
 新建表te_stu2复制表te_stu1的结构但是删除主键与自增长,并使用蠕虫复制多条数据:
 查看一下共有多少条数据以及查询:CREATE TABLE te_stu2 LIKE te_stu1; ALTER TABLE te_stu2 DROP PRIMARY KEY; ALTER TABLE te_stu2 modify id INT; INSERT INTO te_stu2 SELECT * from te_stu1; INSERT INTO te_stu2 SELECT * from te_stu2; //使用多次 
 注意:不能针对某一字段去重。
2)字段别名:
- 为什么使用别名:
 当数据进行查询操作的时候,有时候名字并不一定满足需求(多表查询时会有重名字段):需要对字段名进行重命名:别名。
- 重命名的方式:字段名 [as] 别名 -- as可以省略
- 测试:
 先往te_stu1表中插入学生5和学生6,查询学生4并用number代替code字段:
 结果中的code变为了number:SELECT id,code number,name FROM te_stu1 WHERE name='学生4'; 
3)数据源:
- 数据的来源,关系型数据库的数据来源都是表,只要保证数据类似二维表的都可以。
 数据源分类:单表数据源,多表数据源,查询语句
- 单表数据源:select * from 表名
- 多表数据源:
 如表1有3条记录,表2有4条记录,则不加条件的select * from 表名1,表名2,表名3...select * from 表名1,表名2;会查询出3*4=12条数据。
 从一张表中取出一条数据去另外一张表中匹配所有记录,而且全部保留,这种结果称为:交叉连接(笛卡尔积)。
 应该尽量少用笛卡尔积。
- 查询语句:
 子查询:数据的来源是一条查询语句。
 如查询te_stu2去重后的结果中的学生22:select * from (select 子查询) as 表名;
 运行结果为:SELECT * FROM (SELECT DISTINCT * FROM te_stu2) AS s WHERE s.name='学生22'; as同样可以给数据源提供别名。
4.数据查询(where,groupby子句)
1)Where子句:
- where子句:
 用来判断数据,删选数据
 返回结合:0或1,代表false和true
- where 的判断条件:
 比较运算符:>,<,>=,<=,!=,<>,<=>,like,between and,in/not in;
 逻辑运算符:&&(and),||(or),!(not)
 详细的关于运算符的介绍可以查看笔记3
- where原理:
 where是唯一一个直接从磁盘获取数据的时候就开始判断的条件:
 从磁盘取出一条数据,进行where判断,成立则保存到内存,失败则直接放弃。
- 案例:
 给te_stu1添加age字段与身高height字段:
 给每个学生添加信息(随机):ALTER TABLE te_stu1 ADD age TINYINT UNSIGNED; ALTER TABLE te_stu1 ADD Height TINYINT UNSIGNED;
 查看添加结果:UPDATE te_stu1 SET age=FLOOR(RAND()*10+20),height=FLOOR(RAND()*30+160); 
 找出学生id为1,3,5的学生:
 查询身高在165-180之间的学生:SELECT * FROM te_stu1 WHERE id=1||id=3||id=5; -- 逻辑运算 SELECT * FROM te_stu1 WHERE id IN(1,3,5); -- 落在集合
 注意:between本身是闭区间,且左边的值必须小于等于右边的值。SELECT * FROM te_stu1 WHERE height>=165 && height<180; SELECT * FROM te_stu1 WHERE height BETWEEN 165 AND 180;
 查询所有(有时为保证SQL语句的完整性):SELECT * FROM te_stu1 WHERE 1;
2)group by子句:
- 分组:根据某个字段进行分组,相同的放一组,不同的放一组。
- 基本语法:group by 字段名
- 测试:
 给te_tu1添加一个sex字段:
 给所有学生添加性别(随机):ALTER TABLE te_stu1 ADD sex enum('男','女');
 根据性别分组:UPDATE te_stu1 SET sex=FLOOR(RAND()*2+1);
 结果是这样的:SELECT * FROM te_stu1 GROUP BY sex; 
 只有两条数据?没错只有两条,按分组字段是为了数据统计。统计函数如下:
- 统计函数:Count():统计分组后的记录数 
 Max():统计每组中的最大值
 Min():统计最小值
 Avg():统计平均值
 Sum():统计和
- 统计函数测试:
 测试结果:SELECT COUNT(*),MAX(age),MIN(height),AVG(height),Sum(age),sex FROM te_stu1 GROUP BY sex; 
 一旦用到了group by子句最好就使用统计,否则groupby将没有意义。
- 关于统计函数:
 count()里面可以使用两种参数:*和字段(null不参与运算)。
 avg()中的null字段也算入总数。
- 分组会自动排序,且默认是升序:group by 字段 [asc|desc]; -- 对分组之后的结果进行排序(asc升,desc降)
3)多字段分组:
- 多字段分组:
 先根据一个字段进行分组,然后对分组后的结果再次按照其他字段进行分组。
- 测试:
 先往te_stu1下再添加四个学生,代码略。
 再添加一个class字段:
 添加每个人的班级(随机):ALTER TABLE te_stu1 ADD class enum('班级1','班级2');
 先按班级分组再按性别分组,查找每个班对应性别有多少人:UPDATE te_stu1 SET class=FLOOR(RAND()*2+1);
 测试结果:SELECT class,sex,COUNT(*) FROM te_stu1 GROUP BY class,sex; 
- group_concat()方法:
 可以对分组的结果中的某个字段进行进行字符串连接(会输出该字段的连接值),用法为:
 group_concat(字段),如:
 运行结果为:SELECT class,sex,COUNT(*),group_concat(name) FROM te_stu1 GROUP BY class,sex; 
4)回溯统计with rollup
- 原理:
 任何一个分组后都会有一个小组,最后都会向上级进行汇报统计(根据当前分组的字段)
 这就是回溯统计,回溯统计时会将分组字段置空。
- 测试:
 一层分组的回溯统计:SELECT class,COUNT(*) FROM te_stu1 GROUP BY class; -- 一个字段,分为两组,共10人 SELECT class,COUNT(*) FROM te_stu1 GROUP BY class WITH ROLLUP; -- 回溯统计,会加一个上级的总人数 SELECT class,sex,COUNT(*),group_concat(name) FROM te_stu1 GROUP BY class,sex WITH ROLLUP; -- 层层回溯,会加上各班级总人数(第二级)和总人数(第一级) 
 两层分组的回溯统计: 
5.数据查询(having,orderby,limit子句)
1)Having子句:
- 说明:
 与where子句,也是进行条件判断的。
 where是针对磁盘数据进行判断,进入到内存之后,会进行分组操作,分组的操作就需要having来处理。
 having能做where能做的几乎所有事情,但是很多having能做的事where做不了:- 分组统计的结果或者统计函数的值都只有having能够操作。
- having能使用字段别名而where不行
 
- 基本语法:select ... having 条件
- 案例:
 再次添加5个学生,并且添加班级3,并修改班级更新语句:
 求出所有班级人数大于5的班级(使用别名sum):UPALTER TABLE te_stu1 MODIFY class enum('班级1','班级2','班级3'); UPDATE te_stu1 SET class=FLOOR(RAND()*3+1);
 运行结果:SELECT class,COUNT(*) AS sum FROM te_stu1 GROUP BY class HAVING sum >5; 
 这样是非法的:
 where加载时数据还没到内存,count(*)与别名sum都还没加载。SELECT class,COUNT(*) AS sum FROM te_stu1 WHERE sum >5 GROUP BY class; -- 非法,where不能使用count(*)及别名
2)order by子句:
- 说明:
 排序,根据某个字段进行升序(asc)或者降序(desc)排序,依赖校对集
- 基本用法:select ... Order by 字段名 [asc|desc] -- 默认asc升序
- 与group by的区别: 
 分组是为了排序,而group by是为了统计。
- 多字段排序:
 排序可以进行多字段排序,先根据某一个字段排好序,然后排序好的内部再按某个数据次序进行排序。
 如先按班级排序(升序)再按照年龄排序(降序):
 测试结果:SELECT * FROM te_stu1 ORDER BY class [ASC],age DESC; 
3)limit子句:
- 说明:
 是一种限制结果的语句:本质是限制数量。
- 有两种使用方式:- 只用来限制长度(数量):limit 数据量
- 限制起始位置,限制数量:limit 起始位置(offset),长度(length)(从指定的位置向后找几个数据)
 
- 只用来限制长度(数量):
- 案例测试:
 查询学生前两个:
 从第三条开始查两条:SELECT * FROM te_stu1 LIMIT 2; SELECT * FROM te_stu1 LIMIT 0,2; -- 编号是从0开始的SELECT * FROM te_stu1 LIMIT 2,2;
- 主要作用:实现数据的分页 
 分页:为用户节省时间,提高服务器响应效率,减少资源浪费。
- 分页的简单理论实现 - Page model类需要的属性:
 totalCount:总的记录数量
 totalPage:总的记录数量
 //前俩用于前台首末页等功能,后俩用于分页
 PageSize:每页显示数量(最好固定)
 NowPage:当前所在哪页
- Length=每页显示的数据量:基本不变
 Offset=(页码数量-1)*每页显示数量
- 简单的查询语句这样写:select * from *** limit "+(page.getNowPage()-1)*page.getPageSize()+","+page.getPageSize();
 
- Page model类需要的属性:
最后更新: 2018年03月19日 22:18
原始链接: https://zjxkenshine.github.io/2018/03/17/MySQL数据库学习笔记(四)/
 
                