数据库基础
1.什么是数据库
用来存储数据。数据库可在硬盘及内存中存储数据html
数据库与文件存储数据的区别java
数据库本质也是经过文件来存储数据,数据库的概念就是系统的管理存储数据的文件
数据库介绍 本质就是存储数据的C/S架构的socket套接字软件 数据库服务器端:存放数据的主机集群 数据库客户端:能够链接数据库的任意客户端 数据库管理员:DBA
2.数据库基本概念
库:多个表构建一个数据库
表:多条数据构建一张表
记录:存放一条条数据,本质就是文件中的一条条数据记录
字段,字段,字段。。。==》一条数据 python
3.数据库分类
关系型数据库(MySQL 、Oracle 、 SQL server 、access、db2)
1.有表的概念
2.以表中一条条的纪录存储数据
非关系型数据库 (mongodb、redis、memcache)
1.有表的概念
2.以key-value键值对方式存储数据
mysql
4.数据库安装、启动与链接
安装 server端h和client端
如何启动server? 若是经过client链接serverweb
前提:配置环境变量
一、前往数据库安装路径,bin文件夹下,cmd执行MySQL启动服务器端
二、前往数据库安装路径,bin文件夹下,cmd执行MySQL启动客户端连接服务器端
面试
将MySQL服务器添加到系统服务,在系统服务中启动MySQL,命令:mysqld --install
进入系统 win+r=> services.msc=>找到MySQL服务手动启动或关闭
链接数据库:mysql -hlocalhost -P3306 -uroot -p
经过最高权限进入数据库,要采用root用户进入,连入本地数据库: mysql -uroot -p正则表达式
查看MySQL版本:select version();
查看当前用户:select user();
查看MySQL下(当前用户能够操做的)数据库:show databases;redis
5.密码操做:
修改密码: mysqladmin -uroot -p旧密码 password“新密码” 忘记旧密码 1.绕过受权表启动服务(安全认证的服务中止):mysqld --skip-grant-tables; 2.以任意密码登录root用户:mysql -uroot -p任意 3.更新密码: updata mysql.user set password=passwore("新密码")where user=root and host="localhost" 4.刷新权限:flush privileges;
6.统一字符编码
查看数据库配置信息 1.在mysql安装目录下:建立my.ini (my.cnf) (命令:type nul>文件名.文件后缀) 2.设置配置信息并保存 [mysqld] #port=7777 注释 character-set-server=utf8 collation-server=utf8_general_ci [client] default-character-set=utf8 3.重启服务
库的基本操做
# 前提: 链接上数据库 1.增,建立数据库 采用默认编码集: create database db1; # db1为数据库名 自定义编码集: create database db1 charset="gbk"; 2.查,查看数据库所有信息 纵观全部数据库: show databases; 详细信息: show create database db1; 3.改,修改数据库编码集 alter database db1 charset="utf8"; 4.删, 移除数据库 drop database db1;
表的基本操做
#前提: 在具体的某个库下建立表 进入指定数据库: use db1 肯定当前使用的数据库: select database(); 1.增, 建立表(字段1 类型, ..., 字段n 类型) create table t1(name char, age int); 2.查,查看表信息 纵观全部数据库: show tables; 详细信息: show create table t1; 表字段结构信息: desc t1; 3.改 修改字段属性: alter table t1 modify name char(20); 修改字段名: alter table t1 change name usr char(16); 修改表名: alter table t1 rename t2; 4.删, 移除表 drop table t1;
字段的基本操做
# 前提: 知道具体操做的是哪张表 1.增, 添加字段 insert into t1 (usr, age) values ("aa", 18),("bb", 8); 2.查 select * from t1; 3.改 update t1 set age=28 where usr="aa"; 4.删 delete from t1 where age>8;
引擎与数据类型
前提: 引擎是建表是规定, 提供给表使用的, 不是数据库 mysql> show engines; # 展现全部引擎 # 重点: # innodb(默认): 支持事务, 行级锁, 外键 # myisam: 查询效率要优于innodb, 当不须要支持事务, 行级锁, 外键, 能够经过设置myisam来优化数据库 mysql> use db1; mysql> create table t1(id int)engine=innodb; mysql> create table t2(id int)engine=myisam; mysql> create table t3(id int)engine=blackhole; mysql> 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); select * from t1; ...
建立表完整语法
''' create table 表名( 字段名1 类型[(宽度) 约束条件], 字段名2 类型[(宽度) 约束条件], 字段名3 类型[(宽度) 约束条件] )engine=innodb charset=utf8; ''' # []可选参数 # create table db1.t1(name char(3) not null); # 数据插入时,name不能为空(null), 且最长只能存放三个字符 # 总结: 宽度和约束条件为可选参数, 用来限制存放数据的规则
数据库的模式
# sql_mode: 反映数据库的全局变量 # 数据库模式限制的是客户端对服务器操做数据的方式(是否严格) # 两种模式 no_engine_substitution:非安全性,默认 strict_trans_tables:安全模式 # 查看当前数据库模式: show variables like "%sql_mode%"; # %匹配0~n个任意字符 => 模糊查询 # 设置为安全模式 set global sql_mode="strict_trans_tables"; # 重启链接(客户端) quit # 应用 create table t1(name char(2)); insert into t1 values ("ab") # 正常 insert into t1 values ("zero") # 错误 Data too long for column 'name' at row 1
数据类型
# mysql数据库支持存放哪些数据 # 整型* | 浮点型 | 字符型* | 时间类型 | 枚举类型 | 集合类型
整型
'''类型 tinyint:1字节 -128~127 * smallint:2字节 mediumint:3字节 int:4字节 -2147483648~2147483647 * bigint:8字节 ''' '''约束 * unsigned:无符号 zerofill:0填充 ''' # 不一样类型所占字节数不同, 决定所占空间及存放数据的大小限制 # eg: create table t8(x tinyint); insert into t8 values(200); # 非安全模式存入,值只能到最大值127 select x from t8; '''宽度 1.不能决定整型存放数据的宽度, 超过宽度能够存放, 最终由数据类型所占字节决定 2.若是没有超过宽度,且有zerofill限制, 会用0填充前置位的不足位 3.没有必要规定整型的宽度, 默认设置的宽度就为该整型能存放数据的最大宽度 * ''' # eg:1 create table t9(x int(5)); insert into t9 values(123456); select (x) from t9; # 结果: 123456 insert into t9 values(2147483648); select (x) from t9; # 结果: 2147483647 insert into t9 values(10); select (x) from t9; # 结果: 10 # eg:2 create table t10(x int(5) unsigned zerofill); # 区域0~4294967295 insert into t10 values(10); select x from t10; # 结果: 00010 insert into t10 values(12345678900); select x from t10; # 结果: 4294967295
浮点型
'''类型 float:4字节,3.4E–38~3.4E+38 * double:8字节,1.7E–308~1.7E+308 decimal:M,D大值基础上+2 ''' '''宽度: 限制存储宽度 (M, D) => M为总位数,D为小数位 float(255, 30):精度最低,最经常使用 double(255, 30):精度高,占位多 decimal(65, 30):字符串存,全精度 ''' # eg:1 create table t11 (age float(256, 30)); # Display width out of range for column 'age' (max = 255) create table t11 (age float(255, 31)); # Too big scale 31 specified for column 'age'. Maximum is 30. # eg:2 create table t12 (x float(255, 30)); create table t13 (x double(255, 30)); create table t14 (x decimal(65, 30)); insert into t12 values(1.11111111111111111111); insert into t13 values(1.11111111111111111111); insert into t14 values(1.11111111111111111111); select * from t12; # 1.111111164093017600000000000000 => 小数据,精度要求不高, 均采用float来存储 * select * from t13; # 1.111111111111111200000000000000 select * from t14; # 1.111111111111111111110000000000 alter table t14 modify x decimal(10, 5); # 1.11111 => 限制了数据的存储宽度
字符型
'''类型 char:定长 varchar:不定长 ''' '''宽度 限制存储宽度 char(4):以4个字符存储定长存储数据 varchar(4):数据长度决定字符长度,为可变长度存储数据 ''' # eg: create table t15 (x char(4), y varchar(4)); insert into t15 values("zero", 'owen'); # '' | "" 都可以表示字符 select x,y from t15; # 正常 insert into t15 values("yanghuhu", 'lxxVSegon'); # 非安全模式数据丢失,能够存放, 安全模式报错 select x,y from t15; # 能够正常显示丢失后(不完整)的数据 insert into t15 values('a', 'b'); # 验证数据所在字符长度 # 前提: 安全模式下以空白填充字符 set global sql_mode="strict_trans_tables,PAD_CHAR_TO_FULL_LENGTH"; # 重启链接 select char_length(x), char_length(y) from t15; # a占4 b占1 '''重点: 存储数据的方式 ** => 数据库优化 char: 必定按规定的宽度存放数据, 以规定宽度读取数据, 一般更占空间 varchar: 首先根据数据长度计算所需宽度, 并在数据开始以数据头方式将宽度信息保存起来, 是一个计算耗时过程, 取先读取宽度信息,以宽度信息为依准读取数据, 一般节省空间 ''' 8: zero egon lxx yanghuhu 8: 4zero4egon3lxx8yanghuhu 注: varchar的数据头占1~2字节 规定char|varchar宽度均为4,用来存放4个字符的数据, char存取更高效,char占4字符,varchar占5字符,char更省空间 总结: 数据长度相近的数据提倡用char来存放数据, 数据须要高速存取,以空间换时间, 采用char
时间类型
'''类型 year:yyyy(1901/2155) date:yyyy-MM-dd(1000-01-01/9999-12-31) time:HH:mm:ss datetime:yyyy-MM-dd HH:mm:ss(1000-01-01 00:00:00/9999-12-31 23:59:59) timestamp:yyyy-MM-dd HH:mm:ss(1970-01-01 00:00:00/2038-01-19 ??) ''' # eg: 1 create table t16(my_year year, my_date date, my_time time); insert into t16 values(); # 三个时间类型的默认值均是null insert into t16 values(2156, null, null); # 在时间范围外,不容许插入该数据 insert into t16 values(1, '2000-01-01 12:00:00', null); # 2001 2000-01-01 null insert into t16 values(2019, '2019-01-08', "15-19-30"); # time报格式错误 => 按照时间规定格式存放数据 alter table t16 change my_year myYear year(2); # 时间的宽度修改后仍是采用默认宽度 => 不须要关系宽度 # eg:2 create table t17(my_datetime datetime, my_timestamp timestamp); insert into t17 values(null, null); # 能够为空, 不能为null,赋值null采用默认值current_timestamp insert into t17 values('4000-01-01 12:00:00', '2000-01-01 12:00:00'); # 在各自范围内能够插入对应格式的时间数据 # datetime VS timestamp datetime:时间范围,不依赖当前时区,8字节,能够为null timestamp:时间范围,依赖当前时区,4字节,有默认值CURRENT_TIMESTAMP
枚举与集合
'''类型 enum:单选 set:多选 ''' create table t19( sex enum('male','female','wasai') not null default 'wasai', # 枚举 hobbies set('play','read','music') # 集合 ); insert into t19 values (null, null); # sex不能设置null insert into t19 values (); # wasai null insert into t19 (hobbies) values ('play,read'), ('music,play'); # sex采用默认值, 对hobbies字段添加两条记录 insert into t19 (sex,hobbies) values ('male,female', 'play'); # sex字段只能单选
约束条件
""" primary key:主键,惟一标识,表都会拥有,不设置为默认找第一个 不空,惟一 字段,未标识则建立隐藏字段 foreing key:外键 unique key:惟一性数据, 该条字段的值须要保证惟一,不能重复 auto_increment:自增,只能加给key字段辅助修饰 not null:不为空 default:默认值 unsigned:无符号 zerofill:0填充 """ 注: 1.键是用来说的io提供存取效率 2.联合惟一 create table web ( ip char(16), port int, unique(ip,port) ); 3.联合主键 create table web ( ip char(16), port int, primary key(ip,port) ); # eg:1 # 单列惟一 create table t20 ( id int unique ); # 联合惟一 create table web ( ip char(16), port int, unique(ip,port) ); # 若是联合两个字段,两个字段全相同才相同,不然为不一样 insert into web values ('10.10.10.10', 3306), ('10.10.10.10', 3306); # 注: # 1.表默认都有主键, 且只能拥有一个主键字段(单列主键 | 联合主键) # 2.没有设置主键的表, 数据库系统会自上而下将第一个规定为unique not null字段自动提高为primary key主键 # 3.若是整个表都没有unique not null字段且没有primary key字段, 系统会默认建立一个隐藏字段做为主键 # 4.一般必须手动指定表的主键, 通常用id字段, 且id字段通常类型为int, 由于int类型能够auto_increment # eg:2 create table t21(id int auto_increment); # 自增约束必须添加给key的字段 # eg:3 create table t21(id int primary key auto_increment); # 自增要结合key,不赋值插入,数据会自动自增, 且自增的结果一直被记录保留 # eg:4 # 联合主键 create table t22( ip char(16), port int, primary key(ip,port) ); # 若是联合两个字段,两个字段全相同才相同,不然为不一样 insert into web values ('10.10.10.10', 3306), ('10.10.10.10', 3306);
表关系
1.表的详细操做
1.修改表名 alter table 旧表名 rename 新表名; 2.修改表的引擎与字符编码 alter table 表名 engine="引擎名" charset="编码名"; 3.复制表 * # 结构 create table 新表名 like 旧表名;#只复制表的结构,包括约束 eg: create table nt like tt; # 将tt的表结构复制到新表nt中, 约束条件一并复制 # 结构+数据 create table 新表名 select * from 旧表名; 注: 会复制表结构+数据, 但不会复制约束条件 eg: create table nt1 select * from tt where 1=2; # 将tt的表结构复制到新表nt1中, 键(相关)的约束条件不会复制 4.清空表 truncate 表名; 注:表被重置,自增字段重置
表中字段的详细操做
create table t2( id int primary key auto_increment, x int, y int ); insert into t2(x, y) values(10, 20), (100, 200), (1000, 2000); 1.修改字段信息 alter table 表名 modify 字段名 类型[(宽度) 约束]; alter table t2 modify x bigint default 0; # 模式不一样, 涉及精度问题 2.修改字段名及信息 alter table 表名 change 旧字段名 新字段名 类型[(宽度) 约束]; alter table t2 change y c char(10) not null; # 模式不一样, 涉及类型转换问题 3.添加字段名 # 末尾添加 alter table 表名 add 字段名 类型[(宽度) 约束], ..., add 字段名 类型[(宽度) 约束]; alter table t2 add age int, add gender enum("male", "female", "wasai") default "wasai"; # 首尾添加 alter table 表名 add 字段名 类型[(宽度) 约束] first; # 指定位添加:指定字段后 alter table 表名 add 字段名 类型[(宽度) 约束] after 旧字段名; alter table t2 add y int after x; 4.删除字段名 alter table 表名 drop 字段名; alter table t2 drop y; 5.移动字段名 alter table t1 modify 字段名 类型[(宽度) 约束] first; alter table t1 modify 字段名 类型[(宽度) 约束] after 指定字段名; ??? 若是想删除自增约束,如何操做? auto_increment 需跟在key后,好比自增的是id,表t1: alter table t1 change id id int;
特殊表 (mysql.user) => 用户管理
# 操做前提:登陆root用户 mysql -uroot -p 1.重要字段 Host | User | Password 2.新建用户 create user 用户名@主机名 identified by '密码'; # 正确 create user zero@localhost identified by 'zero'; 注:insert into mysql.user(Host,User,Password) values("主机名","用户名",password("密码")); # 错误 3.设置用户权限* grant 权限们 on 数据库名.表名 to 用户名@主机名 [with grant option]; grant create on db1.* to zero@localhost with grant option; # 权限有select,delete,update,insert,drop..., all表明全部权限 # 数据库名,表名能够用*替换,表明全部 # 设置权限时若是没有当前用户,会自动建立用户,提倡使用 *重点*: grant all on db1.* to owen@localhost identified by 'owen'; # (建立用户)设置权限 4.撤销权限 revoke 权限名 on 数据库名.表名 from 用户名@主机名; revoke delete on db1.* from owen@localhost; 5.修改密码 set password for 用户名@主机名 = password('新密码'); set password for owen@localhost = password('123'); 6.删除用户 drop user 用户名@主机名; '''
表关系
社会中存储须要能够构建成表的数据, 它们造成的表,每每之间存储某种或某些社会关系, mysql数据库创建表结构就是社会中产生的各类数据, 分门别类管理 但mysql创建的(代码层次的)表之间, 一样须要处理表与表之间的关系 造成了 多对一 | 多对多 | 一对一 三种关系 ''' 外键直接影响数据库效率,但会提升数据的完整性(安全),通常首先效率,由于安全能够经过其余方式来处理 '''
多对一
案例:员工employees表 | 部门department表 建表规则: 先创建主表,再创建从表,在从表中设置主表的惟一字段(一般为主键)做为外键 建表语法: create table 主表( id int primary key auto_increment, ... ); create table dep( id int primary key auto_increment, name varchar(16), work varchar(16) ); create table 从表( id int primary key auto_increment, ... 主表_id int, # 只是在从表中起了一个名字, 该名字和主表主键对应,全部起了个见名知义的名字 foreign key(主表_id) references 主表(惟一字段名id) on update cascade on delete cascade ); create table emp( id int primary key auto_increment, name varchar(16), salary float, dep_id int, foreign key(dep_id) references dep(id) on update cascade # 设置级联 on delete cascade ); 插入记录规则: 先插入主表数据,再插入从表数据 insert into dep values(1, '市场部', '销售'), (2, '教学部', '授课'); insert into emp(name, salary, dep_id) values('egon', 3.0, 2),('yanghuhu', 2.0, 2),('sanjiang', 10.0, 1),('owen', 88888.0, 2),('liujie', 8.0, 1); 更新删除数据: 两表间相互影响,先从依赖数据入手,再进行更新删除操做 eg:1 删除主表dep中一个部门 delete from dep where id=1; => 从表emp中属于该部门的员工都被删除了 更新从表emp中一个员工的部门 update emp set dep_id=3 where name='egon'; <= 部门必须存在 insert into dep values(3, '管理部', '吃饭睡觉打豆豆, 明确团队方针');
多对多
案例:做者author表 | 书book表 建表规则: 新建第三张表,经过两个外键造成多对多关系 建表语法: create table 表1( id int primary key auto_increment, ... ); create table book( id int primary key auto_increment, name varchar(16), price int ); create table 表2( id int primary key auto_increment, ... ); create table author( id int primary key auto_increment, name varchar(16) ); create table 关系表( id int primary key auto_increment, 表1_id int, 表2_id int, foreign key(表1_id) references 表1(id) on update cascade on delete cascade, foreign key(表2_id) references 表2(id) on update cascade on delete cascade ); create table book_author( id int primary key auto_increment, 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 );
一对一
为了提升效率,强行将海量数据中相关信息抽离成一个对应表来与依赖表造成一对一算法
案例:丈夫husband表 | 妻子wife表 建表规则: 未存放外键的表被依赖,称之为左表;存放外键的表示依赖表,称之为右表;先操做左边再操做右表 建表语法: create table 左表( id int primary key auto_increment, ... ); create table husband( id int primary key auto_increment, name varchar(16) ); create table 右表( id int primary key auto_increment, ... 左表_id int unique, # 一对一的外键须要惟一性 foreign key(左表_id) references 左表(id) on update cascade on delete cascade ); create table wife( id int primary key auto_increment, name varchar(16), husband_id int unique, # 一对一的外键须要惟一性 foreign key(husband_id) references husband(id) on update cascade on delete cascade );
练习


1.建立一个stu表,字段有:自增主键id,不为空姓名,默认值性别(枚举类型),无限制身高 mysql> create table stu(id int primary key auto_increment, -> name varchar(20) not null, -> gender enum('male','female','wasai') not null default 'wasai', ->height float); 2.为stu表依次插入如下三条数据 i)插入一条包含id,name,gender,height四个信息的数据 insert into stu values(1,"xiaoming","male",170); ii)插入一条name,gender,height三个信息的数据 insert into stu(name,gender,height) values("ming","male",170); iii)插入一条只有name信息的数据 insert into stu(name) values("lal"); 3.实现新表new_stu对已有表stu的字段、约束及数据的拷贝 create table new_stu like stu; insert into new_stu select * from stu; # 拷贝 4.建立一张有姓名、年龄的teacher表,在最后添加工资字段,在姓名后添加id主键字段 create table teacher(name varchar(20),age int); alter table teacher add salary float; alter table teacher add id int primary key after name; 5.思考:将5中id字段移到到表的最前方,造成最终字段顺序为id、姓名、年龄、工资 alter table teacher drop id; alter table teacher add id int primary key first; alter table teacher modify id int first; 6.完成 公民表 与 国家表 的 多对一 表关系的建立 mysql> create table country(id int primary key, -> name char); mysql> create table people(id int primary key, -> name char, -> age int, -> country_id int, -> foreign key(country_id) references country(id) -> on update cascade -> on delete cascade -> ); 7.完成 学生表 与 课程表 的 多对多 表关系的建立 create table stu1( id int primary key auto_increment, name varchar(16), age int ); create table course1( id int primary key auto_increment, name varchar(16) ); create table s_c( id int primary key auto_increment, stu1_id int, course1_id int, foreign key(stu1_id) references stu1(id) on update cascade on delete cascade, foreign key(course1_id) references course1(id) on update cascade on delete cascade ); 8.完成 做者表 与 做者简介表 的 一对一 表关系的建立(思考为何要这样设计) create table author( id int primary key auto_increment, name varchar(16) ); create table jj( id int primary key auto_increment, name varchar(16), author_id int unique, foreign key(author_id ) references author(id) on update cascade on delete cascade );
查询语句
sql_mode配置
sql_mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
增语法
1.全部数据按顺序插入 insert [into] 表名 values (值1, ..., 值n)[, ..., (值1, ..., 值n)]; 2.指定字段匹配插入,能够任意顺序 insert [into] 表名(字段2, 字段1, ..., 字段n) values (值2, 值1, ..., 值n)[, ..., (值2, 值1, ..., 值n)]; 3.插入查询结果 insert [into] 表1(字段1, ..., 字段n) select 字段1, ..., 字段n from 表2 [条件];# 表2与表1字段相同或要插入至表1的字段与表1相同时,表1后字段能够*替 # eg:1 create table t1( id int auto_increment, x int, y int, primary key(id) ); insert t1 values(1,2,3),(2,3,4); # 按顺序插入 insert into t1(y,x) values (5,4); # 按规定字段顺序 create table nt1 like t1;复制表即完整结构 insert into nt1 select * from t1; # 建立新表并按照字段顺序指定插入复制数据
删语法
1.会记录自增信息,操做会被日志记录,效率低 delete from [库名].表名 [条件]; delete from t1; # 没有条件的状况下是清空全部数据。但会记录自增的约束 insert into t1(x,y)values(6,66) 2.清空表,会重置自增信息 truncate table 表名; truncate table nt1; insert into nt1(x, y) values(6, 66);
改语法
update 表名 set 字段1=值1[, ..., 字段n=值n] [条件] updata tt1 set x=666;# 无条件,全改 update tt1 set x=777, z=555 where z<888; # 只修改知足条件的行 没有知足条件的不改变
查询语法
select [distinct] 字段1 [as 别名], ..., 字段n [as 别名] from [库名.]表名 [ where 约束条件 group by 分组依据 =》查询结果只能为聚合结果或分组字段 having 筛选,过滤条件=》对聚合结果的筛选 order by 排序的字段=》asc|desc limit 限制显示的条数 =>n| i,n ]; 注: 1.查表中全部字段用*表示 2.条件的书写规则严格按照语法顺序书写,能够缺省,但不能够错序 3.约束条件的流程:from -> where -> group by -> having -> distinct -> order by -> limit 4.字段能够起别名 5.字段能够直接作运算 select age + 1 'new_age' from emp; 6.分组后的条件都可以使用聚合函数 """ 3. def from(): return "查询的文件" def where(file): return "条件筛选后的结果" def group_by(res): return "分组后的结果" def having(res): return "再次过滤后的结果" def distinct(res): return "去重后的结果" def order_by(res): return "排序后的结果" def limit(res): return "限制条数后的结果" def select(from=from, where=null, ..., limit=null): file = from() res = where(file) if where else file res = group_by(res) if group_by else res ... res = limit(res) if limit else res return res select(where=where, group_by=group_by) """
单表依赖数据
CREATE TABLE 'emp' ( `id` int(0) NOT NULL AUTO_INCREMENT, `name` varchar(10) NOT NULL, `gender` enum('男','女','未知') NULL DEFAULT '未知', `age` int(0) NULL DEFAULT 0, `salary` float NULL DEFAULT 0, `area` varchar(20) NULL DEFAULT '中国', `port` varchar(20) DEFAULT '未知', `dep` varchar(20), PRIMARY KEY (`id`) ); INSERT INTO `emp` VALUES (1, 'yangsir', '男', 42, 10.5, '上海', '浦东', '教职部'), (2, 'engo', '男', 38, 9.4, '山东', '济南', '教学部'), (3, 'jerry', '女', 30, 3.0, '江苏', '张家港', '教学部'), (4, 'tank', '女', 28, 2.4, '广州', '广东', '教学部'), (5, 'jiboy', '男', 28, 2.4, '江苏', '苏州', '教学部'), (6, 'zero', '男', 28, 8.8, '中国', '黄浦', '咨询部'), (7, 'owen', '男', 28, 8.8, '安徽', '宣城', '教学部'), (9, 'ying', '女', 36, 1.2, '安徽', '芜湖', '咨询部'), (10, 'kevin', '男', 36, 5.8, '山东', '济南', '教学部'), (11, 'monkey', '女', 28, 1.2, '山东', '青岛', '教职部'), (12, 'san', '男', 30, 9.0, '上海', '浦东', '咨询部'), (13, 'san1', '男', 30, 6.0, '上海', '浦东', '咨询部'), (14, 'san2', '男', 30, 6.0, '上海', '浦西', '教学部');
经常使用内部函数
concat(字段1,...,字段n):完成字段的拼接 concat_ws(x, 字段1,...,字段n):完成字段的拼接,x为链接符 lower():小写 upper():大写 ceil():向上取整 floor():向下取整 round():四舍五入
简单查询
去重前提:所查全部字段的综合结果彻底一致/相同,才认为是重复的,只保留重复中的一行数据 select distinct * from emp; # 去重,去的是所查结果彻底相同的 select contat(area,'-',port)[as 'xx'] from emp; # 上海-浦东 属于起的别名xx 列 select contat_ws("-",name,area,port)[as 'xx'] from emp; #以"-"字符拼接后面的全部字段 select upper(name) 'name', gender, age from emp; # 能够指定多个字段 select name, ceil(salary), floor(salary), round(salary) from emp where name='kevin'; # 数学函数
where 条件
1.比较运算符 = | < | > | <= | >= | != select * from emp where area!="上海"; 2.区间运算符 between 10 and 20:10~20 in(10, 20, 30):10或20或30 select * from emp where id between 3 and5; # [3,5]闭合的区间 select * from emp where id in (2,4,6,8);# 分离的区间 2,4,6,8,10,12,14都会被显示 3.逻辑运算符 and | or | not select * from emp where area="山东" and port="济南"; 4.类似运算符 like '_owen%':模糊匹配字符串owen,_表示一个字符,%表示任意字符 # 匹配的字段为en,想获得的结果为owen select * from emp where name like '__en%'; # 在en前能够出现2个任意字符, 以后能够出现0或多个任意字符 需求: 查找姓名有数字的员工信息
正则匹配
对like的扩展,like完成模糊匹配,但功能局限,能够模糊个数,但不能模糊类型。正则能够完成类型及个数的模糊匹配 语法:字段 regexp '正则表达式' 注:只支持部分正则语法 select * from emp where name regexp '.*[0-9]+.*';
group by 分组
注意:分了组,一个组就是一个总体, 你不能直接拿到组里的具体的某一条信息(安全模式下会报错), 可是你能够用好比max的方法取出每一个组信息里的最大值sql
分组:根据字段相同值造成不一样的类别,不明确分组其实整个表就为一个默认大组 缘由:把以值共性获得的类别做为考虑单位,再也不关系单条记录,并且一组记录 结果:只能考虑组内多条数据的聚合 结果 (聚合函数结果),分组的字段一样是聚合结果,如:组内的最大最小值 sql_mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION 聚合函数: max():最大值 min():最小值 avg():平均值 sum():和 count():记数 group_concat():组内字段拼接,用来查看组内其余字段 eg:1 每一个部门的平均薪资 select dep, avg(salary) '平均薪资' from emp group by dep; eg:2 每一个部门都有哪些人 select dep, group_concat(name) from emp group by dep; 需求: 各性别中附属于教学部的最高薪资 select max(salary) '最高薪资', gender from emp where dep='教学部' group by gender; 思考: 想知道需求中员工的姓名 => 子查询 思考: 想知道需求中员工的姓名 => 子查询 上方结果: 男的最高薪资对应的人名, 女的最高薪资对应的人名 # select group_concat(name), max(salary) '最高薪资', gender from emp where dep='教学部' group by gender; 错误 select max(salary)"最高薪资",gender,name from emp where dep = "教学部" group by gender; ''' ''' 1. 14条数据部门有3个, 而且每一个部分有多条记录, 能够做为分组依据, 同理, 性别也能够 # select * from emp group by dep; # 非分组安全模式下, 能够查询非聚合结果, 显示的是第一条记录, 没有意义, 分组安全模式下不能查询非聚合结果的字段 select dep from emp group by dep; 2. 若是就像以姓名进行分组, 能够, 但没多大意义, 缘由name值基本上都不相同, 以组考虑会致使组内大多只要一条记录(自成一组), 组的利用就不是很强烈, 此类分组是无心义的 select name from emp group by name; # 能够分组, 意义不大 考虑的三个问题: 以什么分组(相同数据较多的字段) 分组后的考虑单位(组并不是组内的每一条记录) 能够查询的结果(当前分组的字段及聚合函数造成的聚合结果)
解决分组中思考题的过程
# res = select max(salary) '最高薪资', gender from emp where dep='教学部' group by gender; # select name from emp where (salary 跟 res做比较) # 一个查询依赖于另外一个查询的结果 => 一个查询的结果做为另一个查询的条件 => 子查询
子查询
子查询:将一条查询结果做为另一条查询的条件 语法:一条select语句用()包裹获得的结果做为另外一条select语句的条件 # 伪sql: select * from emp where salary =|in (select salary from emp where 条件) 单行子查询: 子查询语句的结果为一行数据,能够结合 = | < | > | <= | >= | != 运算符来完成父查询 select salary from emp where salary > 10; # => 做为子查询 # 查询姓名,性别.地区,基于薪资大于10的结果的查询结果 eg: 1 select name, gender, area from emp where salary = (select salary from emp where salary > 10); 多行子查询: 子查询语句的结果为多行数据,能够结合 in | all | any 运算符来完成父查询 in:任意单一值,只能考虑子查询中的一个结果 all:所有值,将子查询结果做为一个总体考虑 any:任意多个值:子查询的每个结果均可以做为参考依据 eg: 2 # 子查询的结果 (9.4, 3) select name from emp where salary in (select max(salary) '最高薪资' from emp where dep='教学部' group by gender); # 遍历14条数据, 14条数据的salary在(9.4, 3)区域中,就能够完成匹配, 结果为两条(9.4和3那两条) select * from emp where salary < all(select max(salary) '最高薪资' from emp where dep='教学部' group by gender); # 遍历14条数据, salary要小于(9.4, 3)中的每个, 反映就是小于3, 结果为薪资1.2,2.4的那四条数据 select * from emp where salary > any(select max(salary) '最高薪资' from emp where dep='教学部' group by gender); # 遍历14条数据, salary大于9.4或大于3的数据均知足条件, 结果就是刨除小于等于3的那几条数据
having 筛选
why:完成在分组以后的筛选 注意:having条件是实现聚合结果层面上的筛选 => 拿聚会结果完成判断 需求: 1.各部门的平均薪资 select dep, avg(salary) '平均薪资' from emp group by dep; 2.平均薪资大于6w的部门(部门与部门的平均薪资) 解决: 以dep进行分组, 以avg(salary)做为判断条件(筛选) select dep, avg(salary) '平均薪资' from emp group by dep having avg(salary) > 6; # 总结: having经过聚合函数结果完成筛选 select max(salary) from emp having max(salary) > 9.4; # 虽然没有明确书写group by, 但在having中使用了聚合函数,因此该查询就将整个表当作一个默认大表来考虑,因此查询的字段只能为聚合函数的结果
order by 排序
why:完成排序 注意:可使用聚合函数,哪怕没有明确group by 升序 | 降序:asc | desc eg:order by age desc => 按照年龄降序 select * from emp order by age desc; 需求: 将部门按照工资降序方式排序 select dep, avg(salary) from emp group by dep order by avg(salary) desc;
limit 限制
why:限制最终结果的显示数据行数 注意:limit只与数字结合使用 应用: limit 1:只能显示一行数据 limit 6,5:从第6+1行开始显示5条数据(索引从0开始) select * from emp limit 1; select * from emp limit 6,5; 需求: 得到薪资最高的人的一条信息 select * from emp order by salary desc limit 1;
练习


