- 数据库基础
- 需要你自己新建一个my.ini的配置文件
- 验证配置是否真的是自动加载
- 修改配置文件后一定要重启服务才能生效
- 统一编码的配置 无需掌握 直接拷贝即可
- 偷懒 将管理员的用户名和密码也添加到配置文件中
- 存储引擎
- 创建表的完整语法
- 基本数据类型
- 严格模式
- 字段类型
- 存储限制
- 精确度验证
- 要结合实际应用场景 三者都能使用
- 枚举字段 后期在存数据的时候只能从枚举里面选择一个存储
- 集合可以只写一个 但是不能写没有列举的
- 约束条件
- 表与表之间关系
- 修改表
- 复制表
- SQL语句查询
- 模糊匹配 like
- 针对null数据 判断的时候用is不要用=
- 排序 默认是升序
- Navicat软件
- pymysql模块
- 视图
- 固定语法
- 具体操作
- 触发器
- 事务
- 事务相关的关键字
- 1 开启事务
- 2 回滚(回到事务执行之前的状态)
- 3 确认(确认之后就无法回滚了)
- 1 先开启事务
- 2 多条sql语句
- 三种开发模型
- 存储过程
- 针对形参res 不能直接传数据 应该传一个变量名
- 定义变量
- 查看变量对应的值
- 函数
- 流程控制
- 索引
数据库基础
存储数据的演变过程
- 随意的存到一个文件中、数据格式也是千差万别的完全取决于我们自己
"""# 小李jason|123|NB# 小王egon-123-DBJ# 小红tank~123~hecha"""
- 软件开发目录规范
限制了存储数据的具体位置"""binconfcoredblibreadme"""
假设上述是一个单机游戏
那么每个人的游戏记录只会在自己的计算机上面保存
注册登录的账号也只能在自己的计算机上有效
- 如何将单机变成连网
"""将数据库保存部分全部统一起来所有人操作数据都来一个地方操作"""
数据库的本质
"""本质其实就是一款基于网络通信的应用程序那其实每个人都可以开发一款数据库软件 因为它仅仅就是一个基于网络通信的应用程序也就意味着数据库软件其实有很多很多关系型数据库MySQL、oracle、db2、access、sql server非关系型数据库redis、mongodb、memcache"""# 关系型1 数据之间彼此有关系或者约束男生表和前女友表2 存储数据的表现形式通常是以表格存储name password hobbyjason 123 学习egon 123 女教练tank 123 吃生蚝每个字段还会有存储类型的限制比如姓名只能存字符串...# 非关系型存储数据通常都是以k,v键值对的形式
MySQL
"""任何基于网络通信的应用程序底层用的都是socket-服务端-基于socket通信-收发消息-SQL语句-客户端-基于socket通信-收发消息-SQL语句"""MySQL不单单支持MySQL自己的客户端app还支持其他编程语言来充当客户端操作如何解决语言沟通的障碍?# 1 让服务端兼容所有的语言(一个人精通多国语言)# 2 采用统一的语言(SQL语句)
重要概念介绍
"""库 》》》 文件夹表 》》》 文件记录 》》》 文件内一行行的数据name password hobbyjason 123 学习egon 123 女教练tank 123 吃生蚝表头 表格的第一行字段字段 name、password、hobby"""
MySQL的安装
"""在IT界 一般都不会轻易的使用最新版本的软件,因为新版本可能会出现各种问题(你原本项目跑的好好的 非要画蛇添足更新版本 然后项目奔溃)小段子:更新完没事 那么你还是一个普通员工更新完出事 那么你就是一名"烈士""""# MySQL有很多版本(5.6、5.7、8.0) 目前企业里面用的比较多的还是5.6左右
下载5.6版本即可,如果你下载了其他版本问题也不大,因为sql语句是一样的学
按照教学方式下载,会将服务端和客户端一并下载到本地
为了学习方便我们将服务端和客户端都在本地启动,后期到了公司服务端会专门跑在一台服务器上,所有人基于网络连接服务端操作
MySQL服务端与客户端
"""服务端mysqld.exe客户端mysql.exe"""
注意
"""在前期配置MySQL的时候 cmd终端尽量以管理员的身份运行windows+r 输入cmd 进入的是普通用户终端 有一些命令是无法执行的搜索cmd右键 以管理员身份运行"""
启动
先切换到mysqld所在的bin目录下,然后输入mysqld即可
保留原来的cmd窗口重新打开一个 ```python “”” 常见软件的默认端口号 MySQL 3306 redis 6379 mongodb 27017 django 8000 flask 5000 …
MySQL第一次以管理员身份进入是没有密码的 直接回车即可
客户端连接服务端完整命令 mysql -h 127.0.0.1 -P 3306 -uroot -p “””
<a name="7733bae9"></a>## sql语句初识```css"""1 MySQL中的sql语句是以分号作为结束的标志2 基本命令show databases; 查看所有的库名3 连接服务端的命令可以简写mysql -uroot -p4 当你输入的命令不对 又不想让服务端执行并返回报错信息 可以用\c取消错误命令 \c5 客户端退出 退出命令加不加分号都可以执行quitexit6 当你在连接服务端的时候 发现只输入mysql也能连接但是你不是管理员身份 而只是一个游客模式"""
环境变量配置及系统服务制作
小知识点补充
"""1 如何查看当前具体进程tasklisttasklist |findstr mysqld2 如何杀死具体进程(只有在管理员cmd窗口下才能成功)taskkill /F /PID PID号"""
环境变量配置
"""每次启动mysqld需要先切到对应的文件路径下才能操作太多繁琐将mysqld所在的文件路径添加到系统环境变量中"""
还是繁琐 需要起两个cmd窗口 不好
将mysql服务端制作成系统服务(开机自启动)
"""查看当前计算机的运行进程数services.msc将mysql制作成系统服务mysqld --install移除mysql系统服务mysqld --remove"""
设置密码
"""mysqladmin -uroot -p原密码 password 新密码改命令直接在终端输入即可 无序进入客户端mysqladmin -uroot -p123 password 123456"""#重置当前用户的密码update mysql.user set password=password(123) where user='root' and host='localhost';
破解密码
出去玩了好几个月,回来返现密码忘了,死活进不去
"""你可以将mysql获取用户名和密码校验的功能看成是一个装饰器装饰在了客户端请求访问的功能上我们如果将该装饰器移除 那么mysql服务端就不会校验用户名和密码了"""# 1 先关闭当前mysql服务端命令行的方式启动(让mysql跳过用户名密码验证功能)mysqld --skip-grant-tables# 2 直接以无密码的方式连接mysql -uroot -p 直接回车# 3 修改当前用户的密码update mysql.user set password=password(123456) where user='root' and host='localhost';"""真正存储用户表的密码字段 存储的肯定是密文只有用户自己知道明文是什么 其他人都不知道 这样更加的安全密码比对也只能比对密文"""# 4 立刻将修改数据刷到硬盘flush privileges;# 5 关闭当前服务端 然后以正常校验授权表的形式启动
统一编码
- mysql默认的配置文件 ```css “”” my-default.ini ini结尾的一般都是配置文件
程序启动会先加载配置文件中的配置之后才真正的启动 “””
[mysqld] # 一旦服务端启动立刻加载下面的配置 sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
[mysql] # 一旦客户端启动立刻加载下面的配置 …
[client] # 其他客户端 …
需要你自己新建一个my.ini的配置文件
验证配置是否真的是自动加载
[mysql] print(‘hello world’)
修改配置文件后一定要重启服务才能生效
统一编码的配置 无需掌握 直接拷贝即可
偷懒 将管理员的用户名和密码也添加到配置文件中
[mysqld] character-set-server=utf8 collation-server=utf8_general_ci [client] default-character-set=utf8 [mysql] user=”root” password=123456 default-character-set=utf8
<a name="00ec7958"></a># sql语句基础PS:大部分程序的业务逻辑其实都是增删改查```python"""辛辛苦苦一个月 只为了一条sql语句(将自己账户的钱修改一下)"""
针对库的增删改查(文件夹)
# 增create database db1;create database db2 charset='gbk';# 查show databases; # 查所有show create database db1; # 查单个# 改alter database db2 charset='utf8';# 删drop database db2;
针对表的增删改查(文件)
"""在操作表(文件)的时候 需要指定所在的库(文件夹)"""# 查看当前所在的库的名字select database();# 切换库use db1;# 增create table t1(id int,name char(4));# 查show tables; # 查看当前库下面所有的表名show create table t1; #查看创建t1表的语句。describe t1; # desc t1查看t1表字段;# 改alter table t1 modify name char(16); #修改表字段字符数# 删drop table t1; 删除表"""create table db2.t1(id int); 也可以用绝对路径的形式操作不同的库"""
针对数据的增删改查(一行行数据)
"""一定要先有库 有表 最后才能操作记录"""# 增insert into t1 values(1,'jason');insert into t1 values(1,'jason'),(2,'egon'),(3,'tank');# 查select * from t1; # 该命令当数据量特别大的时候不建议使用select name from t1;# 改update t1 set name='DSB' where id > 1;# 删delete from t1 where id > 1;delete from t1 where name='jason';# 将表所有的数据清空delete from t1;
存储引擎
日常生活中文件格式有很多中,并且针对不同的文件格式会有对应不同存储方式和处理机制(txt,pdf,word,mp4…)
针对不同的数据应该有对应的不同的处理机制来存储
存储引擎就是不同的处理机制
MySQL主要存储引擎
Innodb
是MySQL5.5版本及之后默认的存储引擎
存储数据更加的安全myisam
是MySQL5.5版本之前默认的存储引擎
速度要比Innodb更快 但是我们更加注重的是数据的安全memory
内存引擎(数据全部存放在内存中) 断电数据丢失blackhole
无论存什么,都立刻消失(黑洞)
"""# 查看所有的存储引擎show engines;# 不同的存储引擎在存储表的时候 异同点create table t1(id int) engine=innodb;create table t2(id int) engine=myisam; #创建myism引擎的表;create table t3(id int) engine=blackhole;create table t4(id int) engine=memory;# 存数据insert into t1 values(1);insert into t2 values(1);insert into t3 values(1);insert into t4 values(1);"""
创建表的完整语法
# 语法create table 表名(字段名1 类型(宽度) 约束条件,字段名2 类型(宽度) 约束条件,字段名3 类型(宽度) 约束条件)# 字段1 在同一张表中字段名不能重复2 宽度和约束条件是可选的(可写可不写) 而字段名和字段类型是必须的约束条件写的话 也支持写多个字段名1 类型(宽度) 约束条件1 约束条件2...,create table t5(id); 报错3 最后一行不能有逗号create table t6(id int,name char, #不能有逗号); 报错# 字段宽度一般情况下指的是对存储数据的限制create table t7(name char); 默认宽度是1insert into t7 values('jason');insert into t7 values(null); 关键字NULL针对不同的版本会出现不同的效果5.6版本默认没有开启严格模式 规定只能存一个字符你给了多个字符,那么我会自动帮你截取5.7版本及以上或者开启了严格模式 那么规定只能存几个就不能超,一旦超出范围立刻报错 Data too long for ...."""严格模式到底开不开呢?"""MySQL5.7之后的版本默认都是开启严格模式的使用数据库的准则:能尽量少的让数据库干活就尽量少 不要给数据库增加额外的压力# 约束条件 null not null不能插入nullcreate table t8(id int,name char not null);"""宽度和约束条件到底是什么关系宽度是用来限制数据的存储约束条件是在宽度的基础之上增加的额外的约束"""
基本数据类型
整型
分类
TINYINT SMALLINT MEDUIMINT INT BIGINT作用
存储年龄、等级、id、号码等等
"""以TINYINT是否有符号默认情况下是带符号的超出会如何超出限制只存最大可接受值"""create table t9(id tinyint);insert into t9 values(-129),(256);# 约束条件之unsigned 无符号create table t10(id tinyint unsigned);create table t11(id int);# int默认也是带符号的# 整型默认情况下都是带有符号的# 针对整型 括号内的宽度到底是干嘛的create table t12(id int(8));insert into t12 values(123456789);"""特例:只有整型括号里面的数字不是表示限制位数id int(8)如果数字没有超出8位 那么默认用空格填充至8位如果数字超出了8位 那么有几位就存几位(但是还是要遵守最大范围)"""create table t13(id int(8) unsigned zerofill); # 用0填充至8位# 总结:针对整型字段 括号内无需指定宽度 因为它默认的宽度以及足够显示所有的数据了
严格模式
# 如何查看严格模式show variables like "%mode";模糊匹配/查询关键字 like%:匹配任意多个字符_:匹配任意单个字符# 修改严格模式set session 只在当前窗口有效set global 全局有效set global sql_mode = 'STRICT_TRANS_TABLES'; #字段类型严格初始默认:sql_mode NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION修改完之后 重新进入服务端即可针对不同的版本会出现不同的效果5.6版本默认没有开启严格模式 规定只能存一个字符你给了多个字符,那么我会自动帮你截取5.7版本及以上或者开启了严格模式 那么规定只能存几个就不能超,一旦超出范围立刻报错 Data too long for ....# 再次修改sql_mode 让MySQL不要做自动剔除操作set global sql_mode = 'STRICT_TRANS_TABLES,PAD_CHAR_TO_FULL_LENGTH';
字段类型
浮点型
分类
FLOAT、DOUBLE、DECIMAL作用
身高、体重、薪资 ```css存储限制
float(255,30) # 总共255位 小数部分占30位 double(255,30) # 总共255位 小数部分占30位 decimal(65,30) # 总共65位 小数部分占30位
精确度验证
create table t15(id float(255,30)); create table t16(id double(255,30)); create table t17(id decimal(65,30)); “””你们在前期不要给我用反向键 所有的命令全部手敲!!!增加熟练度”””
insert into t15 values(1.111111111111111111111111111111); insert into t16 values(1.111111111111111111111111111111); insert into t17 values(1.111111111111111111111111111111);
float < double < decimal
要结合实际应用场景 三者都能使用
<a name="b33ed1b1"></a>## 字符类型-分类```css"""char定长char(4) 数据超过四个字符直接报错 不够四个字符空格补全varchar变长varchar(4) 数据超过四个字符直接报错 不够有几个存几个"""create table t18(name char(4));create table t19(name varchar(4));insert into t18 values('a');insert into t19 values('a');# 介绍一个小方法 char_length统计字段长度select char_length(name) from t18;select char_length(name) from t19;"""首先可以肯定的是 char硬盘上存的绝对是真正的数据 带有空格的但是在显示的时候MySQL会自动将多余的空格剔除"""# 再次修改sql_mode 让MySQL不要做自动剔除操作set global sql_mode = 'STRICT_TRANS_TABLES,PAD_CHAR_TO_FULL_LENGTH';
char与varchar对比
"""char缺点:浪费空间优点:存取都很简单直接按照固定的字符存取数据即可jason egon alex wusir tank存按照五个字符存 取也直接按照五个字符取varchar优点:节省空间缺点:存取较为麻烦1bytes+jason 1bytes+egon 1bytes+alex 1bytes+tank #1个字节记录字符长度存的时候需要制作报头取的时候也需要先读取报头 之后才能读取真实数据以前基本上都是用的char 其实现在用varchar的也挺多"""补充:进来公司之后你完全不需要考虑字段类型和字段名因为产品经理给你发的邮件上已经全部指明了
时间类型
- 分类
date:年月日 2020-5-4
datetime:年月日时分秒 2020-5-4 11:11:11
time:时分秒11:11:11
Year:2020create table student(id int,name varchar(16),born_year year,birth date,study_time time,reg_time datetime);insert into student values(1,'egon','1880','1880-11-11','11:11:11','2020-11-11 11:11:11');
枚举与集合类型
- 分类
"""枚举(enum) 多选一集合(set) 多选多"""
- 具体使用
```python
create table user(
id int,
name char(16),
gender enum(‘male’,’female’,’others’)
);
insert into user values(1,’jason’,’male’); 正常
insert into user values(2,’egon’,’xxxxooo’); 报错
枚举字段 后期在存数据的时候只能从枚举里面选择一个存储
create table teacher( id int, name char(16), gender enum(‘male’,’female’,’others’), hobby set(‘read’,’DBJ’,’hecha’) ); insert into teacher values(1,’jason’,’male’,’read’); 正常 insert into teacher values(2,’egon’,’female’,’DBJ,hecha’); 正常 #集合可以插入多个数据 insert into teacher values(3,’tank’,’others’,’生蚝’); 报错
集合可以只写一个 但是不能写没有列举的
<br />**总结**```css"""字段类型严格模式约束条件not nullzerofillunsigned"""
约束条件
"""not nullzerofillunsigneddefaultgender enum('male','female','others') default 'male' #给可选增加默认值unique单列唯一id int unique联合唯一ip int,port int,unique(ip,port)primary key 主键1.在限制效果上跟not null + unique一致id int primary key2.它还是Innodb存储引擎组织数据的依据1.使用Innodb规定一张表中必须有且只有一个主键2.当你没有设置主键的时候1.从上往下查找非空且唯一的自动升级为主键2.如果什么都没有设置 那么就会使用内部隐藏的字段作为主键无法使用到3.一张表中通常都应该有一个id字段并且该字段应该是主键auto_increment自增列该限制条件只能加在被设置成键的字段上 并且一般都是跟主键一起使用id int primary key auto_increment"""针对主键补充知识点你在将表中数据删除的时候 主键的自增并不会停止truncate 清空数据并重置主键
外键
"""外键就是用来在代码层面真正的实现表与表之间的关系foreign key"""
表与表之间关系
"""表与表之间的关系只有三种一对多在MySQL中 一对多和多对一 都叫一对多外键字段建在多的那一方多对多关系无需建外键 而是单独开设一张表专门用来存储关系一对一外键字段建在任意一方均可但是推荐你建在查询频率较高的表中""""""判断表关系前期不熟练一定要换位思考 慢慢问自己"""# 一对多判断图书与出版社先站在图书表一本书能否被多个出版社出版 版权问题 不可以!!!再站在出版社表一个出版社能否出版多本书 可以结论:单向的一对多成立 那么表关系就是一对多 书是多的一方外键带来的约束1.在创建表的时候一定要先创建被关联表2.在插入数据的时候也要先插入被关联表3.操作数据的时候 会出现多种限制 同步更新 同步删除create table publish(id ...);create table book(id ...publish_id int,foreign key(publish_id) references publish(id)on update cascade # 同步更新on delete cascade # 同步删除);# 多对多图书和作者先站在图书表一本书可不可以有多个作者 可以!!!再站在作者表一个作者能不能写多本书 可以!!!结论:图书和作者是双向的一对多 那么表关系就是 多对多一定要单独开设一张新的表存储表关系create table book(id ...)create table author(id ...)create table book2author(id ...book_id int,author_id int,foreign key(book_id) references book(id)on update cascade # 同步更新on delete cascade, # 同步删除foreign key(author_id) references author(id)on update cascade # 同步更新on delete cascade, # 同步删除)# 一对一qq用户表客户与学生表"""当你一张表中的数据并不都是频率需要用到的情况 但是字段有特别的多那么这个时候你就应该考虑分表 然后做一对一的关联节省查询时间和传输时间"""作者与作者详情无论站在哪一方都不能成立一对多双方一对多都不成立那么表关系一对一没有关系create table author(id ...authordetail_id int unique,foreign key(authordetail_id) references authordetail(id)on update cascade # 同步更新on delete cascade, # 同步删除);create tabel authordetail(id ...);
补充
"""表与表之间如果有关系的话 可以有两种建立联系的方式1.就是通过外键强制性的建立关系2.就是自己通过sql语句逻辑层面上建立关系delete from emp where id=1;delete from dep where id=1;创建外键会消耗一定的资源 并且增加了表与表之间的耦合度在实际项目中 如果表特别多 其实可以不做任何外键处理 直接通过sql语句来建立逻辑层面上的关系到底用不用外键取决于实际项目需求"""
修改表
"""alter table t1 rename new_t1;alter table t1 add 字段名 ...;alter table t1 add 字段名 ... first;alter table t1 add 字段名 ... after 旧字段;alter table t1 drop 字段名;modify一般都是用来修改字段的字段类型 约束条件等 不能修改字段名alter table t1 modify 字段名 数据类型(宽度) 约束条件alter table t1 change 旧字段名 新字段名 ...;"""
复制表
"""我们sql语句查询出来的结果也可以看成是一张表(虚拟表)言外之意 就是针对这个查询结果还可以继续用查询表的语法继续操作该虚拟表"""
- 作业
"""自己联系表关系的判断与创建笔记里面给的代码只是一个参考而已"""
SQL语句查询
- 如何查询表
"""selectwheregroup byhavingdistinctorder bylimitregexplike..."""
- 连表操作理论
前期表准备
create table emp(id int not null unique auto_increment,name varchar(20) not null,sex enum('male','female') not null default 'male', #大部分是男的age int(3) unsigned not null default 28,hire_date date not null,post varchar(50),post_comment varchar(100),salary double(15,2),office int, #一个部门一个屋子depart_id int);#插入记录#三个部门:教学,销售,运营insert into emp(name,sex,age,hire_date,post,salary,office,depart_id) values('jason','male',18,'20170301','张江第一帅形象代言',7300.33,401,1), #以下是教学部('tom','male',78,'20150302','teacher',1000000.31,401,1),('kevin','male',81,'20130305','teacher',8300,401,1),('tony','male',73,'20140701','teacher',3500,401,1),('owen','male',28,'20121101','teacher',2100,401,1),('jack','female',18,'20110211','teacher',9000,401,1),('jenny','male',18,'19000301','teacher',30000,401,1),('sank','male',48,'20101111','teacher',10000,401,1),('哈哈','female',48,'20150311','sale',3000.13,402,2),#以下是销售部门('呵呵','female',38,'20101101','sale',2000.35,402,2),('西西','female',18,'20110312','sale',1000.37,402,2),('乐乐','female',18,'20160513','sale',3000.29,402,2),('拉拉','female',28,'20170127','sale',4000.33,402,2),('僧龙','male',28,'20160311','operation',10000.13,403,3), #以下是运营部门('程咬金','male',18,'19970312','operation',20000,403,3),('程咬银','female',18,'20130311','operation',19000,403,3),('程咬铜','male',18,'20150411','operation',18000,403,3),('程咬铁','female',18,'20140512','operation',17000,403,3);# 当表字段特别多 展示的时候错乱 可以使用\G分行展示select * from emp\G;# 个别同学的电脑在插入中文的时候还是会出现乱码或者空白的现象 你可以将字符编码统一设置成GBK
几个重要关键字的执行顺序
# 书写顺序select id,name from emp where id > 3;# 执行顺序fromwhereselect"""虽然执行顺序和书写顺序不一致 你在写sql语句的时候可能不知道怎么写你就按照书写顺序的方式写sqlselect * 先用*号占位之后去补全后面的sql语句最后将*号替换后你想要的具体字段明天会一直使用 这里先理解"""
where筛选条件
# 作用:是对整体数据的一个筛选操作# 1.查询id大于等于3小于等于6的数据select id,name,age from emp where id>=3 and id<=6;select id,name from emp where id between 3 and 6; 两者等价# 2.查询薪资是20000或者18000或者17000的数据select * from emp where salary=20000 or salary=18000 or salary=17000;select * from emp where salary in (20000,18000,17000);# 3.查询员工姓名中包含字母o的员工的姓名和薪资"""模糊查询like% 匹配任意多个字符_ 匹配任意单个字符"""select name,salary from emp where name like '%o%';# 4.查询员工姓名是由四个字符组成的 姓名和薪资 char_length() _select name,salary from emp where name like '____';select name,salary from emp where char_length(name) = 4;# 5.查询id小于3或者id大于6的数据select * from emp where id not between 3 and 6;# 6.查询薪资不在20000,18000,17000范围的数据select * from emp where salary not in (20000,18000,17000);# 7.查询岗位描述为空的员工姓名和岗位名 针对null不用等号 用isselect name,post from emp where post_comment = NULL;select name,post from emp where post_comment is NULL;
group by分组
# 分组实际应用场景 分组应用场景非常的多男女比例部门平均薪资部门秃头率国家之间数据统计# 1 按照部门分组select * from emp group by post;"""分组之后 最小可操作单位应该是组 还不再是组内的单个数据上述命令在你没有设置严格模式的时候是可正常执行的 返回的是分组之后 每个组的第一条数据 但是这不符合分组的规范:分组之后不应该考虑单个数据 而应该以组为操作单位(分组之后 没办法直接获取组内单个数据)如果设置了严格模式 那么上述命令会直接报错"""set global sql_mode = 'strict_trans_tables,only_full_group_by';设置严格模式之后 分组 默认只能拿到分组的依据select post from emp group by post;按照什么分组就只能拿到分组 其他字段不能直接获取 需要借助于一些方法(聚合函数)"""什么时候需要分组啊???关键字每个 平均 最高 最低聚合函数maxminsumcountavg"""# 1.获取每个部门的最高薪资select post,max(salary) from emp group by post;select post as '部门',max(salary) as '最高薪资' from emp group by post;select post '部门',max(salary) '最高薪资' from emp group by post;# as可以给字段起别名 也可以直接省略不写 但是不推荐 因为省略的话语意不明确 容易错乱# 2.获取每个部门的最低薪资select post,min(salary) from emp group by post;# 3.获取每个部门的平均薪资select post,avg(salary) from emp group by post;# 4.获取每个部门的工资总和select post,sum(salary) from emp group by post;# 5.获取每个部门的人数select post,count(id) from emp group by post; # 常用 符合逻辑select post,count(salary) from emp group by post;select post,count(age) from emp group by post;select post,count(post_comment) from emp group by post; null不行# 6.查询分组之后的部门名称和每个部门下所有的员工姓名# group_concat不单单可以支持你获取分组之后的其他字段值 还支持拼接操作select post,group_concat(name) from emp group by post;select post,group_concat(name,'_DSB') from emp group by post;select post,group_concat(name,':',salary) from emp group by post;# concat不分组的时候用select concat('NAME:',name),concat('SAL:',salary) from emp;# 补充 as语法不单单可以给字段起别名 还可以给表临时起别名select emp.id,emp.name from emp;select emp.id,emp.name from emp as t1; 报错select t1.id,t1.name from emp as t1;# 查询每个人的年薪 12薪select name,salary*12 from emp;
分组注意事项
# 关键字where和group by同时出现的时候group by必须在where的后面where先对整体数据进行过滤之后再分组操作where筛选条件不能使用聚合函数select id,name,age from emp where max(salary) > 3000;select max(salary) from emp; # 不分组 默认整体就是一组# 统计各部门年龄在30岁以上的员工平均薪资1 先求所有年龄大于30岁的员工select * from emp where age>30;2 再对结果进行分组select * from emp where age>30 group by post;select post,avg(salary) from emp where age>30 group by post;
having分组之后的筛选条件
"""having的语法根where是一致的只不过having是在分组之后进行的过滤操作即having是可以直接使用聚合函数的"""# 统计各部门年龄在30岁以上的员工平均工资并且保留平均薪资大于10000的部门select post,avg(salary) from empwhere age>30group by posthaving avg(salary) > 10000;
distinct去重
"""一定要注意 必须是完全一样的数据才可以去重!!!一定不要将逐渐忽视了 有逐渐存在的情况下 是不可能去重的[{'id':1,'name':'jason','age':18},{'id':2,'name':'jason','age':18},{'id':3,'name':'egon','age':18}]ORM 对象关系映射 让不懂SQL语句的人也能够非常牛逼的操作数据库表 类一条条的数据 对象字段对应的值 对象的属性你再写类 就意味着在创建表用类生成对象 就意味着再创建数据对象点属性 就是在获取数据字段对应的值目的就是减轻python程序员的压力 只需要会python面向对象的知识点就可以操作MySQL"""select distinct id,age from emp;select distinct age from emp;
order by排序
select * from emp order by salary;select * from emp order by salary asc;select * from emp order by salary desc;"""order by默认是升序 asc 该asc可以省略不写也可以修改为降序 desc"""select * from emp order by age desc,salary asc;# 先按照age降序排 如果碰到age相同 则再按照salary升序排# 统计各部门年龄在10岁以上的员工平均工资并且保留平均薪资大于1000的部门,然后对平均工资降序排序select post,avg(salary) from empwhere age>10group by posthaving avg(salary) > 1000order by avg(salary) desc;
limit限制展示条数
select * from emp;"""针对数据过多的情况 我们通常都是做分页处理"""select * from emp limit 3; # 只展示三条数据select * from emp limit 0,5;select * from emp limit 5,5;第一个参数是起始位置第二个参数是展示条数
正则
select * from emp where name regexp '^j.*(n|y)$';
多表操作
前期表准备
#建表create table dep(id int,name varchar(20));create table emp(id int primary key auto_increment,name varchar(20),sex enum('male','female') not null default 'male',age int,dep_id int);#插入数据insert into dep values(200,'技术'),(201,'人力资源'),(202,'销售'),(203,'运营');insert into emp(name,sex,age,dep_id) values('jason','male',18,200),('egon','female',48,201),('kevin','male',18,201),('nick','male',28,202),('owen','male',18,203),('jerry','female',18,204);
表查询
select * from dep,emp; # 结果 笛卡尔积"""了解即可 不知道也没关系"""select * from emp,dep where emp.dep_id = dep.id;"""MySQL也知道 你在后面查询数据过程中 肯定会经常用到拼表操作所以特地给你开设了对应的方法inner join 内连接left join 左连接right join 右连接union 全连接"""# inner join 内连接select * from emp inner join dep on emp.dep_id = dep.id;# 只拼接两张表中公有的数据部分# left join 左连接select * from emp left join dep on emp.dep_id = dep.id;# 左表所有的数据都展示出来 没有对应的项就用NULL# right join 右连接select * from emp right join dep on emp.dep_id = dep.id;# 右表所有的数据都展示出来 没有对应的项就用NULL# union 全连接 左右两表所有的数据都展示出来select * from emp left join dep on emp.dep_id = dep.idunionselect * from emp right join dep on emp.dep_id = dep.id;
子查询
"""子查询就是我们平时解决问题的思路分步骤解决问题第一步第二步...将一个查询语句的结果当做另外一个查询语句的条件去用"""# 查询部门是技术或者人力资源的员工信息1 先获取部门的id号2 再去员工表里面筛选出对应的员工select id from dep where name='技术' or name = '人力资源';select name from emp where dep_id in (200,201);select * from emp where dep_id in (select id from dep where name='技术' or name = '人力资源');
总结
表的查询结果可以作为其他表的查询条件也可以通过起别名的方式把它作为一个张虚拟表根其他表关联"""多表查询就两种方式先拼接表再查询子查询 一步一步来"""
查询语句复习
- 查询主要关键字
select distinct 字段1,字段2,... from 表名where 分组之前的筛选条件group by 分组条件having 分组之后的筛选条件order by 排序字段1 asc,排序字段2 desclimit 5,5
- where ```python where id>=3 and id<=6; where id between 3 and 6;
where salary=18000 or salary=17000; where salary in (18000,17000); where salary not in (18000,17000);
where salary*20 from emp;
模糊匹配 like
“”” % 任意多个字符 任意单个字符 “”” where name like ‘%mode%’; where name like ‘_‘; where char_length(name) = 4;
针对null数据 判断的时候用is不要用=
where post_comment is null;
-group by```css# 分组的应用场景非常多每个平均最大最小..."""分组之后只能直接获取到分组的依据 其他字段都无法直接获取set global sql_mode = 'only_full_group_by'"""select * from emp group by post; 报错select post from emp group by post;# group_concat:帮助我们获取到分组之外的字段信息并且可以拼接多个字段select post,group_concat(salary,':',name) from emp;# concat:分组之前帮助我们获取字段信息并且可以拼接多个字段select concat(name,'??') from emp;# concat_ws:如果多个字段之间的连接符号是相同的情况下 你可以直接使用concat_ws来完成select concat_ws(':',name,age,sex) from emp;"""复习'?'.join([111,222,333,444]) 报错!"""# as语法1 可以给展示字段起别名2 可以给表起别名# 聚合函数maxminsumcountavg聚合函数必须在分组之后使用
- having
# 用法根where一模一样 只不过它是作用于分组之后的再次筛选....group by post having avg(salary) > 30000;
- distinct
# 数据必须是一模一样的情况下才能去重select distinct post from emp;
order by salary asc,age desc; # 还支持多个字段备用比较
-limit```python"""限制数据的展示条数 效果就是分页的效果"""select * from emp limit 5;limit 5;limit 5,5 第一个参数是起始位置 第二个参数是条数
- regexp
"""正则是一门独立的语言在python中如果你想使用正则需要借助于re模块面试题1.re模块中常用的方法findall:分组优先展示^j.*(n|y)$不会展示所有正则表达式匹配到的内容而仅仅展示括号内正则表达式匹配到的内容match:从头匹配search:从整体匹配2.贪婪匹配与非贪婪匹配正则表达式默认都是贪婪匹配的将贪婪变成非贪婪只需要在正则表达式后面加?.* 贪婪.*? 非贪婪"""select * from emp where name regexp '^j.*n$'
- 多表查询
# 联表操作select * from emp,dep; 笛卡尔积inner join只拼接两种表中都公有的部分select * from emp inner join depon emp.dep_id = dep.id;# 要加上表的前缀 不然容易造成冲突left join左表数据全部展示 没有对应的就用NULL补全right join右表数据全部展示 没有对应的就用NULL补全union左右全书展示 没有对应的就用NULL补全# 子查询"""子查询就是我们平时解决问题的思路 分步处理将一张表的查询结果当做另外一条sql语句的查询条件(当做条件的时候 用括号括起来)select * from emp where id in (select id from dep);"""
- 总结
# 书写sql语句的时候 select后面先用*占位 之后写完再改# 在写较为复杂的sql语句的时候 不要想着一口气写完 写一点查一点看一点再写!!!(只要是涉及到数据查询相关的语法都不应该一次性写完 不太现实)# 在做多表查询的时候 联表操作和子查询可能会结合使用
多表查询
# 查询平均年龄在25岁以上的部门名称"""只要是多表查询 就有两种思路 联表 子查询"""# 联表操作1 先拿到部门和员工表 拼接之后的结果2 分析语义 得出需要进行分组select dep.name from emp inner join dep """涉及到多表操作的时候 一定要加上表的前缀"""on emp.dep_id = dep.idgroup by dep.namehaving avg(age) > 25;# 子查询select name from dep where id in(select dep_id from emp group by dep_idhaving avg(age) > 25);# 关键字exists(了解)只返回布尔值 True False返回True的时候外层查询语句执行返回False的时候外层查询语句不再执行select * from emp where exists(select id from dep where id>3);select * from emp where exists(select id from dep where id>300);
今日内容概要
- navicat可视化界面操作数据库
- 数据库查询题目讲解(多表操作)
- python如何操作MySQL(pymysql模块)
- sql注入问题
- pymysql模块增删改查数据操作
Navicat软件
"""一开始学习python的时候 下载python解释器然后直接在终端书写pycharm能够更加方便快捷的帮助你书写python代码excel word pdf我们在终端操作MySQL 也没有自动提示也无法保存等等 不方便开发Navicat内部封装了所有的操作数据库的命令用户在使用它的时候只需要鼠标点点即可完成操作 无需书写sql语句"""
安装
直接百度搜索 有破解版的也有非破解非破解的有试用期 你如果不嫌麻烦 你就用使用到期之后重新装再使用 或者破解一下也很简单https://www.cr173.com/soft/126934.html下载完成后是一个压缩包 直接解压 然后点击安装 有提醒直接点击next即可navicat能够充当多个数据库的客户端navicat图形化界面有时候反应速度较慢 你可以选择刷新或者关闭当前窗口再次打开即可当你有一些需求该软件无法满足的时候 你就自己动手写sql
提示
"""1 MySQL是不区分大小写的验证码忽略大小写内部统一转大写或者小写比较即可upperlower2 MySQL建议所有的关键字写大写3 MySQL中的注释 有两种--#4 在navicat中如何快速的注释和解注释ctrl + ? 加注释ctrl + ? 基于上述操作再来一次就是解开注释如果你的navicat版本不一致还有可能是ctrl + shift + ?解开注释"""
练习题
"""课下一定要把握上课将的这几道题全部自己独立的理解并写出来在解决sql查询问题的时候 不要慌一步一步慢慢来 最终能够东拼西凑出来就过关了!!!"""-- 1、查询所有的课程的名称以及对应的任课老师姓名-- SELECT-- course.cname,-- teacher.tname-- FROM-- course-- INNER JOIN teacher ON course.teacher_id = teacher.tid;-- 4、查询平均成绩大于八十分的同学的姓名和平均成绩-- SELECT-- student.sname,-- t1.avg_num-- FROM-- student-- INNER JOIN (-- SELECT-- score.student_id,-- avg( num ) AS avg_num-- FROM-- score-- INNER JOIN student ON score.student_id = student.sid-- GROUP BY-- score.student_id-- HAVING-- AVG( num ) > 80-- ) AS t1 ON student.sid = t1.student_id;-- 7、 查询没有报李平老师课的学生姓名# 分步操作# 1 先找到李平老师教授的课程id# 2 再找所有报了李平老师课程的学生id# 3 之后去学生表里面取反 就可以获取到没有报李平老师课程的学生姓名-- SELECT-- student.sname-- FROM-- student-- WHERE-- sid NOT IN (-- SELECT DISTINCT-- score.student_id-- FROM-- score-- WHERE-- score.course_id IN ( SELECT course.cid FROM teacher INNER JOIN course ON teacher.tid = course.teacher_id WHERE teacher.tname = '李平老师' )-- );-- 8、 查询没有同时选修物理课程和体育课程的学生姓名-- (只要选了一门的 选了两门和没有选的都不要)# 1 先查物理和体育课程的id# 2 再去获取所有选了物理和体育的学生数据# 3 按照学生分组 利用聚合函数count筛选出只选了一门的学生id# 4 依旧id获取学生姓名-- SELECT-- student.sname-- FROM-- student-- WHERE-- student.sid IN (-- SELECT-- score.student_id-- FROM-- score-- WHERE-- score.course_id IN ( SELECT course.cid FROM course WHERE course.cname IN ( '物理', '体育' ) )-- GROUP BY-- score.student_id-- HAVING-- COUNT( score.course_id ) = 1-- );-- 9、 查询挂科超过两门(包括两门)的学生姓名和班级# 1 先筛选出所有分数小于60的数据# 2 按照学生分组 对数据进行计数获取大于等于2的数据SELECTclass.caption,student.snameFROMclassINNER JOIN student ON class.cid = student.class_idWHEREstudent.sid IN (SELECTscore.student_idFROMscoreWHEREscore.num < 60 GROUP BY score.student_id HAVING COUNT( score.course_id ) >= 2);
pymysql模块
"""支持python代码操作数据库MySQL"""pip3 install pymysqlimport pymysqlconn = pymysql.connect(host = '127.0.0.1',port = 3306user = 'root',password = '123456', # 还可以简写passwd = '123456'database = 'db666', # 还可以简写db = 'db666'charset = 'utf8' # 千万不要加横杆)# cursor = conn.cursor() # 括号内不加参数的话 那么查询出来的数据是元组的形式 数据不够明确 容易混乱cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 字典形式返回数据 数据有具体的描述信息 更加的合理方便sql = 'select * from user'affect_rows = cursor.execute(sql) # 返回值是当前sql语句执行的受影响的行数cursor.fetchone() # 只能结果的一条 数据本身cursor.fetchall() # 拿所有 列表套多个数据cursor.fetchmany(n) # 指定获取几条"""上述三个方法在读取数据的时候有一个类似于文件指针的特点"""cursor.scroll(1,'relative') # 相对于光标所在的当前位置往后移动cursor.scroll(1,'absolute') # 相对于数据开头往后移动
pymysql实现登录验证
"""利用一些语法的特性 书写一些特点的语句实现固定的语法MySQL利用的是MySQL的注释语法select * from user where name='jason' -- jhsadklsajdkla' and password=''select * from user where name='xxx' or 1=1 -- sakjdkljakldjasl' and password=''"""日常生活中很多软件在注册的时候都不能含有特殊符号因为怕你构造出特定的语句入侵数据库 不安全# 敏感的数据不要自己做拼接 交给execute帮你拼接即可# 结合数据库完成一个用户的登录功能?import pymysqlconn = pymysql.connect(host = '127.0.0.1',port = 3306,user = 'root',password = '123456',database = 'day48',charset = 'utf8' # 编码千万不要加-)cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 链接数据库username = input('>>>:')password = input('>>>:')sql = "select * from user where name=%s and password=%s"# 不要手动拼接数据 先用%s占位 之后将需要拼接的数据直接交给execute方法即可print(sql)rows = cursor.execute(sql,(username,password)) # 自动识别sql里面的%s用后面元组里面的数据替换if rows: #如果用户名密码查出来正确,则返回登录成功print('登录成功')print(cursor.fetchall())else:print('用户名密码错误')
作业布置
"""1 navicat自己玩一玩2 练习题一定要搞懂 照着我的思路一遍遍的看敲3 熟悉pymysql的使用4 sql注入产生的原因和解决方法 了解5 思考:如何结合mysql实现用户的注册和登录功能?"""
pymysql增删改查
import pymysqlconn = pymysql.connect(host = '127.0.0.1',port = 3306,user = 'root',passwd = '123456',db = 'day48',charset = 'utf8',autocommit = True)cursor = conn.cursor(pymysql.cursors.DictCursor)# 增sql = 'insert into user(name,password) values(%s,%s)'# rows = cursor.execute(sql,('jackson',123))rows = cursor.executemany(sql,[('xxx',123),('ooo',123),('yyy',123)]) #executemany执行多次查询print(rows)# conn.commit() # 确认# 修改# sql = 'update user set name="jasonNB" where id=1'# rows = cursor.execute(sql)# print(rows)# conn.commit() # 确认# 删除sql = 'delete from user where id=7'rows = cursor.execute(sql)print(rows)conn.commit() # 确认# 查 #查不需要提交# sql = 'select * from user'# cursor.execute(sql)# print(cursor.fetchall()) #获取查询内容"""增删改查中删改增它们的操作设计到数据的修改需要二次确认"""
存储过程
import pymysqlconn = pymysql.connect(host = '127.0.0.1',port = 3306,user = 'root',passwd = '123456',db = 'day48',charset = 'utf8',autocommit = True)cursor = conn.cursor(pymysql.cursors.DictCursor)# 调用存储过程cursor.callproc('p1',(1,5,10))"""@_p1_0=1@_p1_1=5@_p1_2=10"""# print(cursor.fetchall())cursor.execute('select @_p1_2;')print(cursor.fetchall())
今日内容概要
今日内容基本都是了解知识点,作为一名开发人员你在工作中基本用不到
- 视图(了解)
- 触发器(了解)
- 事务(需要掌握)
- 存储过程(了解)
- 内置函数(了解)
- 流程控制(了解)
- 索引理论(了解 日后再去扩展)
今日内容详细
视图
- 什么是视图
"""视图就是通过查询得到一张虚拟表,然后保存下来,下次可以直接使用其实视图也是表"""
- 为什么要用视图
"""如果要频繁的操作一张虚拟表(拼表组成的),你就可以制作成视图 后续直接操作"""
具体操作
create view teacher2course as select * from teacher INNER JOIN course on teacher.tid = course.teacher_id ;
-注意```css"""1 创建视图在硬盘上只会有表结构 没有表数据(数据还是来自于之前的表)2 视图一般只用来查询 里面的数据不要继续修改 可能会影响真正的表"""
- 视图到底使用频率高不高呢?
"""不高当你创建了很多视图之后 会造成表的不好维护"""# 总结视图了解即可 基本不用!!!
触发器
在满足对表数据进行增、删、改的情况下,自动触发的功能
使用触发器可以帮助我们实现监控、日志…
触发器可以在六种情况下
自动触发 增前 增后 删前删后 改前改后
基本语法结构
create trigger 触发器的名字 before/after insert/update/delete on 表名for each rowbeginsql语句end# 具体使用 针对触发器的名字 我们通常需要做到见名知意# 针对增create trigger tri_before_insert_t1 before insert on t1for each rowbeginsql语句endcreate trigger tri_after_insert_t1 after insert on t1for each rowbeginsql语句end"""针对删除和修改 书写格式一致"""ps:修改MySQL默认的语句结束符 只作用于当前窗口delimiter $$ 将默认的结束符号由;改为$$delimiter ;# 案例CREATE TABLE cmd (id INT PRIMARY KEY auto_increment,USER CHAR (32),priv CHAR (10),cmd CHAR (64),sub_time datetime, #提交时间success enum ('yes', 'no') #0代表执行失败);CREATE TABLE errlog (id INT PRIMARY KEY auto_increment,err_cmd CHAR (64),err_time datetime);"""当cmd表中的记录succes字段是no那么就触发触发器的执行去errlog表中插入数据NEW指代的就是一条条数据对象"""delimiter $$create trigger tri_after_insert_cmd after insert on cmdfor each rowbeginif NEW.success = 'no' theninsert into errlog(err_cmd,err_time) values(NEW.cmd,NEW.sub_time);end if;end $$delimiter ;# 朝cmd表插入数据INSERT INTO cmd (USER,priv,cmd,sub_time,success)VALUES('jason','0755','ls -l /etc',NOW(),'yes'),('jason','0755','cat /etc/passwd',NOW(),'no'),('jason','0755','useradd xxx',NOW(),'no'),('jason','0755','ps aux',NOW(),'yes');# 删除触发器drop trigger tri_after_insert_cmd;
事务
- 什么是事务
"""开启一个事务可以包含多条sql语句 这些sql语句要么同时成功要么一个都别想成功 称之为事务的原子性"""
事务的作用
"""保证了对数据操作的安全性"""eg:还钱的例子egon用银行卡给我的支付宝转账10001 将egon银行卡账户的数据减1000块2 将jason支付宝账户的数据加1000块你在操作多条数据的时候可能会出现某几条操作不成功的情况
- 事务的四大特性
"""ACIDA:原子性一个事务是一个不可分割的单位,事务中包含的诸多操作要么同时成功要么同时失败C:一致性事务必须是使数据库从一个一致性的状态变到另外一个一致性的状态一致性跟原子性是密切相关的I:隔离性一个事务的执行不能被其他事务干扰(即一个事务内部的操作及使用到的数据对并发的其他事务是隔离的,并发执行的事务之间也是互相不干扰的)D:持久性也叫"永久性"一个事务一旦提交成功执行成功 那么它对数据库中数据的修改应该是永久的接下来的其他操作或者故障不应该对其有任何的影响"""
“””模拟转账功能””” create table user( id int primary key auto_increment, name char(16), balance int ); insert into user(name,balance) values (‘jason’,1000), (‘egon’,1000), (‘tank’,1000);
1 先开启事务
start transaction;
2 多条sql语句
update user set balance=900 where name=’jason’; update user set balance=1010 where name=’egon’; update user set balance=1090 where name=’tank’;
“”” 总结 当你想让多条sql语句保持一致性 要么同时成功要么同时失败 你就应该考虑使用事务 “””
<a name="a74b3354-1"></a># 存储过程<br />存储过程就类似于python中的自定义函数<br />它的内部包含了一系列可以执行的sql语句,存储过程存放于MySQL服务端中,你可以直接通过调用存储过程触发内部sql语句的执行<br />**基本使用**```pythoncreate procedure 存储过程的名字(形参1,形参2,...)beginsql代码end# 调用call 存储过程的名字();
三种开发模型
第一种
"""应用程序:程序员写代码开发MySQL:提前编写好存储过程,供应用程序调用好处:开发效率提升了 执行效率也上去了缺点:考虑到认为元素、跨部门沟通的问题 后续的存储过程的扩展性差"""
第二种
"""应用程序:程序员写代码开发之外 设计到数据库操作也自己动手写优点:扩展性很高缺点:开发效率降低编写sql语句太过繁琐 而且后续还需要考虑sql优化的问题"""
第三种
"""应用程序:只写程序代码 不写sql语句 基于别人写好的操作MySQL的python框架直接调用操作即可 ORM框架优点:开发效率比上面两种情况都要高缺点:语句的扩展性差 可能会出现效率低下的问题"""
存储过程
第一种基本不用。一般都是第三种,出现效率问题再动手写sql
- 存储过程具体演示 ```python delimiter $$ create procedure p1( in m int, # 只进不出 m不能返回出去 in n int, out res int # 该形参可以返回出去 ) begin select tname from teacher where tid>m and tid<n; set res=666; # 将res变量修改 用来标识当前的存储过程代码确实执行了 end $$ delimiter ;
针对形参res 不能直接传数据 应该传一个变量名
定义变量
set @ret = 10;
查看变量对应的值
select @ret;
<br />在pymysql模块中如何调用存储过程呢?```pythonimport pymysqlconn = pymysql.connect(host = '127.0.0.1',port = 3306,user = 'root',passwd = '123456',db = 'day48',charset = 'utf8',autocommit = True)cursor = conn.cursor(pymysql.cursors.DictCursor)# 调用存储过程cursor.callproc('p1',(1,5,10))"""@_p1_0=1@_p1_1=5@_p1_2=10"""# print(cursor.fetchall())cursor.execute('select @_p1_2;')print(cursor.fetchall())
函数
跟存储过程是有区别的,存储过程是自定义函数,函数就类似于是内置函数
('jason','0755','ls -l /etc',NOW(),'yes')CREATE TABLE blog (id INT PRIMARY KEY auto_increment,NAME CHAR (32),sub_time datetime);INSERT INTO blog (NAME, sub_time)VALUES('第1篇','2015-03-01 11:31:21'),('第2篇','2015-03-11 16:31:21'),('第3篇','2016-07-01 10:21:31'),('第4篇','2016-07-22 09:23:21'),('第5篇','2016-07-23 10:11:11'),('第6篇','2016-07-25 11:21:31'),('第7篇','2017-03-01 15:33:21'),('第8篇','2017-03-01 17:32:21'),('第9篇','2017-03-01 18:31:21');select date_format(sub_time,'%Y-%m'),count(id) from blog group by date_format(sub_time,'%Y-%m');
流程控制
# if判断delimiter //CREATE PROCEDURE proc_if ()BEGINdeclare i int default 0;if i = 1 THENSELECT 1;ELSEIF i = 2 THENSELECT 2;ELSESELECT 7;END IF;END //delimiter ;# while循环delimiter //CREATE PROCEDURE proc_while ()BEGINDECLARE num INT ;SET num = 0 ;WHILE num < 10 DOSELECTnum ;SET num = num + 1 ;END WHILE ;
索引
ps:数据都是存在与硬盘上的,查询数据不可避免的需要进行IO操作
索引:就是一种数据结构,类似于书的目录。意味着以后在查询数据的应该先找目录再找数据,而不是一页一页的翻书,从而提升查询速度降低IO操作
索引在MySQL中也叫“键”,是存储引擎用于快速查找记录的一种数据结构
- primary key
- unique key
- index key
注意foreign key不是用来加速查询用的,不在我们的而研究范围之内
上面的三种key,前面两种除了可以增加查询速度之外各自还具有约束条件,而最后一种index key没有任何的约束条件,只是用来帮助你快速查询数据
本质
通过不断的缩小想要的数据范围筛选出最终的结果,同时将随机事件(一页一页的翻)
变成顺序事件(先找目录、找数据)
也就是说有了索引机制,我们可以总是用一种固定的方式查找数据
一张表中可以有多个索引(多个目录)
索引虽然能够帮助你加快查询速度但是也有缺点
"""1 当表中有大量数据存在的前提下 创建索引速度会很慢2 在索引创建完毕之后 对表的查询性能会大幅度的提升 但是写的性能也会大幅度的降低"""索引不要随意的创建!!!
b+树
"""只有叶子节点存放的是真实的数据 其他节点存放的是虚拟数据 仅仅是用来指路的树的层级越高查询数据所需要经历的步骤就越多(树有几层查询数据就需要几步)一个磁盘块存储是有限制的为什么建议你将id字段作为索引占得空间少 一个磁盘块能够存储的数据多那么久降低了树的高度 从而减少查询次数"""
聚集索引(primary key)
"""聚集索引指的就是主键Innodb 只有两个文件 直接将主键存放在了idb表中MyIsam 三个文件 单独将索引存在一个文件"""
辅助索引(unique,index)
查询数据的时候不可能一直使用到主键,也有可能会用到name,password等其他字段
那么这个时候你是没有办法利用聚集索引。这个时候你就可以根据情况给其他字段设置辅助索引(也是一个b+树)
"""叶子节点存放的是数据对应的主键值先按照辅助索引拿到数据的主键值之后还是需要去主键的聚集索引里面查询数据"""
覆盖索引
在辅助索引的叶子节点就已经拿到了需要的数据
# 给name设置辅助索引select name from user where name='jason';# 非覆盖索引select age from user where name='jason';
测试索引是否有效的代码
感兴趣就自己试一试 不感兴趣直接忽略
**准备**```mysql#1. 准备表create table s1(id int,name varchar(20),gender char(6),email varchar(50));#2. 创建存储过程,实现批量插入记录delimiter $$ #声明存储过程的结束符号为$$create procedure auto_insert1()BEGINdeclare i int default 1;while(i<3000000)doinsert into s1 values(i,'jason','male',concat('jason',i,'@oldboy'));set i=i+1;end while;END$$ #$$结束delimiter ; #重新声明分号为结束符号#3. 查看存储过程show create procedure auto_insert1\G#4. 调用存储过程call auto_insert1();
# 表没有任何索引的情况下select * from s1 where id=30000;# 避免打印带来的时间损耗select count(id) from s1 where id = 30000;select count(id) from s1 where id = 1;# 给id做一个主键alter table s1 add primary key(id); # 速度很慢select count(id) from s1 where id = 1; # 速度相较于未建索引之前两者差着数量级select count(id) from s1 where name = 'jason' # 速度仍然很慢"""范围问题"""# 并不是加了索引,以后查询的时候按照这个字段速度就一定快select count(id) from s1 where id > 1; # 速度相较于id = 1慢了很多select count(id) from s1 where id >1 and id < 3;select count(id) from s1 where id > 1 and id < 10000;select count(id) from s1 where id != 3;alter table s1 drop primary key; # 删除主键 单独再来研究name字段select count(id) from s1 where name = 'jason'; # 又慢了create index idx_name on s1(name); # 给s1表的name字段创建索引select count(id) from s1 where name = 'jason' # 仍然很慢!!!"""再来看b+树的原理,数据需要区分度比较高,而我们这张表全是jason,根本无法区分那这个树其实就建成了“一根棍子”"""select count(id) from s1 where name = 'xxx';# 这个会很快,我就是一根棍,第一个不匹配直接不需要再往下走了select count(id) from s1 where name like 'xxx';select count(id) from s1 where name like 'xxx%';select count(id) from s1 where name like '%xxx'; # 慢 最左匹配特性# 区分度低的字段不能建索引drop index idx_name on s1;# 给id字段建普通的索引create index idx_id on s1(id);select count(id) from s1 where id = 3; # 快了select count(id) from s1 where id*12 = 3; # 慢了 索引的字段一定不要参与计算drop index idx_id on s1;select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx';# 针对上面这种连续多个and的操作,mysql会从左到右先找区分度比较高的索引字段,先将整体范围降下来再去比较其他条件create index idx_name on s1(name);select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx'; # 并没有加速drop index idx_name on s1;# 给name,gender这种区分度不高的字段加上索引并不难加快查询速度create index idx_id on s1(id);select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx'; # 快了 先通过id已经讲数据快速锁定成了一条了select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 慢了 基于id查出来的数据仍然很多,然后还要去比较其他字段drop index idx_id on s1create index idx_email on s1(email);select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 快 通过email字段一剑封喉
联合索引
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx';# 如果上述四个字段区分度都很高,那给谁建都能加速查询# 给email加然而不用email字段select count(id) from s1 where name='jason' and gender = 'male' and id > 3;# 给name加然而不用name字段select count(id) from s1 where gender = 'male' and id > 3;# 给gender加然而不用gender字段select count(id) from s1 where id > 3;# 带来的问题是所有的字段都建了索引然而都没有用到,还需要花费四次建立的时间create index idx_all on s1(email,name,gender,id); # 最左匹配原则,区分度高的往左放select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 速度变快
总结:上面这些操作,你感兴趣可以敲一敲,不感兴趣你就可以不用敲了,权当看个乐呵。理论掌握了就行了
慢查询日志
设定一个时间检测所有超出该时间的sql语句,然后针对性的进行优化!
