mysql基础学习笔记
/ / 点击 / 阅读耗时 43 分钟mysql基础学习笔记
学习视频:https://www.bilibili.com/video/av19538278?t=47&p=32
视频中用到的资料:https://blog.csdn.net/qq_27229113/article/details/75206309
这套视频详细介绍了增删改查,左连接,子查询,视图,简单介绍了事务,引擎的区别。关于这套教程的一些想法:老师是真的教得好。还会讲一些项目、字段的设计、字符集之类的话题,感觉平时自己只是随便取出数据就用来控制或是显示了,没有怎么细想过,现在感觉理解上有些不一样了,也说不好哪儿不一样,总之感觉进步了一点点。
orm vs 原生sql:
- https://stackoverflow.com/questions/3360304/orm-vs-traditional-database-query-which-are-their-fields
- https://stackoverflow.com/questions/494816/using-an-orm-or-plain-sql
- https://blog.logrocket.com/why-you-should-avoid-orms-with-examples-in-node-js-e0baab73fa5
- https://medium.com/ameykpatil/why-orm-shouldnt-be-your-best-bet-fffb66314b1b
决定后面的练习写sql了。
解决乱码的办法
set names gbk
一个建表语句
create table class(
id int primary key auto_increment,
sname varchar(10) not null default ‘’,
gender char(1) not null default ‘’,
company varchar(20) not null default ‘’,
salary decimal(6,2) not null default 0.00,
fanbu smallint not null default 0
) engine myisam charset utf8;
增的操作
- 往哪张表
- 往哪几列
- 添加哪些值
insert into class
(id,sname,gender,company,salary,fanbu)
values
(1,’张三’,’男’,’百度’,8888.67,234);
insert into class
(sname,company,salary)
values
(‘刘备’,’皇室成员’,15.28),
(‘孙策’,’江东集团’,56.34),
(‘曹操’,’宦官后裔’,88.56);
删的操作
删除就是指删除整行,不存在删除某几列
- 删哪张表
- 删哪行
delete from class where salary>8000;
delete from class where salary>8000 and gender=’女’;
改的操作
- 改哪张表
- 该哪几列
- 改成什么值
- 在哪一行生效
update class
set
gender=’女’,
company=’千度’
where id=2;
update class
set fanbu=150
where gender=’男’ and salary>8000;
update class
set fanbu=100
where 1;
查的操作
- 从哪张表查
- 查哪几列
- 满足哪些条件
select * from class; // 查class表中的所有数据
select sname,gender from class where id<5; //查class表中id<5的sname和gender列
select * from class where id=3; // 从class表中查id=3的所有列
select sname,gender from class; //从class表中查sname,gender两列的所有行
后面要学
- 如何自己建表
- 如何修改表(增加减少列等)
- 多表联查
- 子查询
- 触发器
- 事务
- 存储过程
- 备份恢复
建表
建表的过程就是一个 声明字段 的过程.
存储同样的数据,不同的列类型所占据的空间和效率是不一样的,这就是建表前学习列类型的意义。重点学列类型的存储范围和占据的字节大小。
为什么建表时,加not null default ‘’/0?
答:不想让表中出现null值。
为什么不想要null值?
答:1.不好比较,null是一种类型,比较时只能用专门的is null和is not null来比较,碰到运算符一律返回null;2.效率不高,不利于提高索引效果。因此 在建表时往往声明not null default 0/‘’。
比较null要用专门的方法:
1 | select * from test where sname is null; |
mysql三大列类型
数值型
整型
- Tinyint
- 占据空间:1个字节
- 存储范围:-128~127(0~255)
- 参数:(M) unsigned zerofill
- 默认有符号
- Smallint
- Mediumint
- bigint
- Tinyint
小数(浮点型、定点型)
- M:精度(小数所有位数)
- D:标度(小数后位数)
- 范围:float能存10^38.10^-38
- 占据空间:M<=24,占4个字节,否则占8个字节
- 定点:定点数是把整数部分和小数部分分开存储的,比float精确。float有时会损失精度,如果像银行这样的敏感字段,建议用decimal
补码规则
计算机中的负数,不是按照后面的绝对值位乘-1得到的,而是用补码规则换算的。
负数 = 绝对值位 - 128
1111 1111 =========> -1
1000 0000 =========> -128
字符串类型
- char:定长类型,对于char(M),M代表宽度(占用空间,限制的是字符不是字节),0<=M<=255之间,表示能输M个字符,不够M个长度在尾部用空格补齐,能通过数学计算出位置。从利用率的角度,能达到100%。在存取特点上,char型如果不够M个字符,内部用空格补齐,取出时再把右侧空格删掉,这意味着如果右侧本身有空格,将会丢失。速度上定长类型快一些。
- varchar:变长类型,对于varchar(M),M代表宽度(占用空间,限制的是字符不是字节),0<=M<=65535(以ascii字符为例,utf8 22000左右)。varchar(100)能存0-100个字符。每个varchar前还有1-2个字符用来表示当前字符的长度,从利用率的角度,到不了100%。
- Text:文本类型,可以存比较大的文本段,搜索速度稍慢。如果不是特别大的内容,建议使用char、varchar来代替。
- Blob:是二进制类型,用来存储图像,音频等二进制信息。Blob在于防止因为字符集的问题,导致信息丢失。比如一张图片中有0xFF字节,这个在ascii字符集中认为非法,在入库的时候被过滤。
- 日期类型:5.7版本的mysql的date类型不支持0为默认值,具体原因参考这里。date类型能存储的范围是
1000-01-01~9999-12-31
。 - 时间类型
- 日期时间类型:输入格式YYYY-mm-dd HH:mm:ss。但一般使用时间戳来存储。
- 时间戳类型:timestamp
- 年份类型:year 1901-2155 255个年份+0000一个错误年份
修改表
新增列语法:
- alter table 表名 add 列名称 列类型 列参数;(加在最后)
- alter table 表名 add 列名称 列类型 列参数 after 指定列;(加在指定列后面)
- alter table 表名 add 列名称 列类型 列参数 first;(加在第一行)
删除列语法:
- alter table 表名 drop 类名;
修改列语法:
- alter table 表名 modify 列明 新类型 新参数;(此方法不能该表名)
修改列明和列类型语法:
- alter table 表名 change 旧列名 新列名 新参数;
查询建表语句
show create table 表名;
从一个表中将特定几列的数据拷入另一个表中
查询
使用某个具体的字段来数的话,如果这个字段下某一行的值为null,使用count时,不计入数量。使用count(*)数绝对的行数。
用count(*)和count(1)谁好呢?
对于myisam引擎的表来说没有区别,这种表内部有一个计数器用来维护行数。对于Innodb引擎的表来说,用count(*)直接读行数效率很低,因为innodb真的要去数一遍
以下查询基于这里的数据。
基础查询 where 的练习
1.1 主键为32的商品id、商品名称和商城内售价
1
select goods_id,goods_name,shop_price from goods where goods_id=32;
1.2 类型不是3的商品id、类型id、商品名称、商城内售价
1
select goods_id,cat_id,goods_name,shop_price from goods where cat_id!=3;
1.3 商城内价格高于3000块的商品的商品id、类型id、商品名称、商城内售价
1
select goods_id,cat_id,goods_name,shop_price from goods where shop_price>3000;
1.4 商城内价格低于或等于100块的商品的商品id、类型id、商品名称、商城内售价
1
select goods_id,cat_id,goods_name,shop_price from goods where shop_price<=100;
1.5 查询分类为4和11的商品,不许用or
1
select goods_id,cat_id,goods_name.shop_price from goods where cat_id in (4,11);
1.6 查询大于等于100块小于等于500块的商品,不能用逻辑与and
1
select goods_id,cat_id,goods_name,shop_price from goods where shop_price between 100 and 500;
1.7 取出不再分类为3且不在分类为11中的商品,用not in 和 and 分别实现
1
2
3
4
5<!-- 用not in实现 -->
select goods_id,cat_id,goods_name,shop_price from goods where cat_id not in (3,11);
<!-- 用and实现 -->
select goods_id,cat_id,goods_name,shop_price from goods where cat_id != 3 and cat_id != 11;1.8 取出价格大于100且小于300或者大于4000且小于5000的商品
1
select goods_id,cat_id,goods_name,shop_price from goods where (shop_price>100 and shop_price<300) or (shop_price>4000 and shop_price<5000);
1.9 取出分类3下的价格小于1000或者大于3000,同时点击量大于等于5的商品
1
select goods_id,cat_id,goods_name,shop_price from goods where (cat_id=3) and (shop_price<1000 or shop_price>3000) and (click>=5);
1.10 查出名字以诺开头的商品
模糊查询 关键字 like
在查询字段后加 %(百分号匹配任意个字符)
1
select goods_id,cat_id,goods_name,shop_price from goods where goods_name like '诺记%';
使用 _匹配任意个单个字符
1
select goods_id,cat_id,goods_name,shop_price from goods where goods_name like '诺记__';
1.11 查出在商城中购买商品比市场价省多少钱
将字段名字看作是变量,既然是变量就可以参与运算
1
select goods_id,goods_name,(market_price-shop_price) as discount from goods where cat_id>10;
1.12 查出本店价比市场价省的钱,而且省200以上的商品
1
select goods_id,goods_name,(market_price-shop_price) as discount from goods where (market_price-shop_price)>200;
此处where中不能使用discount是因为where是在原表中发挥作用,原表中没有discount的字段,结果表中才有discount的字段,继续筛结果表中的数据要使用 having。
1.13 将20-19之间的数改为20,30-39之间的数改为30
把num当成变量看,num/10取整再乘以10。。。麻的,真妥么想不到诶。。。
1
update cv set num=floor(num/10)*10 where num between 20 and 40;
大胆的把列看成变量,参与运算,甚至调用函数来处理,常用的函数有substring()、concat()
分组 group 查询
group by cat_id 从语义上来分析,select的列只能是cat_id/max/min/avg/sum/count()这种统计类型的列。使用select *从语义上分析就不对,严格说不符合sql标准
2.1 查询出最贵的商品价格
1
select max(shop_price) from goods;
2.2 查询出最便宜的商品的价格
1
select min(shop_price) from goods;
2.3 查询出最小的商品id
1
select min(goods_id) from goods;
2.4 查询所有商品总库存量
1
select sum(goods_num) from goods;
2.5 查询所有商品的平均价格
1
select avg(shop_price) from goods;
2.6 查询有多少种商品
1
select count(*) from goods;
2.7 计算分类3下的所有商品的库存量之和
1
select sum(goods_number) from goods where cat_id=3;
2.8 一条语句计算每个分组下的库存量之和
1
select cat_id,sum(goods_number) from goods group by cat_id;
2.9 按商品分类分组,计算每个分类下的商品的平均价格
1
select cat_id,avg(shop_price) from goods group by cat_id;
having查询
sql语句的查询筛选过程:先根据where查询出满足条件的行,列和原表的列一样,得到一个中间表,再在这个中间表的基础上进行列之间的计算以及分组group,得到的结果集再通过having进行下一步的操作得到最终结果集。
3.1 查询出每个商品积压的货款
1
select goods_id,shop_price * goods_number from goods;
3.2 查询该店积压的总货款
1
select sum(shop_price * goods_number) as sum from goods;
3.3 查询每个分类下挤压的货款
1
select cat_id,sum(shop_price*goods_number) as sum from goods group by cat_id;
3.4 查询加压货款超过2万以及积压的货款
1
select cat_id,sum(shop_price*goods_number) as sum from goods group by cat_id having sum>20000;
3.5 查询比市场价省钱200块以上的商品以及该商品省的钱
1
select goods_id,(market_price-shop_price) as discount from goods having discount>200;
3.6 查询2门及2门以上不及格者的平均成绩
1
2
3
4
5
6
7
8
9
10
11
12
13//写错了。。。。。。
//因为count函数不管传入的是什么都数
select name,avg(score) as average,count(score<60) as gks from result group by name having gks>=2;
<!-- 正确结果:利用布尔值正确返回1错误返回0 -->
select name,avg(score),sum(score<60) as gks from result group by name having gks>=2;
<!-- 另一种方法 -->
<!-- 先筛选出名字,得到一张表,根据这张表继续筛选 -->
<!-- 三层嵌套 -->
select name,avg(score) from result where name in (select name from (select name,count(1) as gks from result where score<60 group by name having gks>=2)as tmp) group by name;排序
排序发生在得到最终的结果集(拿到原始筛选数据)之后,排序是针对最终结果集的,也就是说order要放在where/group by/having后面,顺序不能乱。
排序的语法:
order by 结果集中的列名1 desc/asc,结果集中的列名2 desc/asc (注意:这样写有排序先后,先按列名1排序,再按列名2排序)
4.1 取出分类4下的商品,并按价格由高到低排序。
1
select goods_id,goods_name,shop_price from goods where cat_id=4 order by shop_price desc;
4.2 按分类升序排列,同一个分类下的商品,再按商品的价格降序排列
1
select goods_id,goods_name,shop_price from goods order by cat_id asc,shop_price desc;
- limit限制条数
limit用在语句的最后,起到限制条目的作用。
limit[offset,] N,其中:
offset:偏移量,如果不写,相当于0
N:取出条目
5.1 取出本店价格最高的前三名
1
select goods_id,goods_name,shop_price from goods order by shop_price desc limit 0,3;
5.2 查询本店价格最高的第三名到第五名
1
select goods_id,goods_name,shop_price from goods order by shop_price desc limit 2,3;
5.3 取商品价格最高的一件商品
1
select goods_id,goods_name,shop_price from goods order by shop_price desc limit 1;
子查询
where型子查询:将内层查询的结果作为外层查询的条件。
典型题:查询最大商品、最贵商品
如果where列=(内层sql),则内层sql返回的必是单行单列,也就是单个值
如果where列 in (内层sql),则内层sql只返回单列,可以多行from型子查询
这里插入一个重要概念
查询模型
列就是变量,在每一行上,列的值都在变化
- where条件是表达式,在哪一行上表达式为真,哪一行就取出来
查询结果集可以当成表看
from型子查询:内层sql的查询结果,当成一张临时表,供外层sql再次查询。
exists型子查询
exists型子查询:把外层sql的结果拿到内层sql去测试,如果内层sql成立,则该行被取出。
练习题
6.1 查询出每个分类下id号最大的一条商品
1
2
<!-- 仅想出这种嵌套的方法 -->
select goods_id,goods_name,cat_id from goods where goods_id in (select max(goods_id) from goods group by cat_id) order by cat_id asc;
6.2 查出本店最新的商品,要求不用排序
1
2
3
select goods_id,goods_name from goods where goods_id=(select max(goods_id) from goods);
<!-- select max(goods_id) from goods 这条语句返回的就是最大值 -->
6.3 查出有商品的分类号和分类名
1
select cat_id,cat_name from category where exists (select * from goods where goods.cat_id=category.cat_id);
表与集合的关系
一张表就是一个集合
每一行就是一个元素
在数据库中,用 ,(逗号)将两个表名隔开即可完成两张表笛卡尔积查询
比如查询商品表和分类表,得到商品名称、库存、价格和分类名称。
1 | select goods_id,goods_name,cat_name from goods,category where goods.cat_id=category.cat_id; |
但是这样做效率低。
两张表全相乘,比如A表10000行数据,B表10000万行数据,都不算大,但是两表全相乘,在内存中生成一个非常大的数据。10000\10000行。另外索引没用上。*
先记下来:两张表联查之后没有索引
左连接
假设A表在左,不动,B表在A表的右边滑动,A表与B表通过一个关系来筛选B表的行。
语法:
A left join B on 条件,条件为真,则B表对应的行取出。
A left join B on 条件这个部分形成的也是一个结果集,可以看成一张表,设为c。既如此,可以对c表进行查询,where\group\having\order by\limit照常可以使用。同样,C看成表之后还可以继续和其他表进行左连接。
直接使用上面的语句会报语法错误诶…貌似5.7版本不能单独使用,和select一块儿使用就可以通过…
问:C表可以查询的列有哪些?
答:AB两表的列都可以查询。
练习:
取出第4个分类下的商品以及商品的分类名称
1
select goods_id,goods_name,shop_price,cat_name from (goods left join category on goods.cat_id = category.cat_id) where goods.cat_id = 4;
- 左连接、右连接、内连接的区别
左右连接可以互换
A left join B <=> B right join A
注意:既然左右连接可以互换,尽量用左连接,处于移植时兼容性方面的考虑。
1 | select boy.*,girl.* from boy inner join girl on boy.other = girl.other; |
从集合的角度看,内连接是左右连接的交集。
外连接是左右连接的并集,但是在mysql中不支持外连接。
作业题:
1 | <!-- 表结构 --> |
注意:要把第二次左连接用到的team表名起一个别名,否则会报错。
注意:match是mysql的关键字。
union
合并2条或多条语句的结果。
语法:sql1 union sql2
问:内否从2张表查询完再union呢?
答:可以,union合并的是“结果集”,不区分在哪一张表。
问:取自于2张表,通过别名让2个结果集的列一致,如果取出的结果集,列名字不一样,还能否union?
答:可以,以第一张表的列名为准。
问:union满足什么条件就可以用了?
答:只要结果集中的列数一致就可以。
问:列的类型不一致也可以嘛?
答:见上题。
问:union后的结果集可否再排序呢?
答:可以。
问:使用union语句时,内层的排序有时发挥效果有时不发挥效果,这是问什么?
答:内层的order by语句单独使用时,不影响结果集,仅排序,在执行期间就被mysql的代码分析器给优化掉了,内层的order by必须能够影响结果集时才有意义,比如配合limit使用。
问:如果union后的结果有重复(即某2行或n行的值都一样),怎么办?
答:这种情况是比较常见的默认会去重。
问:如果不想去重怎么办?
答:union all
函数
如果mysql函数和javascript函数都实现某个功能,优先使用哪一个?
mysql的函数肯定是要影响查询速度。应该在建表时通过合理的表结构减少函数的使用合理的表结构减少函数的使用;
如果确实要使用函数,比如时间的格式化,优先放在业务逻辑层,即用javascript处理;
在查询时使用了函数,最大的一个坏处是索引无法使用,比如date_format(A),则A列的索引将无法使用。在where函数中,使用了索引,where查询的是函数处理后的条件,索引失效。
视图
视图View可以看成一张虚拟表,是表通过某种运算得到的一个投影,表的变化会影响到视图。
视图的定义一直存在,但不占用空间。
对于一些简单的视图,它在发挥作用的过程中,并没有建立临时表,而只是把条件存起来,下次查询,把条件一合并,直接去查表。
问:建视图的时候要指定视图的列名与列类型么?
答:不要,它只是一个投影,是一种关系,会继承原表的字段。
- 创建视图的语法
既然视图只是表的某种查询的投影,所以主要步骤在于查询表上,查询的结果命名为视图就可以了。
create view 视图名字 as select语句
视图有什么用
- 可以简化查询;
- 可以更精细的权限控制,比如某张用户表,2个网站搞合作,可以查询对方网站的用户,需要向对方开放用户表的权限,但是又不想开放用户表中的密码字段,可以使用视图;
- 数据多,分表时可以用到。比如小说站,article表,1000万篇,分成article1、article2…article5,查询小说时,不知道在哪张表上,可以创建一个五张表合在一起的视图一次型查出。
表与视图数据变化时的相互影响问题
- 表的数据变化,视图跟着变
- 视图的数据发生变化时,满足视图的数据和表的数据一一对应,类似函数和反函数的关系时,会影响到原表
注意:一一对应指的是根据select关系,从表中取出的行,只能计算出视图中确定的一行,反之,视图中任意抽一行,能够反推出表中确定的一行
注意:order by limit得到的结果与表不是一一对应的
- 删除视图
drop view 表名
algorithm
视图合并查询条件 vs 临时表
如果将创建的视图看作临时表
1
2tmp = select * from godds where shop_price > 300;
select * from tmp where shop_price < 500;如果没有临时表,查这个视图,还有没有可能完成
可以。查原表,把建视图时的条件和查询视图的条件叠加起来,直接去查原表。
这两种方式哪个快
- 建表:查询=>形成临时表=>查询临时表
- 叠加:合并条件=>查询表
到底是创建临时表还是合并语句,就由algorithm来决定,当它的值为:
- merge:合并查询语句
- temptable:临时表
undefined:未定义,由系统判断
合并条件的视图:
1
2
3create algorithm=merge view v2
as
select * from goods where shop_price>300;建临时表的视图:
1
2
3
4
5
6
7<!-- 复杂查询 -->
<!-- 比如建立平均价格视图 -->
create algorithm=temptable view v3
as
select goods_id,cat_id,goods_name,shop_price
from goods
order by cat_id asc,shop_price desc;由系统来决定:
1
如果拿不准就写algorithm=undefined,由系统来决定
字符集
- 字符集的概念
2进制编码到字符的映射就是字符集。
- 一些字符集
ascii、ansi、gb2312、gbk、unicode、utf-8。
从容量上来看:GB2312<GBK<UTF-8
- unicode和utf-8的关系
unicode用4个字节来编号,有2^32种组合。
unicode只负责分配编号,而且都是用4个字节来分配编号。
utf-8在不改变编号的基础上简化字节,把高位浪费的0值,用一定的规则舍弃。
unicode和utf-8的关系就像源文件和压缩文件的关系。由给定的unicode字符可=>utf-8的二进制值,反过来,由utf-8的二进制值=>unicode字符。
- utf8的长度
变长,否则压缩就没有意义了。
- 如何截取utf8(各国语言都有),无乱码?
从头开始,取1个字节。通过位运算,计算连续的1的个数。如果为0,则截取1个字节,如果为N,则截取N个字节。
- GBK是如何转为UTF-8的?
GBK也是和unicode有对应关系的,GBK=>unicode=>utf8。
乱码原因
- 可修复的情况:解码时与实际编码不一致
- 不可修复的情况:传输过程中,编码不一致,导致字节丢失
客户端和服务器端的编码转换
客户端与服务器之间存在一个 连接器。客户端的字符先发给连接器,连接器选择一种编码将其转换,临时存储,之后再次转换成服务器需要的编码,并最终存储在服务器。
告诉连接器客户端使用GBK
1 | set character_set_client=gbk; |
告诉连接器使用utf8
1 | set character_set_connection=utf8; |
告诉连接器如果返回值,返回GBK结果
1 | set character_set_results=gbk; |
如果上面三个参数都设置成GBK,可以简写,简写成:
1 | set names gbk; |
- 使用上面三个设置的一个基本原则
服务器字符集容量 >= 连接器字符集容量 >= 客户端字符集容量
保证下面三者字符集的一直就绝不会出现乱码的可能
- 网页文件本身编码
- meta信息
- client/connection/results的指定