1、查询教学部山东人的平均薪资 select avg(salary)"平均薪资",area from emp where dep="教学部" group by area having area="山东"; select avg(salary)"平均薪资",dep from emp where area="山东" group by dep having dep="教学部"; select avg(salary)"平均薪资",area,dep from emp where dep="教学部" and area="山东"; 2、查询姓名中包含英文字母n而且居住在上海的人的全部信息 select area,name from emp where area="上海" and name regexp ".*n.*"; 3、查询姓名中包含英文字母n但不包含数字的人的全部信息 select * from emp where name regexp ".*n.*"and name not regexp ".*[0-9]+.*"; 4、查看各部的平均年龄并升序排序。升序时,asc可省 pythonx select avg(age),dep from emp group by dep order by avg(age) asc; 5、思考:按照年龄升序的基础上再姓名降序查看全部人的信息 select * from emp order by age asc,name desc; 六、查询各部门中年纪最大的人的姓名与居住地(户籍+区域) select name,dep,concat_ws("-",area,port)"居住地" from emp where(age,dep) in (select max(age),dep from emp group by dep); select name, concat(area, '-', port), dep from emp where (age,dep) in (select max(age),dep from emp group by dep); 7、查询不一样年龄层次平均薪资大于5w组中工资最高者的姓名与薪资 select age,name,avg(salary)"平均薪资",max(salary)"最高薪资" from emp group by age having avg(salary)>5 ; # 非安全模式下可行,但name不是聚合,不可直接查,安全模式下报错 select age,name,salary from emp where (salary,age) in(select max(salary),age from emp group by age having avg(salary)>5);
多表操做
多表依赖数据
create table dep( id int primary key auto_increment, name varchar(16), work varchar(16) ); create table emp( id int primary key auto_increment, name varchar(16), salary float, dep_id int ); insert into dep values(1, '市场部', '销售'), (2, '教学部', '授课'), (3, '管理部', '开车'); insert into emp(name, salary, dep_id) values('egon', 3.0, 2),('yanghuhu', 2.0, 2),('sanjiang', 10.0, 1),('owen', 88888.0, 2),('liujie', 8.0, 1),('yingjie', 1.2, 0);
笛卡尔积(交叉链接)
# 需求: # 查看每位员工的部门的全部信息 select * from emp; select * from dep; # 子查询, 最终结果只能显示单表的信息, 但需求是同时显示两张表的信息 => 先将两张表合成一张表 select * from emp where dep_id in (select id from dep); # Empty set (0.06 sec) 默认查询结果:笛卡尔积 select * from emp, dep; # Empty set (0.07 sec) 数学概念:A{a, b}*B{0, 1, 2} => O{{a, 1}, {a, 2}, {a, 3}, {b, 1}, {b, 2}, {b, 3}} 交叉查询:select * from emp,dep;| select * from emp course join dep; # 作了筛选, 结果<=完整数据, 非笛卡尔积 select * from emp, dep where db2.emp.dep_id = db2.dep.id; # 同sql语句上表现是从两张表拿数据 # 注意: 同时查询两张表造成新的表,能够称之为虚拟表, 原表与表之间可能存在重复字段, 同时使用时须要明确所属表,必要时还需明确所属数据库
合理对应关系的虚拟表
select * from emp, dep where emp.dep_id = dep.id; 注:虚拟表中可能出现重复字段,须要在sql语句中明确实体表名
多表链接(*****)
===>虚拟的单表
用专业的语法来关联多表
内链接
内链接:结果为两张表有对应关系的数据(emp有dep无\emp无dep有的数据均不会被显示) 语法:左表 inner join 右表 on 两表有关联的字段的条件 eg: select * from emp inner join dep on emp.dep_id = dep.id;
左链接
左链接:在内链接的基础上还保留左表的记录 语法:左表 left join 右表 on 两表有关联的字段的条件 eg: select * from emp left join dep on emp.dep_id = dep.id;
右链接
右链接:在内链接的基础上还保留右表的记录 语法:左表 right join 右表 on 两表有关联的字段的条件 eg: select * from emp right join dep on emp.dep_id = dep.id;
全链接
#注意:mysql不支持全外链接 full JOIN #强调:mysql可使用此种方式间接实现全外链接 全链接:在内链接的基础上分别保留这左表右表的记录 语法:mysql没有full join on语法,但能够经过去重达到效果 eg: select * from emp left join dep on emp.dep_id = dep.id union # ??? select * from emp right join dep on emp.dep_id = dep.id;
练习
1.查询每一位员工对应的工做职责 # 每一位员工 => 左表为emp表, 那么左表的全部数据均须要被保留, 全部采用左链接 => 左表为dep表, 那么右表的全部数据均须要被保留, 全部采用右链接 # select emp.name, dep.work from emp left join dep on emp.dep_id = dep.id; select emp.name, dep.work from dep right join emp on emp.dep_id = dep.id; 2.查询每个部门下的员工们及员工职责 # select max(dep.name), max(dep.work), group_concat(emp.name) from emp right join dep on emp.dep_id = dep.id group by dep_id; # 分析过程 # 每个部门 => dep的信息要被所有保留, 须要分组 # 员工职责 => dep.work, 因为分组不能直接被查询 => 须要用聚合函数处理 # 员工们 => emp.name作拼接 => group_concat(emp.name) # 分组的字段 => 部门 => emp.dep_id => emp.dep_id能够直接被查询,但没有显示意义 => dep.name用来显示 => dep.name须要用聚合函数处理 select max(dep.name), max(dep.work), group_concat(emp.name) from dep left join emp on emp.dep_id = dep.id group by emp.dep_id; # 注: on在where条件关键词之左
navicat
Navicat Premium是一款专业的数据管理工具, 支持链接MsSql、MySQL、SQLite、Oracle、PostgreSQL等类型的数据库, 可导入/导出链接。编辑数据库各项数据。 同步传输数据或数据库结构, 在数据库或模式中查找数据。 1. 安装navicat 2.链接数据库,并建库 3.建立表、设置字段、插入数据 4.新建查询
python使用mysql
# 模块pymysql # 安装并导入pymysql: pip3 insatll pymysql # 经过pymysql操做数据库分四步: 1.创建链接 conn = pymysql.connect(host="localhost", port=3306, db='db2', user='root', password='root') 2.设置字典类型游标 cursor = conn.cursor(pymysql.cursors.DictCursor) line=cursor.execute(sql,args)args=>防注入,其可为元组|列表|字典 3.执行sql语句并使用执行结果 # 书写sql语句 sql = 'select * from emp' # 执行sql语句, 有返回值, 返回值为获得的记录行数 line = cursor.execute(sql) print(line) ''' # 使用执行的结果: date=cursor.fetchone()当前游标日后获取一行记录 cursor.fetchall()当前游标日后全部的记录 scroll(num, mode="relative|absolute") relative: 游标从当前位置日后移动num行 ablolute: 游标从头日后移动num行, 通常能够结合line来使用能定位到任意位置 ''' tag = cursor.fetchone() # 第一条 print(tag) print(tag['salary']) tag = cursor.fetchone() # 第二条 print(tag) cursor.scroll(1, mode='relative') # 偏移第三条 # cursor.scroll(line - 1, mode='absolute') # 指针绝对, 游标永远从头开始偏移 tags = cursor.fetchall() # 第四条到最后 print(tags) 4.断开链接 cursor.close() conn.close()
pymysql处理了sql注入
# 什么是sql注入: # 经过书写sql包含(注释相关的)特殊字符, 让原有的sql执行顺序发生改变, 从而改变执行获得的sql # 目的: # 绕过原有的sql安全认证, 达到对数据库攻击的目的 # 没有处理sql注入的写法 sql = 'select * from user where usr="%s" and pwd="%s"' % (usr, pwd) res = cursor.execute(sql) import pymysql conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db='db2') cursor = conn.cursor(pymysql.cursors.DictCursor) # 登陆 # 获得用户输入的帐户密码 usr = input("请输入用户名: ") #abc pwd = input("请输入密码: ") #123 # sql注入 # 1.知道用户名: abc" -- hehe | ooo # select * from user where usr="abc" -- hehe" and pwd="ooo" # 2.不知道用户名 aaa" or 1=1 -- hehe | 000 # select * from user where usr="aaa" or 1=1 -- hehe" and pwd="000" # 处理方式 # 对输入的帐户密码作彻底处理 => 不可能造成达到sql注入的特殊语法 => 正则 # 和数据库的帐户密码进行配对 # sql = 'select * from user where usr="%s" and pwd="%s"' % (usr, pwd) # select * from user where usr="abc" and pwd="123" # res = cursor.execute(sql) # pymysql已经处理了sql注入 sql = 'select * from user where usr=%s and pwd=%s' res = cursor.execute(sql, (usr, pwd)) # print(res) if res: print('login success') else: print('login failed') cursor.close() conn.close()
增删改
import pymysql conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='root', db='db2') cursor = conn.cursor(pymysql.cursors.DictCursor) # 增 # 增sql语句 sql1 = 'insert into user(usr, pwd) values (%s, %s)'# cursor执行sql语句,在内存中完成了对数据的插入, 但不能将数据存放到硬盘.会将id完成自增 # 在内存中一次插入一条 cursor.execute(sql1, ("opq", "123")) # 在内存中一次插入多条 cursor.executemany(sql1, [("aaa", "000"), ("bbb", "111")]) # 将内存中的数据提交到硬盘中, 完成真实意义上的数据存储 conn.commit() # 删 sql2 = 'delete from user where usr=%s' cursor.execute(sql2, ("aaa")) conn.commit() # 改 sql3 = 'update user set pwd=%s where usr=%s' res = cursor.execute(sql3, ("222", "bbb")) conn.commit()
练习
一、准备数据
create table book( id int primary key auto_increment, name varchar(16), price int ); create table author( id int primary key auto_increment, name varchar(16) ); create table book_author( id int primary key auto_increment, book_id int, author_id int ); insert into book values(1, '葵花宝典', 88), (2, '九阴真经', 78), (3, '辟邪剑谱', 99); insert into author values(1, '张全蛋'), (2, '李诞'), (3, '寡人'); insert into book_author(id, book_id, author_id) values(1,1,1),(2,1,2),(3,1,3),(4,2,1),(5,2,3),(6,3,2),(7,3,2);
id | name | pirce |
---|---|---|
1 | 葵花宝典 | 88 |
2 | 九阴真经 | 78 |
3 | 辟邪剑谱 | 99 |
id | name |
---|---|
1 | 张全蛋 |
2 | 李诞 |
3 | 寡人 |
id | book_id | author_id |
---|---|---|
1 | 1 | 1 |
2 | 1 | 2 |
3 | 1 | 3 |
4 | 2 | 1 |
5 | 2 | 3 |
6 | 3 | 2 |
7 | 3 | 2 |
二、查看每本书的做者们
select book.name,group_concat(distinct author.name) from book left join book_author on book.id=book_author.book_id inner join author on author.id=book_author.author_id group by book.name; 1.去重 select distinct book_id,author_id from book_author; 2.连表 book inner join author inner join (select distinct book_id,author_id from book_author)as ba on book_id=ba.book_id and author.id=ba.author_id; 3.分组 group by ba.book_id; 4.查询字段 select book.name,group_concat(author.name) 5.结果 select max(book.name),group_concat(author.name)from book inner join author inner join (select distinct book_id,author_id from book_author; )as ba on book_id=ba.book_id and author.id=ba.author_id group by ba.book_id;
视图
三、查看每位做者都有哪些做品
select author.name,group_concat(distinct book.name) from book right join book_author on book.id=book_author.book_id inner join author on author.id=book_author.author_id group by author.name;
四、假设每本书各卖出10本,每位做者的收益是多少
按做者分组--》看查啥字段--》=好--》看查出的字段是几条数据--》数据关系--》再按做者分组,求和 select author.name author_name,sum(book.price*10) money from author,book,book_author c where author.id = c.author_id and book.id = c.book_id group by author.name;
五、一个历史操做残留的数据库表user,有自增主键id,帐号usr及密码pw两个字段,有4条数据,但数据id值分别为2,3,5,7(第1条数据等被删除操做了),现需求要保证数据主内容不变,重构数据索引从1开始,4条数据id分别修改成1,2,3,4
# 备份数据 create table new_user select * from user; # 清空表 truncate user; # 重构数据 insert into user(usr,pwd)select usr,pwd from new_user; # 删除备份 drop table new_user;
六、经过pymysql完成对表emp的彻底拷贝(结果+数据),获得新表new_emp
import pymysql conn=pymysql.connect( host="localhost", port=3306, user="root", password="root", db="db1" ) cursor = conn.cursor(pymysql.cursors.DictCursor) sql1 = "create table new_emp like emp " sql2 ="insert into new_emp select * from emp" cursor.execute(sql) cursor.execute(sql) conn.commit() cursor.close() conn.close()
七、实现注册功能
帐号密码由控制台录入
数据库已有该用户则提示用户登陆
数据库没有该用户则完成注册
usr=input("输入帐号") pwd=input("输入密码") # 先查
emmm 我知道我没写完,就酱。。何时想起来有空了我会再捋一遍的!!!
数据库高级
一.视图
what: 由一张表或多张表的查询结果造成的一张虚拟表 -- 有实体的表结构 | 没有实体属于本身的数据, 数据来源于真实表 | (没有键, 存在于真实表) why: 多张表的某一查询结果须要不断的重复使用(复用性) | 一张表能够经过不一样需求(权限)来造成多张子表
视图的另外一个做用是,能够不一样的视图来展现开放不一样数据的访问
例如,同一张工资表,老板能够查看所有,部门主管能够查看该部门全部人,员工只能看本身的一条记录
建立视图
CREATE [OR REPLACE] VIEW view_name [(column_list字段名们)] AS select_statement查询结果
加上OR REPLACE 时若是已经存在相同视图则替换原有视图
column_list指定哪些字段要出如今视图中
注意:因为是一张虚拟表,视图中的数据实际上来源于其余其余表,因此在视图中的数据不会出如今硬盘上
使用视图
视图是一张虚拟表 因此使用方式与普通表没有任何区别
查看视图
1.desc view_name; //查看数据结构 2.show create view view_name;//查看 建立语句
修改视图
alter view_name select_statement
删除视图
drop view view_name
案例1: 简化多表sql语句
#准备数据 create database db02 charset utf8; use db02 create table student( s_id int(3), name varchar(20), math float, chinese float ); insert into student values(1,'tom',80,70),(2,'jack',80,80),(3,'rose',60,75); create table stu_info( s_id int(3), class varchar(50), addr varchar(100) ); insert into stu_info values(1,'二班','安徽'),(2,'二班','湖南'),(3,'三班','黑龙江'); #建立视图包含 编号 学生的姓名 和班级 create view stu_v (编号,姓名,班级) as select student.s_id,student.name ,stu_info.class from student,stu_info where student.s_id=stu_info.s_id; # 查看视图中的数据 select *from stu_v;
案例2: 隔离数据
# 建立工资表 create table salarys( id int primary key, name char(10), salary double, dept char(10) ); insert into salarys values (1,"刘强东",900000,"市场"), (2,"马云",800090,"市场"), (3,"李彦宏",989090,"财务"), (4,"马化腾",87879999,"财务"); # 建立市场部视图 create view dept_sc as select *from salarys where dept = "市场"; # 查看市场部视图 select *from dept_sc;
注意 对视图数据的insert update delete 会同步到原表中,但因为视图多是部分字段,不少时候会失败
总结:mysql能够分担程序中的部分逻辑,但这样一来后续的维护会变得更麻烦
若是须要改表结构,那意味着视图也须要相应的修改,没有直接在程序中修改sql来的方便
二.触发器
what: 在一个表数据发生变化时(增删改), 触发(对另外一表)某项功能(执行sql语句块) why: 须要完成的操做基于对某一种表的增删改操做
建立触发器
delimiter && CREATE TRIGGER t_name t_time t_event ON table_name FOR EACH ROW begin stmts..... end && delimiter ;
结论: 触发器内不能出现查询语句(Not allowed to return a result set from a trigger), 不对对自身表完成增删改查
支持的时间点(t_time):时间发生前和发生先后 before|after
支持的事件(t_event): update insert delete
在触发器中能够访问到将被修改的那一行数据 根据事件不一样 能访问也不一样 update 可用OLD访问旧数据 NEW访问新数据 insert 可用NEW访问新数据 delete 可用OLD访问旧数据
能够将NEW和OLD看作一个对象其中封装了这列数据的全部字段
案例:有cmd表和错误日志表,需求:在cmd执行失败时自动将信息存储到错误日志表中
#准备数据 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 ); # 建立触发器 delimiter // create trigger trigger1 after insert on cmd for each row begin if new.success = "no" then insert into errlog values(null,new.cmd,new.sub_time); end if; end// delimiter ; #往表cmd中插入记录,触发触发器,根据IF的条件决定是否插入错误日志 INSERT INTO cmd ( USER, priv, cmd, sub_time, success ) VALUES ('egon','0755','ls -l /etc',NOW(),'yes'), ('egon','0755','cat /etc/passwd',NOW(),'no'), ('egon','0755','useradd xxx',NOW(),'no'), ('egon','0755','ps aux',NOW(),'yes'); # 查看错误日志表中的记录是否有自动插入 select *from errlog;
delimiter
用于修改默认的行结束符 ,因为在触发器中有多条sql语句他们须要使用分号来结束,可是触发器是一个总体,因此咱们须要先更换默认的结束符,在触发器编写完后在将结束符设置回分号
注意:
外键不能触发事件 主表删除了某个主键 从表也会相应删除 可是并不会执行触发器。触发器中不能使用事务,相同时间点的相同事件的触发器 不能同时存在
删除触发器
语法: drop trigger trigger_name; 案例: drop trigger trigger1;
一样的这种需求咱们彻底能够在python中来完成! mysql最想完成的事情是讲全部能处理的逻辑所有放到mysql中,那样一来应用程序开发者的活儿就变少了,相应的数据库管理员的工资就高了,惋惜大多中小公司都没有专门的DBA;
疑惑:修改行结束符后,触发器内的sql语句任然是以分号结束,为何? 实际上在mysql中输入分号回车,mysql会当即将语句发送给服务器端,修改行结束符仅仅是告诉mysql客户端,语句没有写完,不要当即发送!
三.事务
what: 一组sql操做, 要么所有成功, 要么所有失败 why: 一组操做须要同时成功,不然就断定为失败, 要将其构建成事务
例如转帐操做,
1.从原有帐户减去转帐金额
2.给目标帐户加上转帐金额
若中间忽然断电了或系统崩溃了,钱就不知去向了!
使用事务
start transaction; --开启事物,在这条语句以后的sql将处在同一事务,并不会当即修改数据库
commit;--提交事务,让这个事物中的sql当即执行数据的操做,
rollback;--回滚事务,取消这个事物,这个事物不会对数据库中的数据产生任何影响
案例:转帐过程当中发生异常
#准备数据 create table account( id int primary key auto_increment, name varchar(20), money double ); insert into account values(1,'赵大儿子',1000); insert into account values(2,'刘大牛',1000); insert into account values(3,'猪头三',1000); insert into account values(4,'王进',1000); insert into account values(5,'黄卉',1000); # 赵大儿子刘大牛佳转帐1000块 # 未使用事务 update account set money = money - 1000 where id = 1; update account set moneys = money - 1000 where id = 1; # money打错了致使执行失败 # 在python中使用事务处理 sql = 'update account set money = money - 1000 where id = 1;' sql2 = 'update account set moneys = money + 1000 where id = 2;' # money打错了致使执行失败 try: cursor.execute(sql) cursor.execute(sql2) conn.commit() except: conn.rollback()
注意:事务的回滚的前提是能捕捉到异常,不然没法决定什么时候回滚,Python中很简单就实现了,另外mysql中须要使用存储过程才能捕获异常!
事务的四个特性:
原子性:事务是一组不可分割的单位,要么同时成功,要么同时不成功
一致性:事物先后的数据完整性应该保持一致,(数据库的完整性:若是数据库在某一时间点下,全部的数据都符合全部的约束,则称数据库为完整性的状态);
隔离性:事物的隔离性是指多个用户并发访问数据时,一个用户的事物不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离
持久性:持久性是指一个事物一旦被提交,它对数据的改变就是永久性的,接下来即便数据库发生故障也不该该对其有任何影响
事务的用户隔离级别:
数据库使用者能够控制数据库工做在哪一个级别下,就可与防止不一样的隔离性问题
read uncommitted --不作任何隔离,可能脏读,幻读
read committed----能够防止脏读,不能防止不可重复读,和幻读,
Repeatable read --能够防止脏读,不可重复读,不能防止幻读
Serializable--数据库运行在串行化实现,全部问题都没有,就是性能低
修改隔离级别:
select @@tx_isolation;--查询当前级别
set[session|global] transaction isolation level .... ;修改级别
实例:
set global transaction isolation level Repeatable read ; 修改后从新链接服务器生效
四.存储过程
1.什么是存储过程
存储过程是一组任意的sql语句集合,存储在mysql中,调用存储过程时将会执行其包含的全部sql语句;与python中函数相似;
2.为何使用存储过程
回顾触发器与视图都是为了简化应用程序中sql语句的书写,可是仍是须要编写,而存储过程当中能够包含任何的sql语句,包括视图,事务,流程控制等,这样一来,应用程序能够从sql语句中彻底解放,mysql能够替代应用程序完成数据相关的的逻辑处理!
那咱们之后都是用存储过程不就完了?
三种开发方式对比
1.应用程序仅负责业务逻辑编写,全部与数据相关的逻辑都交给mysql来完成,经过存储过程(推荐使用)
优势:
应用程序与数据处理完解耦合,一堆复杂的sql被封装成了一个简单的存储过程,考虑到网络环境因素,效率高
应用程序开发者不须要编写sql语句,开发效率高
缺点:
python语法与mysql语法区别巨大,学习成本高,而且各类数据库的语法大不相同,因此移植性很是差
应用程序开发者与BDA的跨部门沟通成本高,形成总体效率低
2.应用程序不只编写业务逻辑,还须要编写全部的sql语句
优势:扩展性高,对于应用程序开发者而言,扩展性和维护性相较于第一种都有所提升
缺点:sql语句过于复杂,致使开发效率低,且须要考虑sql'优化问题
3.应用程序仅负责业务逻辑,sql语句的编写交给ORM框架,(经常使用解决方案)
优势:应用程序开发者不须要编写sql语句,开发效率高
缺点:执行效率低,因为须要将对象的操做转化为sql语句,且须要经过网络发送大量sql
建立存储过程
create procedure pro_name(p_Type p_name data_type) begin sql语句......流程控制 end
p_type 参数类型
in 表示输入参数
out 表示输出参数
inout表示既能输入又能输出
p_name 参数名称
data_type 参数类型 能够是mysql支持的数据类型
案例:使用存储过程完成对student表的查询
delimiter // create procedure p1(in m int,in n int,out res int) begin select *from student where chinese > m and chinese < n; #select *from student where chineseXXX > m and chinese < n; 修改错误的列名以测试执行失败 set res = 100; end// delimiter ; set @res = 0; #调用存储过程 call p1(70,80,@res); #查看执行结果 select @res;
须要注意的是,存储过程的out类参数必须是一个变量,不能是值;
在python中调用存储过程
import pymysql #创建链接 conn = pymysql.connect( host="127.0.0.1", user="root", password="admin", database="db02" ) # 获取游标 cursor = conn.cursor(pymysql.cursors.DictCursor) # 调用用存储过程 cursor.callproc("p1",(70,80,0)) #p1为存储过程名 会自动为为每一个值设置变量,名称为 @_p1_0,@_p1_1,@_p1_2 # 提取执行结果是否有结果取决于存储过程当中的sql语句 print(cursor.fetchall()) # 获取执行状态 cursor.execute("select @_p1_2") print(cursor.fetchone())
此处pymysql会自动将参数都设置一个变量因此能够直接传入一个值,固然值若是做为输出参数的话,传入什么都无所谓!
删除存储过程
drop procedure 过程名称;
修改存储过程意义不大,不如删除重写!
查看存储过程
#当前库全部存储过程名称 select `name` from mysql.proc where db = 'db02' and `type` = 'PROCEDURE'; #查看建立语句 show create procedure p1;
存储过程当中的事务应用
存储过程当中支持任何的sql语句包括事务!
案例:模拟转帐中发送异常,进行回滚
delimiter // create PROCEDURE p5( OUT p_return_code tinyint ) BEGIN DECLARE exit handler for sqlexception BEGIN # ERROR set p_return_code = 1; rollback; END; # exit 也能够换成continue 表示发送异常时继续执行 DECLARE exit handler for sqlwarning BEGIN # WARNING set p_return_code = 2; rollback; END; START TRANSACTION; update account set money = money - 1000 where id = 1; update account set moneys = money - 1000 where id = 1; # moneys字段致使异常 COMMIT; # SUCCESS set p_return_code = 0; #0表明执行成功 END // delimiter ; #在mysql中调用存储过程 set @res=123; call p5(@res); select @res;
总结:抛开沟通成本,学习成本,存储过程无疑是效率最高的处理方式,面试会问,一些公司也有一些现存的存储过程,重点掌握!
五.函数
函数与python中的定义一致,再也不啰嗦!
内置函数
日期相关:
字符串相关:
数字相关:
其余函数:
固然也包括以前学习的聚合函数
自定义函数
语法:
CREATE FUNCTION f_name(paramters) returns dataType; return value;
说明: paramters 只能是in 输入参数 参数名 类型 必须有返回值 不能呢加begin 和end returns 后面是返回值的类型 这里不加分号 return 后面是要返回的值
案例: 将两数相加
create function addfuntion(a int,b int) returns int return a + b; #执行函数 select addfuntion(1,1);
注意:
函数只能返回一个值 函数通常不涉及数据的增删改查 就是一个通用的功能 调用自定义的函数 与调用系统的一致 不须要call 使用select 可得到返回值 函数中不能使用sql语句 就像在java中不能识别sql语句同样
六.数据备份与恢复
使用mysqldump程序进行备份
mysqldump -u -p db_name [table_name,,,] > fileName.sql
能够选择要备份哪些表 若是不指定表明 所有备份
#示例: #单库备份 mysqldump -uroot -p123 db1 > db1.sql mysqldump -uroot -p123 db1 table1 table2 > db1-table1-table2.sql #多库备份 mysqldump -uroot -p123 --databases db1 db2 mysql db3 > db1_db2_mysql_db3.sql #备份全部库 mysqldump -uroot -p123 --all-databases > all.sql
使用 mysql 进行恢复
1.退出数据库后
mysql -u -p < filename.sql;
2.不用退出数据库
2.1 建立空数据库
2.2选择数据库
2.3而后使用source filename; 来进行还原
use db1;
source /root/db1.sql
数据库迁移
务必保证在相同版本之间迁移 # mysqldump -h 源IP -uroot -p123 --databases db1 | mysql -h 目标IP -uroot -p456
七.流程控制
if语句的使用
if 条件 then 语句; end if;
第二种 if elseif if 条件 then 语句1; elseif 条件 then 语句2; else 语句3; end if;
案例:编写过程 实现 输入一个整数type 范围 1 - 2 输出 type=1 or type=2 or type=other;
create procedure showType(in type int,out result char(20)) begin if type = 1 then set result = "type = 1"; elseif type = 2 then set result = "type = 2"; else set result = "type = other"; end if; end
CASE 语句
大致意思与Swtich同样的 你给我一个值 我对它进行选择 而后执行匹配上的语句 语法:
create procedure caseTest(in type int) begin CASE type when 1 then select "type = 1"; when 2 then select "type = 2"; else select "type = other"; end case; end
定义变量
declare 变量名 类型 default 值; 例如: declare i int default 0;
WHILE循环
循环输出10次hello mysql create procedure showHello() begin declare i int default 0; while i < 10 do select "hello mysql"; set i = i + 1; end while; end
LOOP循环的
没有条件 须要本身定义结束语句 语法:
输出十次hello mysql; create procedure showloop() begin declare i int default 0; aloop: LOOP select "hello loop"; set i = i + 1; if i > 9 then leave aloop; end if; end LOOP aloop; end
REPEAT循环
#相似do while #输出10次hello repeat create procedure showRepeat() begin declare i int default 0; repeat select "hello repeat"; set i = i + 1; until i > 9 end repeat; end #输出0-100之间的奇数 create procedure showjishu() begin declare i int default 0; aloop: loop set i = i + 1; if i >= 101 then leave aloop; end if; if i % 2 = 0 then iterate aloop; end if; select i; end loop aloop; end
索引
在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构; 也称之为key
索引的做用至关于图书的目录,能够根据目录中的页码快速找到所需的内容。
为何须要索引
相比较下,对数据的读操做会更加频繁,比例在10:1左右,也就是说对数据库的查询操做是很是频繁的
随着时间的推移,表中的记录会愈来愈多,此时若是查询速度太慢的话对用户体验是很是不利的
索引是提高查询效率最有效的手段!
简单的说索引的就是用帮咱们加快查询速度的
须要注意的是:在数据库中插入数据会引起索引的重建
小白的误区
既然索引如此神奇,那之后只要速度慢了就加索引,
这种想法是很是low的,
索引是否是越多越好,而且有了索引后还要考虑索引是否命中
加上索引后对数据的写操做速度会下降
索引的实现原理
如何能实现加快查询的效果呢?
来看一个例子:
初版的新华字典共800页,那时没有检字表,每一个字的详细信息,随机的罗列在书中,一同窗买回来查了一次,在也没用过,由于没有任何的数据结构,查字只能一页一页日后翻,反了两小时没翻着,只能放弃了!
后来出版社发现了这个问题,他们将书中全部字按照拼音音节顺序进行了排序,拼音首字母为a的排在最前,首字母为z的排在最后:
如此一来再再也不须要一页一页的去查字了,而是先查看索引,找出字的拼音首字母到索引中进行对照,例如:找搭
字其拼音首字母为d,因此直接找到D对应的索引目录,很快就能定位到要找的搭
字在79页,查询速度获得数量级的提高!
须要注意的是,原来内容为800页如今由于多了索引数据,总体页数必然增长了
数据库中的索引,实现思路与字典是一致的,须要一个独立的存储结构,专门存储索引数据
本质上索引是经过不断的缩小查询范围来提升查询效率
磁盘IO问题(了解)
数据库的数据最终存储到了硬盘上
机械硬盘因为设计原理,致使查找数据时须要有一个寻道时间与平均延迟时间,常规硬盘寻道为5ms,平均延迟按照每分钟7200转来计算,7200/60 = 120 ; 1000/120/2 = 4ms 总共为9ms,那么9毫秒对于cpu而言已经很是很是的长了,足够作不少运算操做,目前最新的处理器每秒能处理数万亿次运算,拿一个很是垃圾的处理器来举例子,假设处理器每秒处理5亿次计算,每毫秒是50万次运算,9ms能够进行450万次运算,数据库中成千上万的数据,每条数据9ms显然慢到不行!
操做系统预读取(了解)
考虑到磁盘IO是很是高昂的操做,计算机操做系统作了一些优化,当一次IO时,不只获取当前磁盘地址的数据,并且把相邻的数据也都读取到内存缓冲区内,由于局部预读性原理告诉咱们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据咱们称之为一页(page)。具体一页有多大数据跟操做系统有关,通常为4k或8k,也就是咱们读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计很是有帮助。
索引数据结构剖析
在字典的例子中咱们知道了,索引是独立于真实数据的一个存储结构,这个结构究竟是什么样的?
索引最终的目的是要尽量下降io次数,减小查找的次数,以最少的io找到须要的数据,此时B+树闪亮登场
光有数据结构还不行,还须要有对应的算法作支持,就是二分查找法
有了B+数据结构后查找数据的方式就再也不是逐个的对比了,而是经过二分查找法来查找(流程演示)
另外,其实大多数文件系统都是使用B+是来完成的!
应该尽量的将数据量小的字段做为索引
经过分析能够发如今上面的树中,查找一个任何一个数据都是3次IO操做, 可是这个3次并非固定的,它取决于数结构的高度,目前是三层,若是要存储新的数据比99还大的数据时,发现叶子节点已经不够了必须在上面加一个子节点,因为树根只能有一个,因此整个数的高度会增长,一旦高度增长则 查找是IO次数也会增长,因此:
应该尽量的将数据量小的字段做为索引,这样一个叶子节点能存储的数据就更多,从而下降树的高度;
例如:name
和id
,应当将id设置为索引而不是name
最左匹配原则*
当b+树的数据项是复合的数据结构,好比(name,age,sex)的时候(多字段联合索引),b+树会按照从左到右的顺序来创建搜索树,好比当(张三,20,F)这样的数据来检索的时候,b+树会优先比较name来肯定下一步的所搜方向,若是name相同再依次比较age和sex,最后获得检索的数据;但当(20,F)这样的没有name的数据来的时候,b+树就不知道下一步该查哪一个节点,由于创建搜索树的时候name就是第一个比较因子,必需要先根据name来搜索才能知道下一步去哪里查询。好比当(张三,F)这样的数据来检索时,b+树能够用name来指定搜索方向,但下一个字段age的缺失,因此只能把名字等于张三的数据都找到,而后再匹配性别是F的数据了, 这个是很是重要的性质,即索引的最左匹配特性。
汇集索引*
mysql官方文档原文: 插入了解 或折叠
MySQL为表把它的数据词典信息以.frm文件的形式存在数据库目录里,这对全部MySQL存储引擎都是真的。但 是每一个InnoDB表在表空间内的InnoDB内部数据词典里有它本身的条目。当MySQL移除表或数据库,它不得不 删除.frm文件和InnoDB数据词典内的相应条目。这就是为何你不能在数据库之间简单地移动.frm文件来移 动InnoDB表。
每一个InnoDB表有专门索引,被称为clustered index,对行的数据被存于其中。若是你对你的表定义一 个PRIMARY KEY, 主键的索引是集束索引。
若是你没有为表定义PRIMARY KEY,MySQL拾取第一个仅有NOT NULL列的UNIQUE索引做为主键,并 且InnoDB把它看成集束索引来用。若是表中没有这样一个索引,InnoDB内部产生一个集束索引,其中 用InnoDB在这样一个表内指定给行的行ID来排序行。行ID是一个6字节的域,它在新行被插入的时候简单地增长。所以被行ID排序的行是物理地按照插入顺序排的。
经过集束索引访问一个行是较快的,由于行数据是在索引搜索引导的同一页面。若是表是巨大的,当对比于传 统解决方案,集束索引构架常常节约磁盘I/O。(在许多数据库,数据传统地被存在与索引记录不一样的页)。
在InnoDB中,非集束索引里的记录(也称为第二索引)包含对行的主键值。InnoDB用这个 主键值来从集束索 引中搜索行。注意,若是主键是长的,第二索引使用更多空间。
简单总结:
聚焦索引的特色:
叶子节点保存的就是完整的一行记录,若是设置了主键,主键就做为汇集索引,
若是没有主键,则找第一个NOT NULL 且QUNIQUE的列做为汇集索引,
若是也没有这样的列,innoDB会在表内自动产生一个汇集索引,它是自增的
辅助索引*
除了汇集索引以外的索引都称之为辅助索引或第二索引,包括 foreign key
与 unique
辅助索引的特色:
其叶子节点保存的是索引数据与所在行的主键值,InnoDB用这个 主键值来从汇集索引中搜查找数据
覆盖索引
覆盖索引指的是须要的数据仅在辅助索引中就能找到:
#假设stu表的name字段是一个辅助索引 select name from stu where name = "jack";
这样的话则不须要在查找汇集索引数据已经找到
回表
若是要查找的数据在辅助索引中不存在,则须要回到汇集索引中查找,这种现象称之为回表
# name字段是一个辅助索引 而sex字段不是索引 select sex from stu where name = "jack";
须要从辅助索引中获取主键的值,在拿着主键值到汇集索引中找到sex的值
查询速度对比:
汇集索引 > 覆盖索引 > 非覆盖索引
正确使用索引
案例:首先准备一张表数据量在百万级别
create table usr(id int,name char(10),gender char(3),email char(30)); #准备数据 delimiter // create procedure addData(in num int) begin declare i int default 0; while i < num do insert into usr values(i,"jack","m",concat("xxxx",i,"@qq.com")); set i = i + 1; end while; end // delimiter ; #执行查询语句 观察查询时间 select count(*) from usr where id = 1; #1 row in set (3.85 sec) #时间在秒级别 比较慢 1. #添加主键 alter table usr add primary key(id); #再次查询 select count(*) from usr where id = 1; #1 row in set (0.00 sec) #基本在毫秒级就能完成 提高很是大 2. #当条件为范围查询时 select count(*) from usr where id > 1; #速度依然很慢 对于这种查询没有办法能够优化由于须要的数据就是那么多 #缩小查询范围 速度立马就快了 select count(*) from usr where id > 1 and id < 10; #当查询语句中匹配字段没有索引时 效率测试 select count(*) from usr where name = "jack"; #1 row in set (2.85 sec) # 速度慢 3. # 为name字段添加索引 create index name_index on usr(name); # 再次查询 select count(*) from usr where name = "jack"; #1 row in set (3.89 sec) # 速度反而下降了 为何? #因为name字段的区分度很是低 彻底没法区分 ,由于值都相同 这样一来B+树会没有任何的子节点,像一根竹竿每一都匹配至关于,有几条记录就有几回io ,全部要注意 区分度低的字段不该该创建索引,不能加速查询反而下降写入效率, #同理 性别字段也不该该创建索引,email字段更加适合创建索引 # 修改查询语句为 select count(*) from usr where name = "aaaaaaaaa"; #1 row in set (0.00 sec) 速度很是快由于在 树根位置就已经判断出树中没有这个数据 所有跳过了 # 模糊匹配时 select count(*) from usr where name like "xxx"; #快 select count(*) from usr where name like "xxx%"; #快 select count(*) from usr where name like "%xxx"; #慢 #因为索引是比较大小 会从左边开始匹配 很明显全部字符都能匹配% 因此全都匹配了一遍 4.索引字段不能参加运算 select count(*) from usr where id * 12 = 120; #速度很是慢缘由在于 mysql须要取出全部列的id 进行运算以后才能判断是否成立 #解决方案 select count(*) from usr where id = 120/12; #速度提高了 由于在读取数据时 条件就必定固定了 至关于 select count(*) from usr where id = 10; #速度天然快了 5.有多个匹配条件时 索引的执行顺序 and 和 or #先看and #先删除全部的索引 alter table usr drop primary key; drop index name_index on usr; #测试 select count(*) from usr where name = "jack" and gender = "m" and id = 1 and email = "xxxx2@qq.com"; #1 row in set (1.34 sec) 时间在秒级 #为name字段添加索引 create index name_index on usr(name); #测试 select count(*) from usr where name = "jack" and gender = "m" and id = 1 and email = "xxxx2@qq.com"; #1 row in set (17.82 sec) 反而时间更长了 #为gender字段添加索引 create index gender_index on usr(gender); #测试 select count(*) from usr where name = "jack" and gender = "m" and id = 1 and email = "xxxx2@qq.com"; #1 row in set (16.83 sec) gender字段任然不具有区分度 #为id加上索引 alter table usr add primary key(id); #测试 select count(*) from usr where name = "jack" and gender = "m" and id = 1 and email = "xxxx1@qq.com"; #1 row in set (0.00 sec) id子弹区分度高 速度提高 #虽然三个字段都有索引 mysql并非从左往右傻傻的去查 而是找出一个区分度高的字段优先匹配 #改成范围匹配 select count(*) from usr where name = "jack" and gender = "m" and id > 1 and email = "xxxx1@qq.com"; #速度变慢了 #删除id索引 为email创建索引 alter table usr drop primary key; create index email_index on usr(email); #测试 select count(*) from usr where name = "jack" and gender = "m" and id = 1 and email = "xxxx2@qq.com"; #1 row in set (0.00 sec) 速度很是快 #对于or条件 都是从左往右匹配 select count(*) from usr where name = "jackxxxx" or email = "xxxx0@qq.com"; #注意 必须or两边都有索引才会使用索引 6.多字段联合索引 为何须要联合索引 案例: select count(*) from usr where name = "jack" and gender = "m" and id > 3 and email = "xxxx2@qq.com"; 假设全部字段都是区分度很是高的字段,那么除看id为谁添加索引都可以提高速度,可是若是sql语句中没有出现因此字段,那就没法加速查询,最简单的办法是为每一个字段都加上索引,可是索引也是一种数据,会占用内存空间,而且下降写入效率 此处就可使用联合索引, 联合索引最重要的是顺序 按照最左匹配原则 应该将区分度高的放在左边 区分度低的放到右边 #删除其余索引 drop index name_index on usr; drop index email_index on usr; #联合索引 create index mul_index on usr(email,name,gender,id); # 查询测试 select count(*) from usr where name = "xx" and id = 1 and email = "xx"; 只要语句中出现了最左侧的索引(email) 不管在前在后都能提高效率 drop index mul_index on usr;
over