二十、mysql数据库

二十、mysql数据库

一、mysql数据库的基础知识

一、什么是数据

  描述事物的符号记录称为数据,描述事物的符号既可以是数字,也可以是文字,图片,图像,声音,语言等,数据由多种表现形式,它们都可以经过数字化后存入计算机

二、什么是数据库

  数据库就是存放数据的仓库,而且数据是按一定的格式存放的,数据库是长期存放在计算机内,有组织,可共享的数据

  最原始的数据存放就是文件操作

  文件操作的一些缺陷:

    1.IO操作多,效率不高

    2.多用户会竞争数据

    3.需要网络来访问

    4.用户登入要验证

三、常见数据库

  关系型:数据之间存在某种关联关系,具有表结构

    Oracle:目前最好的关系型数据库,体现在用户管理,分布式,商业用途收费

    mysql:免费开源,功能支持没有Oracle强,但是可以满足中小企业使用,先后被sun和Oracle收购,mysql创始人担心mysql会闭源,于是另起炉灶,创建了Mariadb,Mariadb的使用和mysql方法一模一样

    sqlserver:属于微软生态链,需要和Windows配合使用

    DB2:IBM开发的大型关系型数据库,收费的,通常与硬件捆绑销售

  非关系型:数据以key=value的形式来存储,数据存在内存中,速度快,没有表结构

    MongoDB

    Redis

    memercach

四、数据库的相关概念

  age=18,一个变量无法描述清楚数据

  1.单个数据,称之为一个字段

  age=18,name=maple,gender=male 一堆变量在一起,可以描述清楚数据

  2.这一堆数据,称之为一条记录,对应着文件中的一行

  文件需要分门别类,每个文件存储不同的数据

  3.一个文件称之为表

  文件越来越多,就得分文件夹

  4.一个文件夹称之为库

  数据库中包含所有内容:

  字段,记录,表,库,DBMS,数据库服务器

  数据库本质上就是一套C/S架构的socket软件

五、安装数据库与配置使用环境

  1.代码 界面:傻瓜式安装,记住密码

  2.绿色解压版:

    1.5.7版本以后需要先初始化:mysqld --initialize-insecure

    2.启动需要先进入安装目录,比较麻烦,需要添加到环境变量,打开环境变量,将mysql安装目录下的bin添加到path环境变量

    3.将mysql加入到Windows的系统服务中

六、绿色版修改默认密码

  注意:每次修改密码成功后,需要重启服务器

  修改默认密码需要先登入成功

  执行update语句直接修改user表中的密码

  5.7版本

  update mysql.user set authentication_string = password("需要修改的密码") where user = "root";

  5.6版本

  update mysql.user set password = password("需要修改的密码") where user = "root";

  修改密码方式2 :不需要登入,需要知道旧密码

  mysqladmin -uroot -p旧密码 password 新密码

 

  破解密码,当忘记密码时,无法登入,也就不能直接执行update语句

  破解思路:

    1.启动服务器时,控制它不要加载授权表

    2.无密码登入服务器

    3.执行update mysql.user set authentication_string = password("需要修改的密码") where user = "root"

    4.重启服务器

  用到的系统指令:

  1.mysqld直接运行服务器程序

  2.mysql --skip-grant-tables跳过授权表,用于重设密码

  3.tasklist | findstr mysqld  taskkill /f /pid 结束服务器程序

  4.mysql 运行客户端程序 -u用户名 -p密码 -h主机地址 -P端口号

  5.mysqld --install  将mysqld注册当windows服务中  在服务中叫MySQL

  Windows就是绑定了一个exe程序  

  sc delete mysql 删除windows服务

  exit 退出客户端

 

  5.7界面版,破解密码方法:

  1.在my.ini文件的[mysqld]下添加一行skip-grant-tables

  2.重启服务器

  3.直接进入mysql修改密码

七、简单的sql语句

  *****针对库的相关操作

    增:

      create database 库名称

    删:

      drop database 库名称

    改:

      alter database 库名称 要改的属性名称

      alter database 库名称  DEFAULT CHARACTER SET utf8;

      alter database 库名称 CHARSET utf8;

      注意:在mysql中 utf8 不能写成utf-8

    查:show databases;查看所有数据库

      show create database 库名称; 查看建库的语句

    命名规范:

      大致和python命名规范相同,不区分大小写

      1.不能使用纯数字

      2.可以是数字,字母,下划线的组合

      3.可以下划线开头

      4.不能是关键字,如create

  *****针对表相关的操作:

    增:

      建表时要明确数据库

      use 数据库名

      create table 表名称(字段名 类型(长度),。。。)

      create table dog(nikename char(10)),gender char(10),age int);

      创建时同时制定数据库

      create table 库名称.表名称(字段名 类型(长度),。。。)

    删:

      drop table 表名;

    改:

      alter table 表名 drop|change|modify|add

      drop 字段名称:alter table dog drop color;

      change 旧的字段名 新的字段名 新的类型:alter table dog change gender sex  char(2);

      modify 字段名 新的类型:alter table dog modify color char(5);

      add 字段名称 类型:alter table dog add color char(10);

      重命名表:

      rename table 旧表名称 to 新表名称:rename table dog to dogtable;

      修改表的属性:

      alter table 表名 属性名 值:alter table dogtable DEFAULT CHARSET gbk;

    查:

      show tables;查看所有表

      desc 表名称;查看表结构

      show create table 表名;查建表语句

  *****针对记录的相关操作:

    增:

      insert into 表名 values(值1,值2.。。。)

    删:

      delete from 表名 where 字段名称=值

      没有条件的话删除全部数据

    改:

      update 表名  set 字段名 =新的值 where 标识字段名(通常是name) = 值

      没有条件的话修改全部值

    查:

      select *from 表名;*表示通配符,查看所有字段

      select 字段名称1,字段名称2.。。。from 表名;

八、修改默认编码

  配置文件放在安装路径根目录中,就是和bin同级的,名称必须叫my.ini

  客户端的配置:

  [client]

  这里添加客户端的配置

  [mysql]

  这里也是添加客户端的配置

  #如初始默认有账户密码

  user=root

  password=root

  #默认的客户端编码

  default-character-set=utf8

  [mysqld]

  #这里是服务端的配置

  #默认的服务端编码

  character-set-server = utf8

  注意5.7界面版的,需要在bin同级目录和data同级目录都添加上my.ini文件

九、5.6版本和5.7版本的区别

  1、5.7的绿色版需要初始化

  2、5.6游客模式:没有密码也可以登入,但是无法操作数据,只能看到部分数据库

  3、5.7绿色版中data数据存放目录是由初始化决定的,5.6ban就在安装目录中

  4、密码存储字段名,在5.6中时password,而5.7中时authentication_string

二、更多详细SQL语句 

1、详细的建表语句

  create table 表名称(字段名 数据类型[(长度) 约束条件])

  []代表可选的

  给数据分类的原因:

    1.描述数据更加准确

    2.节省内存空间

2、数据类型

  1.整数类型:

    默认有符号的

    设置为无符号的

      1.create table 表名称(字段名 tinyint unsigned);

      2.建表后用alter修改:alter table 表名称 modify 字段名 属性值;

  注意:对于整数类型而言长度不是数据所占的字节数,是现实数据时的宽度(字符数),默认情况下,存储数值的十进制位数小于所设置的显示宽度时,不会填充,没有任何效果。当数据的十进制位长度大于显示宽度时,可以正常显示

  整数类型的具体分类:

    tinyint:1个字节,最小整数型

    smallint:2个字节,小整数型

    mediumint:3个字节,中整数型

    int:4个字节,整数型(常用)

    bigint:8个字节,大整数型

    总结区别:除了存储范围的不同,其他都一样,都是整型,默认有符号,显示宽度原理也相同

  2.浮点类型:

    浮点类型分类:

    1.float:4字节

    2.double:8字节

    3.decimal :不固定字节

    语法:

    create table 表名称(字段名 float(m,d)

    括号中的m和d,可以限制数据存储范围,与整型不同

    m表示总长度,d表示小数部分的长度,长度的表示不是数据存储范围,而是字符长度,例如:10.12  总长为4  小数部分为2

    各个类型的最大长度:

    float:(255,30)

    double:(255,30)

    decimal:(65,30)

    区别:

      float与double的进度不同,都是不准确的小数

      decimal是精准小数,不会丢失精度

    具体使用哪种类型的根据使用场景判读,float能满足大部分使用场景,decimal适合银行系统,科学研究等

  3.字符串类型:

    常用的两种:

    char:定长字符串

    varchar:可变长度字符串

    注意字符串中长度制定的是数据的字符长度,与字节没关系

    create table 表名称(字段名 char,字段名 varchar(10));

    在创建时varchar必须指定长度,char有默认值,默认值为1

    不同点:

      a char(3)   b char(3)

      A 3个空格,B3个空格

      char类型在取数据时,就是根据长度来获取的,不关心真实数据长度,无论数据有多长,占用的空间是固定的,造成了一定的存储空间浪费

      a varchar(30) b varchar(30)

      (1)A                (2)AB

      varchar类型在取数据时,先获取数据长度,在根据长度获取真实数据,需要计算数据的真实长度,所以需要先在存储数据前加一个字节用来计算长度,再存储真实数据,不会浪费存储空间,但是由于需要计算数据的长度,所以存取速度比定长类型的慢

    相同点:

      括号中的数字,都是表示存储最大字符长度

  4.枚举:enum,可以指定一堆字符串的值,在插入数据时,数据必须是这堆字符串中的某一个,多选一

  5.集合:set,可以指定一堆字符串的值,在插入数据时,数据必须在这堆字符串中的某一个或多个,多选多

    set和enum的共同点:数据都是字符串类型

三、严格模式

  1.什么是严格模式:

    对插入的数据严格要求,不在范围内的直接报错,例如往tinyint类型中插入大于255的数字

  2.什么是非严格模式:

    不对插入的数据严格要求,不在范围内也可以保存,保存的是当前类型最大支持的值

  5.6默认是非严格模式

  5.7默认是严格模式

  查看SQL模式:

    select @@sql_mode;

    show varchar like 'sql_mode';

  修改sql模式:

    set @@sql_mode=‘值’;

    正常情况不需要修改

四、日期和时间

  常用的关键字:

    year:年份

    time:时间

    date:日期

    datetime:日期时间

    timestamp:日期时间

  timestamp的特点:可以给null自动输入当前时间,当这条记录被修改了会自动更新当前时间

*****注意:时间格式都必须加引号

datetime 和 timestam的区别
8字节          4字节
1001-9999     1970-2038
默认null 默认为当前时间 并且 update语句会自动更新为当前时间
以上所有类型都可以使用now() 函数来输入当前时间
在插入数据时 都可以使用字符串的方式来插入
insert into t8 value(now());

 

  

五、约束

  约束就是一种对数据的限制

  例如:数据类型 unsigned无符号,字符串长度,浮点的长度

  约束的作用:

    为了保证数据的正确性,完整性

    例如:要存储密码char(20)只能限制类型和长度,无法保证数据的正确性

  额外的约束语法:

    创建时指定约束:

    create table 表名称(字段名 类型(长度) 约束名称1 约束名称n,......);

    后期修改的方式添加约束:

    alter table 表名称 modify 字段名 类型(长度) 约束名称1 约束名称n,....);

  常用的约束:

    NOT NULL:非空约束,限制该字段的值不能为空

    UNIQUE:唯一性约束,限制该字段的值是唯一的不能出现重复

    DEFAULT:默认值约束,如果插入数据时没有指定该字段的值,则使用默认设置的值

    PRIMARY KEY:主键约束,限制该字段不能为空,并且是唯一的,可以唯一标识一条数据

    FOREIGN KEY:外键约束,用来指向另一个表的主键

  注意点:

  1.每一个表应该有一个主键,需要唯一标识,否则可能出现完全相同的两个数据,无法区分

  2.UNIQUE 只能约束不能重复,但是可以为空,这样也不能唯一标识

  3.UNIQUE NOT NULL 不能为空且唯一,可以唯一标识一条数据,约束的前后顺序没有关系

  UNIQUE NOT NULL 与主键约束的区别:

  1.UNIQUE NOT NULL 不能被其他表引用(不能作为其他表的外键)

  2.UNIQUE NOT NULL 约束一个表中可以有多个,但是主键只能有一个

  撤销约束名:

    撤销UNIQUE约束:alter table 表名称 drop index 字段名;

    撤销PRIMARY KEY约束:alter table 表名称 drop PRIMARY KEY;

    撤销FOREIGN KEY约束:alter table 表名称 drop FOREIGN KEY 字段名;

    撤销NOT NULL 约束:alter table 表名称 modify 字段名 数据类型 null;

六、初识索引

  1.索引用于加速查询

  2.InnoDB中,索引是树形结构

    为了提高查询效率,InnoDB为的是找一个不为空且唯一的字段作为主键

    如果表中不存在这样的字段,会自动帮你建一个隐藏主键字段,但是无法提升查询效率

    只要是使用InnoDB就应该为每个表指定一个非空且唯一的字段

    InnoDB组织数据时,首先使用主键,如果没有主键,找一个非空且唯一的,如果也没有,那么就建一个隐藏字段

  多字段联合主键:不常用

    例如:学生表 stu_id  course_id 作为联合主键

      1    1    已有数据

      1    2    可以插入

      2    1    可以插入

      1    1    不能插入

      只有当两个字段都重复才算重复

    当一个表中,由于业务需求没有一个非空且唯一的字段时,我们可以建一个新的字段专门作为主键,从而来管理主键的值

    由于管理主键的值比较麻烦,需要记得上一次插入的主键值,mysql可以帮你自动管理主键,auto_increment自动增加主键值,所以auto_increment一般就用整型字段表示,并且该字段必须具备索引,通常都和主键一起连用

    手动修改自动增长的计数:

      alter table 表名称 auto_increment 新的值;

      注意:如果新的值小于当前的最大值,是无效的

      通常建一个表就要建一个主键,主键的类型通常是整型

  insert 语句的其他用法:

    insert into 表名(字段名1,字段名n,。。。)values(值1,值n,。。。)

    可以选择行得插入某些字段,要求值的顺序必须与表名后面声明的字段名一致

七、引擎

  引擎:一个产品或服务的核心部分称之为引擎

  mysql的核心功能就是存取数据

  mysql存储引擎就是负责存取数据的那一段代码

  查看引擎:show engines;

  mysql的主要引擎:

    1.InnoDB:默认引擎,存储在硬盘中

    2.MRG_MYISAM:

    3.MEMORY:

    4.BLACKHOLE:数据存进去就立即消失

    5.MyISAM:

    6.CSV :

    7.ARCHIVE :

    8.PERFORMANCE_SCHEMA :

    9.FEDERATED :

  启动引擎语句:create table 表名称(字段名 类型)engine=引擎名称;

 

八、多表关联

1、多对一:

  把所有数据都存放在一张表中的弊端:

    1.表的组织结构复杂不清晰

    2.浪费空间

    3.扩展性极差

  所以需要表与表之间的关系来解决问题

  例如:

  

create table dept(id int primary key auto_increment,name char(20),job char(20));
create table emp(id int primary key auto_increment,name char(20),gendr char,age int,salary float,d_id int);

以上代码可以建立关联关系,但是这个关系是逻辑上的,实际不存在,需要为他建立物理上的关联,我们可以通过外键来约束

create table 表名(字段名 类型(长度),foreign key(外键的字段名称) references 对方表名(对方主键名));

使用外键时,必须分清主从关系

外键的第一种约束:

  先建主表

  再建从表

create table dept(id int primary key auto_increment,name char(20),job char(20));
create table emp(id int primary key auto_increment,name char(20),d_id int,foreign key(d_id) references dept(id));

外键的第二个约束:

  先插入主表

  再插入从表

外键的第三个约束:

  删除记录时

  先删除从表记录

  再删除主表记录

外键的第四个约束:

  从表更新外键时,必须保证外键是存在的

外键的第五个约束:

  更新主表的id时

  必须先删除从表关联的数据

  或者把关联数据关联到其他的主表id

外键的第六个约束:

  删除主表时,要先删除从表

2、级联

有了这几个约束后,主表和从表中的数据必然是完整的,相应也会受到外键约束,当主表要删除和更新操作时就会被限制,很多情况下,我们就是要删除一个部门的表,然而需要至少2条sql语句来搞定,这时我们就可以使用级联

create table emp(
id int primary key auto_increment,
name char(20),
d_id int,
foreign key(d_id) references dept(id)
on delete cascade
on update cascade
);

on delete cascade:当主表删除记录时,从表相关联的记录同步删除

on update cascade:当主表id更新时,从表相关联的记录同步更新

注意点:单向操作,只能是操作主表影响从表,不能是操作从表想来影响主表。

3、多对多

两张表之间是一个双向的多对一关系,称之为多对多

实现多对多需要建立第三张表,该表中有一个字段关联表1,另一字段关联表2

例如:老师和学生

一个老师可以教多个学生,一个学生可以被多个老师教,老师表和学生表是多对多的关系,需要一个中间表专门存储关联关系

create table teacher(id int primary key auto_increment,name char(15));
create table student(id int primary key auto_increment,name char(15));
#中间表
create table tsr(
id int primary key auto_increment,
t_id int,s_id int,
foreign key(t_id) references teacher(id),
foreign key(s_id) references student(id)
);
#现在老师和学生 都是主表  关系表是从表
#先插入老师和学生数据
insert into teacher values
(1,"高跟"),
(2,"矮跟");

insert into student values
(1,"炜哥"),
(2,"仨疯");

# 插入对应关系
insert into tsr values
(null,1,1),
(null,1,2),
(null,2,2);

4、一对一

一对一就是左表的一条记录唯一对应右表的一条记录,反之也一样

例如:客户和学生

一个客户只能产生一个学生,一个学生只能对应一个客户,这样的关系是一对一的,使用外键来关联,但是需要给外键加上唯一约束,客户和学生有主从关系,需要先建立客户再建学生

create table customer(c_id int primary key auto_increment,
name char(20),phonenum char(11),addr char(20));
create table student1(s_id int primary key auto_increment,
name char(20),
class char(11),
number char(20),
housenum char(20),c_id int UNIQUE,
foreign key(c_id) references customer(c_id)
);

一对一的另一种使用场景:

当一个表中字段太多,而常用字段不多时,可以采取垂直分表的方式来提高效率

例如:原有的person表中有,姓名,性别,年龄,身份证,地址,名族,身高,体重,血型,学历,政治,联系方式

我们可以把原表拆分为2个,一个常用,一个不常用

person_use info:姓名,性别,年龄,联系方式

person_details info:身份证,地址,名族,身高,体重,血型,学历,政治

也有另一种提升效率的方式,水平分表

当一个表中数据记录太多时,查询效率会降低,可以采取水平分表方式,字段完全相同,就是表名不同

总结:在日常开发中,如果性能要求贼高,不应该使用外键

  1.效率会降低

  2.耦合关系多起来,以后的管理会麻烦

  3.很有可能产生错误数据

5、清空表

delete from 表名称;

该条命令确实可以将表里的所有记录都删除,但是不会将id重置为0,所以该条命令根本不是用来清空表的,而是用来删除表中某些符合条件的记录

例如:delete from T1 where id>10;

如果要清空表,就使用truncate T1;将整张表重置,它的执行过程是先记录表结构,再删除整个表再重新建出新表,自增的id会归零

6、补充:表的修改,复制,蠕虫复制

1.修改表:

  add:添加字段 after,first

    after:添加字段到指定字段后面

    first:添加字段到最前面

  modify:修改字段类型

  change:修改字段名称或类型

  drop:删除字段

  rename:改表名

2.复制表:

  create table 新的表名 select *from 原表名;

    只复制数据,结构,不能复制约束

  当条件不成立时,只是复制表结构

  create table 新的表名 select *from 原表名 where 1=2;

3.蠕虫复制:

  本质就是自我复制

  insert into 表名称 select *from 自己的表名称;

  如果有主键,避开主键字段

  insert into 表名称(其他字段)select 其他字段 from 表名称;

九、表查询

1、不带关键字的查询

select {1.*|2.字段名|3.四则运算|4.聚合函数} from 表名 [where 条件]

1.表示查询所有字段

2.可以手动查询需要的字段

3.字段的值可以进行加减乘除运算

4.聚合函数,用于统计数据

select 的完整语法:

例如:

create table stu(id int primary key auto_increment,name
char(10),math float,english float);
insert into stu values(null,"赵云",90,30);
insert into stu values(null,"小乔",90,60);
insert into stu values(null,"小乔",90,60);
insert into stu values(null,"大乔",10,70);
insert into stu values(null,"李清照",100,100);
insert into stu values(null,"铁拐李",20,55);
insert into stu values(null,"小李子",20,55);

2、关键字的作用

distinct:去除重复数据,所有数据全部都重复才算重复

where:在逐行读取数据时的一个判断条件

group by:对数据分组

having:对分组后的数据进行过滤

order by:对结果排序

limit:指定获取数据条数

distinct:去除重复数据,所有数据全部都重复才算重复
select distinct name,math,english from stu ;
where:在逐行读取数据时的一个判断条件
select *from stu where math=90;
group by:对数据分组,group by后面跟的字段名需要与from前面的字段名一一对应,原因是group by会把分组以外的字段名数据都隐藏起来
select math from stu group by math;
注意:
1.只有出现在group by 后面的字段 才可以被显示 其他都被影藏了
2.聚合函数不能写在where的后面,where最先执行,它的作用硬盘读取数据并过滤,以为数据还没有读取完,此时不能进行统计

聚合函数:
sum
count
avg
max
min
having:对分组后的数据进行过滤
select math from stu group by math having math>90;
order by:对结果排序
select *from stu order by math;

order by 排序用的
asc 表示升序 是默认的
desc 表示降序
by 后面可以有多个排序依据
limit:指定获取数据条数
select *from stu limit 0,2;

limit a,b
limit 1,5
从1开始 到5结束 错误
从1开始 不包含1 取5条

分页查询
每页显示3条 共有10条数据
if 10 % 3 == 0:
10 / 3
else:
10/3 +1
总页数4

第一页
select *from emp limit(0,3)
第二页
select *from emp limit(3,3)
第二页
select *from emp limit(6,3)

起始位置的算法
页数 - 1 * 条数
1 - 1 = 0 * 3 = 0
2 - 1 = 1 * 3 = 3

 

完整的select 语句 语法:
select [distinct] * from 表名
[where
group by
having
order by
limit
]

注意在书写时,必须按照这个顺序来写,但是书写顺序不代表执行顺序

数据库的执行顺序伪代码:

def from():
    打开文件
def where():
    对读取的数据进行过滤

def group_by():
    对数据分组

def having():
    对分组后的数据进行过滤

def distinct():
    去除重复数据

def order():
    排序

def limit():
    指定获取条数

select 语句的执行顺序
def select(sql):
    data = from()
    data = where(data)
    data = group by(data)
    data = having(data)
    data = distinct(data)
    data = orderby(data)
    data = limit(data)
    return data;

指定显示格式:

concat()函数用于拼接字符串

select
    (
    case
    when english + math > 120 then
    concat(name," nice")
    when english + math <= 130 then
    concat(name," shit")
    end
) ,english,math from stu;

where约束:

1. 比较运算符:> < >= <= <> !=

2. between 80 and 100 值在10到20之间

3. in(80,90,100) 值是10或20或30

4. like 'egon%' pattern可以是%或_, %表示任意多字符 _表示一个字符

5. 逻辑运算符:在多个条件直接可以使用逻辑运算符 and or not

聚合函数:

#计算函数
SELECT COUNT(*) FROM 表名;
#计算后加条件过滤
SELECT COUNT(*) FROM 表名 WHERE 字段名=1;
#求最大值
SELECT MAX(salary) FROM 表名;
#求最小值
SELECT MIN(salary) FROM 表名;
#求平均值
SELECT AVG(salary) FROM 表名;
#求总合
SELECT SUM(salary) FROM 表名;
#求总合加条件过滤
SELECT SUM(salary) FROM 表名 WHERE 字段名=3;

 

了解:

在mysql 5.6中 分组后会默认显示 每组的第一条记录 这是没有意义的,
在5.7中不显示 因为5.7中sql_mode中自带ONLY_FULL_GROUP_BY group by 后面可以有多个分组与依据 会按照顺序执行

 

3、正则表达式匹配

由于like只能使用%和_不太灵活

可以将like换为regexp 来使用正则表达式

regexp正则表达式匹配
select *from stu where math regexp '9';
模式  描述
^   匹配输入字符串的开始位置。
$   匹配输入字符串的结束位置。
.   匹配任何字符(包括回车和新行)
[...]   字符集合。匹配所包含的任意一个字符。例如, '[abc]' 可以匹配 "plain" 中的 'a'。
[^...]  负值字符集合。匹配未包含的任意字符。例如, '[^abc]' 可以匹配 "plain" 中的'p'。
p1|p2|p3    匹配 p1 或 p2 或 p3。例如,'z|food' 能匹配 "z""food"'(z|f)ood' 则匹配 "zood""food"# ^  匹配 name 名称 以 "e" 开头的数据
select * from person where name REGEXP '^e';
  
# $  匹配 name 名称 以 "n" 结尾的数据
select * from person where name REGEXP 'n$';
  
# . 匹配 name 名称 第二位后包含"x"的人员 "."表示任意字符
select * from person where name REGEXP '.x';
  
# [abci] 匹配 name 名称中含有指定集合内容的人员
select * from person where name REGEXP '[abci]';
  
# [^alex] 匹配 不符合集合中条件的内容 , ^表示取反
select * from person where name REGEXP '[^alex]';
#注意1:^只有在[]内才是取反的意思,在别的地方都是表示开始处匹配
#注意2 : 简单理解 name  REGEXP '[^alex]' 等价于 name != 'alex'
  
# 'a|x' 匹配 条件中的任意值
select * from person where name REGEXP 'a|x';  
  
#查询以w开头以i结尾的数据
select * from person where name regexp '^w.*i$';
#注意:^w 表示w开头, .*表示中间可以有任意多个字符, i$表示以 i结尾

 

4、多表查询

例如:

#数据准备
create table emp (id int,name
char(10),sex char,dept_id int);
insert emp values(1,"大黄","m",1);
insert emp values(2,"老王","m",2);
insert emp values(3,"老李","w",30);

create table dept (id int,name char(10));
insert dept values(1,"市场");
insert dept values(2,"财务");
insert dept values(3,"行政");

1、笛卡尔积查询

select *from 表1,表n

查询结果是:

将左表中的每条记录与右表中的每条记录都关联一遍,因为它不知道什么样的对应关系是正确的,只能帮你全都对一遍

如果a表有m条记录,b表有n条记录

笛卡尔积结果为m*n记录

需要自己筛选出正确的关联关系

select *from emp,dept where emp.dept_id = dept.id;

2、内连接查询,就是笛卡尔积查询

select *from emp [inner] join dept;
select *from emp inner join dept where emp.dept_id = dept.id;

3、左外连接查询

select *from emp left join dept on emp.dept_id = dept.id;
左表数据全部显示   右表只显示匹配上的

4、右外连接查询

select *from emp right join dept on emp.dept_id = dept.id;
右表数据全部显示   左表只显示匹配上的

内和外的理解:内指的是匹配上的数据,外指的是没有匹配上的数据

5、全外连接

select *from emp full join dept on emp.dept_id = dept.id;  注意:此full语句mysql不支持
union 合并查询结果
select *from emp left join dept on emp.dept_id = dept.id
union
select *from emp right join dept on emp.dept_id = dept.id;

union 去除重复数据 ,只能合并字段数量相同的表
union all 不会去除重复数据

on 关键字:

在单表中where的作用是筛选过滤条件,在多表中where是连接多表,满足条件就连接,不满足就不连接,为了区分是单表还是多表,新增加了一个名字就是on,只要是连接多表条件的就使用on

三表查询:

例如:

create table stu(id int primary key auto_increment,name char(10));
create table tea(id int primary key auto_increment,name char(10));
create table tsr(id int primary key auto_increment,t_id int,s_id int,
foreign key(s_id) references stu(id),
foreign key(s_id) references stu(id));
insert into stu values(null,"张三"),(null,"李四");
insert into tea values(null,"egon"),(null,"wer");
insert into tsr values(null,1,1),(null,1,2),(null,2,2);

select *from stu join tea join tsr
on stu.id = tsr.s_id and tea.id = tsr.t_id
where tea.name = "egon";

多表查询的套路:

1.把所有表都连起来

2.加上连接条件

3.如果有别的过滤条件加上where

5、子查询

当一个查询的结果是另一个查询的条件时,这个查询称之为子查询(内层查询)

什么时候使用子查询:

  当一个查询无法得到想要的结果时,需要多次查询

解决问题的思路:

  就是把一个复杂的问题,拆分为多个简单的问题

  然后把一个复杂的查询,拆分成多个简单的查询

in 关键字查询

查询平均年龄⼤于25的部⻔名称
⼦查询⽅式:
平均年龄⼤于25的部⻔id有哪些?
先要求出每个部⻔的平年龄! 筛选出平均年龄⼤于25的部⻔id
拿着部⻔id 去查询部⻔表查询
select name from dept where id in (select dept_id from emp group by
dept_id having avg(age) > 25);

 

exists 关键字⼦查询
exists 后跟⼦查询 ⼦查询有结果是为True 没有结果时为False
为true时外层执行 为false外层不执⾏行
select *from emp where exists (select *from emp where salary > 1000);

 

6、在mysql中的操作

mysql> select *from dept,emp;
+------+--------+------+--------+------+---------+
| id   | name   | id   | name   | sex  | dept_id |
+------+--------+------+--------+------+---------+
|    1 | 市场   |    1 | 大黄   | m    |       1 |
|    2 | 财务   |    1 | 大黄   | m    |       1 |
|    3 | 行政   |    1 | 大黄   | m    |       1 |
|    1 | 市场   |    2 | 老王   | m    |       2 |
|    2 | 财务   |    2 | 老王   | m    |       2 |
|    3 | 行政   |    2 | 老王   | m    |       2 |
|    1 | 市场   |    3 | 老李   | w    |      30 |
|    2 | 财务   |    3 | 老李   | w    |      30 |
|    3 | 行政   |    3 | 老李   | w    |      30 |
+------+--------+------+--------+------+---------+
9 rows in set (0.01 sec)

mysql> select *from dept;
+------+--------+
| id   | name   |
+------+--------+
|    1 | 市场   |
|    2 | 财务   |
|    3 | 行政   |
+------+--------+
3 rows in set (0.00 sec)

mysql> select *from emp;
+------+--------+------+---------+
| id   | name   | sex  | dept_id |
+------+--------+------+---------+
|    1 | 大黄   | m    |       1 |
|    2 | 老王   | m    |       2 |
|    3 | 老李   | w    |      30 |
+------+--------+------+---------+
3 rows in set (0.00 sec)

mysql> select *from dept,emp where dept.id=emp.id;
+------+--------+------+--------+------+---------+
| id   | name   | id   | name   | sex  | dept_id |
+------+--------+------+--------+------+---------+
|    1 | 市场   |    1 | 大黄   | m    |       1 |
|    2 | 财务   |    2 | 老王   | m    |       2 |
|    3 | 行政   |    3 | 老李   | w    |      30 |
+------+--------+------+--------+------+---------+
3 rows in set (0.00 sec)

mysql> select *from dept join emp on dept.id=emp.id;
+------+--------+------+--------+------+---------+
| id   | name   | id   | name   | sex  | dept_id |
+------+--------+------+--------+------+---------+
|    1 | 市场   |    1 | 大黄   | m    |       1 |
|    2 | 财务   |    2 | 老王   | m    |       2 |
|    3 | 行政   |    3 | 老李   | w    |      30 |
+------+--------+------+--------+------+---------+
3 rows in set (0.00 sec)

mysql> select *from dept join emp on dept.id=emp.dept_id;
+------+--------+------+--------+------+---------+
| id   | name   | id   | name   | sex  | dept_id |
+------+--------+------+--------+------+---------+
|    1 | 市场   |    1 | 大黄   | m    |       1 |
|    2 | 财务   |    2 | 老王   | m    |       2 |
+------+--------+------+--------+------+---------+
2 rows in set (0.00 sec)

mysql> select *from dept left join emp on dept.id=emp.dept_id;
+------+--------+------+--------+------+---------+
| id   | name   | id   | name   | sex  | dept_id |
+------+--------+------+--------+------+---------+
|    1 | 市场   |    1 | 大黄   | m    |       1 |
|    2 | 财务   |    2 | 老王   | m    |       2 |
|    3 | 行政   | NULL | NULL   | NULL |    NULL |
+------+--------+------+--------+------+---------+
3 rows in set (0.00 sec)

mysql> select *from dept right join emp on dept.id=emp.dept_id;
+------+--------+------+--------+------+---------+
| id   | name   | id   | name   | sex  | dept_id |
+------+--------+------+--------+------+---------+
|    1 | 市场   |    1 | 大黄   | m    |       1 |
|    2 | 财务   |    2 | 老王   | m    |       2 |
| NULL | NULL   |    3 | 老李   | w    |      30 |
+------+--------+------+--------+------+---------+
3 rows in set (0.00 sec)

mysql> select *from dept right join emp on dept.id=emp.dept_id
    -> union
    -> select *from dept left join emp on dept.id=emp.dept_id;
+------+--------+------+--------+------+---------+
| id   | name   | id   | name   | sex  | dept_id |
+------+--------+------+--------+------+---------+
|    1 | 市场   |    1 | 大黄   | m    |       1 |
|    2 | 财务   |    2 | 老王   | m    |       2 |
| NULL | NULL   |    3 | 老李   | w    |      30 |
|    3 | 行政   | NULL | NULL   | NULL |    NULL |
+------+--------+------+--------+------+---------+
4 rows in set (0.02 sec)

 

十、mysql用户管理

1、数据库的安全非常重要,不可能随便分配root账户,应该按照不同开发岗位的需求分配不同的账户和权限,在mysql中,将用户相关的数据存放在mysql库中:

  账户和权限的存放文件顺序:

  user——》db——》tables_priv——》columns_priv

  如果用户拥有对所有库的访问权:存放在user文件中

  如果用户拥有对部分库的访问权:存放在db文件中

  如果用户拥有对部分表的访问权:存放在tables_priv文件中

  如果用户拥有对表中的某些字段的访问权:存放在columns_priv文件中

2、创建新账户

  语法:

  

create user "账户名"@"主机名" identified by 密码

create user "tom"@"localhost" identified by "123";

授予所有数据库所有表的所有权限给maple这个用户,并允许maple在任意一台电脑上登入:

如果用户在不存在会自动创建

grant all on *.* to "maple"@"%" identified by  "123" with grant option;
with grant option:这个用户可以将拥有的权限授予别人
授予db1数据库所有表的所有权限给jack这个用户  并允许maple在任意一台电脑登录:
grant all on  db1.* to "maple"@"%" identified by  "123";
授予db1数据库的emp表的所有权限给maple这个用户  并允许maple在任意一台电脑登录:
grant all on  db1.emp to "maple"@"%" identified by  "123";
授予db1数据库的emp表的name字段的查询权限给maple这个用户  并允许maple在任意一台电脑登录:
grant select(name) on  db1.emp to "maple"@"%" identified by  "123";

3、收回权限

  语法:

REVOKE all privileges [column] on db1.table from user@"host";

如何授权就如何收回 因为不同权限信息存到不同的表中
REVOKE all privileges  on db1.emp from maple@"%";

立即刷新权限信息
flush privileges;

4、删除用户

  语法:

drop user 用户名@主机
drop user maple@%

当你在云服务器部署了 mysql环境时,你的程序无法直接连接到服务器 ,需要授予在任意一台电脑登录的权限
grant all on *.* to "maple"@"%" identified by  "123" with grant option;

十一、pymysql

后期开发中都是用框架代替在cmd中写mysql语句,所有我们以后要学习pymysql模块

使用pymysql使我们能方便的在程序中连接数据库,然后进行增删改查的擦作

 1 import pymysql
 2 
 3 conn=pymysql.connect(host='127.0.0.1',
 4                      port=3306,
 5                      user='root',
 6                      password='123',
 7                      database='db2',
 8                      charset='utf8')
 9 # cursor 游标对象 负责执行sql语句 获取返回的数据
10 # pymysql.cursors.DictCursor指定使用字典类型的游标 默认是元祖类型
11 cursor=conn.cursor(pymysql.cursors.DictCursor)
12 
13 sql='select *from emp2;'
14 
15 # 返回值是本次查询的记录条数
16 res=cursor.execute(sql)
17 print(res)
18 # 提取一条结果,游标移动到第二条记录前
19 res=cursor.fetchone()
20 print(res)
21 # 提取所有结果,游标移动到最后
22 res=cursor.fetchall()
23 for line in res:
24     print(line)
25 
26 # 提取指定条数结果,游标移动到指定条数后
27 res=cursor.fetchmany(3)
28 print(res)
29 
30 # 游标从当前位置往前移动1条记录
31 cursor.scroll(-1,mode='relative')
32 
33 # 游标从开始位置往后移动1条记录
34 cursor.scroll(1,mode="absolute")
35 
36 # 增删改pymysql不会自动提交 ,对数据的修改不会持久化 需要手动commit
37 conn.commit()

 

#防止sql注入攻击
import pymysql

conn = pymysql.connect(
    host="127.0.0.1",
    port=3306,
    user="root",
    password="123",
    database="db2",
    charset="utf8"
)
# cursor 游标对象 负责执行sql语句 获取返回的数据
# pymysql.cursors.DictCursor指定使用字典类型的游标 默认是元祖类型
cursor = conn.cursor(pymysql.cursors.DictCursor)

name = input("输入用户名:")

pwd = input("输入密码:")

sql = "select *from user where name = %s and password = %s"
res = cursor.execute(sql,(name,pwd))
if res:
    print("登录成功")
else:
    print("登录失败")

# 什么是sql注入攻击 一些了解sql语法的攻击者  可以通过一些特殊符号 来修改 sql执行逻辑 达到绕过验证的效果
# 避免的方式 1.在输入时加上正则判断 不允许输入与sql相关的关键字 这种方式 无法避免 代理服务器发起的攻击
# 2.在服务器端 执行sql前先来一波判断
# pymysql中已经帮你做了处理  只要将参数的拼接交给pymysql来完成就能够避免攻击

 

十二、视图

1、什么是视图

  视图是由一张表或多张表的查询结果构成的一张虚拟表

2、为什么使用视图

  当我们在使用多表查询时,我们的sql语句可能会非常的复杂,如果每次都编写以便sql语句的话,无疑是一件麻烦的事情,这时候就可以使用视图来避免多次编写sql的问题

  简单的说可以帮我们节省sql的编写

  视图的另一个作用是:可以用不同的视图来展示开放不同数据的访问

  例如:

    同一张工资表,老板可以查看全部,部门主管可以查看该部门所有人,员工只能看自己的一条记录

3、使用方法

  创建视图

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来的方便

十三、触发器

1、什么是触发器

  触发器是一段与表有关的mysql程序,当这个表在某个时间点发生了某种事件时,将会自动执行相应的触发器程序

2、何时使用触发器

  当我们想要在一个表记录被更新时,做一些操作时就可以使用触发器

  但是我们完全可以在python中来完成这个事情,因为python的扩展性更强,语法更简单

3、创建触发器

  

语法:
CREATE TRIGGER t_name t_time t_event ON table_name FOR EACH ROW
begin
stmts.....
end

  支持的时间点t_time:时间发生前和发生前后before|after

  支持的事件t_even: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语句,他们需要使用分号来结束,但是触发器是一个整体,所有我们需要先更换默认的结束符,在触发器编写完后再将结束符设置回分号

  注意:外键不能触发事件,主表删除了某个主键,从表也会相应删除,但是并不会执行触发器,触发器中不能使用事务,相同时间点的相同事件的触发器不能同时存在

4、删除触发器

  

语法:
drop trigger trigger_name;
案例:
drop trigger trigger1;

  同样的这种需求我们完全可以在python中来完成,mysql最想完成的事情是将所有能处理的逻辑全部放到mysql中,那样一来应用程序开发者的活儿就变少了,相应的数据库管理员的工资就高了,可惜大多中小公司都没有专门的DBA

十四、事务

1、什么是事务

  事务是逻辑上的一组操作,要么都成功,要么都失败

2、为什么需要事务

  很多时候一个数据操作,不是一个sql语句就能完成的,可能有很多个sql语句,如果部分sql执行成功而部分sql执行失败将导致数据错乱

  例如转账操作:

    1.从原有账户减去转账金额

    2.给目标账户加上转账 金额

    若中间突然断电了或系统崩溃了,钱就不翼而飞了

3、使用事务

  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中需要使用存储过程才能捕获异常

4、事务的四个特性

  1.原子性:

  事务是一组不可分割的单位,要么同时成功,要么同时失败

  2.一致性:

  事务前后的数据完整性因该保持一致,(数据库的完整性:如果数据库在某一时间点下,所有的数据都符合所有的约束,则称数据量为完整性的状态)

  3.隔离性:

  事务的隔离性是指多个用户并发访问数据时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离

  4.持久性:

  持久性是指一个事务一旦被提交,它对数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响

5、事务的用户隔离级别

  数据库使用者可以控制数据库工作在哪个级别下,就可与防止不同的隔离性问题

  1.read uncommitted 不做任何隔离,可能脏读,幻读

  2.read committed 可以防止脏读,不能防止不可重复读,和幻读

  3.repeatable read 可以防止脏读和不可重复读,不能防止幻读

  4.serializable 数据库运行在串行化,所有问题都没有了,就是性能低

6、修改隔离级别

#查询当前级别
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可以代替应用程序完成数据相关的逻辑处理,但是有缺点,请看下面的对比

3、三种开发方式对比

  1.应用程序仅负责业务逻辑编写,所有与数据相关的逻辑都交给mysql来完成,通过存储过程

  优点: 

  应用程序与数据处理完全解耦合,一堆复杂的sql被封装成了一个简单的存储过程,考虑到网络环境因素,效率高,应用程序开发者不需要编写sql语句,开发效率高

  缺点:

  python语法与mysql语法区别巨大,学习成本高,并且各种数据库的语法大不相同,所以移植性非常差,应用程序开发者与BDA的库部门沟通成本也高,造成整体效率低

  2.应用程序不仅编写业务逻辑,还需要编写所有的sql语句

  优点:

  扩展性高,对于应用程序开发者而言,扩展性和维护性相较于第一种都有所提高

  缺点:

  sql语句过于复杂,导致开发效率低,且需要考虑sql优化问题

  3.应用程序仅负责业务逻辑,sql语句的编写交给ORM框架,(以后常用方案)

  优点:

  应用程序开发者不需要编写sql语句,开发效率高

  缺点:

  执行效率低,由于需要将对象的操作转化为sql语句,且需要通过网络发送大量sql语句

4、创建存储过程

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类参数必须是一个变量,不能是值;

 

5、在python中调用存储过程

 1 import  pymysql
 2 #建立连接
 3 conn = pymysql.connect(
 4     host="127.0.0.1",
 5     user="root",
 6     password="admin",
 7     database="db02"
 8 )
 9 # 获取游标
10 cursor = conn.cursor(pymysql.cursors.DictCursor)
11 12 # 调用用存储过程
13 cursor.callproc("p1",(70,80,0)) #p1为存储过程名 会自动为为每个值设置变量,名称为 @_p1_0,@_p1_1,@_p1_2
14 # 提取执行结果是否有结果取决于存储过程中的sql语句
15 print(cursor.fetchall())
16 # 获取执行状态
17 cursor.execute("select @_p1_2")
18 print(cursor.fetchone())

  此处pymysql会自动将参数都设置一个变量,所以可以直接传入一个值,当然值如果作为输出参数的话,传入什么都无所谓

6、删除存储过程

  drop procedure 过程名称;

  修改存储过程意义不大,不如删除重写

  查看存储过程

 #当前库所有存储过程名称
 select `name` from mysql.proc where db = 'db02' and `type` = 'PROCEDURE';
 #查看创建语句
 show create procedure p1;

7、存储过程中的事务应用

  存储过程中的支持任何的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中的定义一致

1、内置函数

  日期相关:

 

  字符相关

  数字相关

  其他函数

  当然还包括之前学习的聚合函数

  

2、自定义函数

  语法:

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语句一样

 

十七、数据备份与恢复

1、使用mysqldump程序进行备份

#db_name [table_name,,,]这里填写想要备份的库名称或表名称
#fileName.sql这里填写想要备份到的路径和sql文件名
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

2、使用mysql进行恢复

  1.退出数据库后

#filename.sql想要恢复的路径和sql文件名
mysql -u -p < filename.sql;

#这里要注意,sql文件里必须要有创建库名和使用库名的sql语句:
create database db;
use db
必须加上这两句sql语句

  2.不用退出数据库的操作:

    第一步:创建空数据库

    第二步:选择数据库

    第三步:然后使用source filename;来进行恢复

use db1;
source /root/db1.sql

3、数据库迁移

务必保证在相同版本之间迁移
# mysqldump -h 源IP -uroot -p123 --databases db1 | mysql -h 目标IP -uroot -p456

 

十八、流程控制

1、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

2、if语句的使用

  大体意思与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

3、定义变量

  declare 变量名 类型 default 值; 例如: declare i int default 0;

4、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

5、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

6、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

 

十九、详细介绍索引

1、什么是索引

  在关系型数据库中,索引是一种单独的,物理的对数据库表中一列或多列的值,进行排序的一种数据存储结构,又称之为key

  索引的作用相当于图书的目录,可以根据目录中的页码快速找到所需要的内容

2、为什么需要索引

  索引是提升查询效率最有效的方法,在数据库中插入数据会引发索引的重建,简单的说索引就是帮我们加快查询速度

3、索引的数据结构

  索引是独立于真实数据的一个存储结构,这个结构的目的是要尽可能降低io操作次数,减少查找的次数,以最少的io找到需要的数据,其实就是B+树结构,还需要二分查找法的支持

4、最左匹配原则

  由于B+树的数据是复合的数据结构,当有多个字段 被添加索引时,比如联合索引,在用查询语句时,必须使用最左索引匹配原则,这样才能起到快速查找的目的

5、聚集索引

  主键设置就是聚集索引,而除了主键之外的索引都是非聚集索引,也称为辅助索引

  聚集索引的特点:

    1.叶子节点保存的就是完整的一行记录,如果设置了主键,主键就作为聚集索引

    2.如果没有主键,则找第一个not null 且unique的列作为聚集索引

    3.如果也没有这样的列,innodb会在表内自动产生一个聚集索引,它是自增的

6、辅助索引

  除了聚集索引之外的索引都称之为辅助索引

  辅助索引的特点:

    其叶子节点保存的是索引数据与所在行的主键值,innodb用这个主键值来从聚集索引中搜查找数据

7、覆盖索引

  覆盖索引指的是需要的数据仅在辅助索引中就能找到

#假设stu表的name字段是一个辅助索引
select name from stu where name = "jack";
#这样的话则不需要在查找聚集索引数据已经找到

8、回表

  如果要查找的数据在辅助索引中不存在,则需要回到聚集索引中查找,这种现象就称回表

# name字段是一个辅助索引 而sex字段不是索引 
select sex from stu where name = "jack";
#需要从辅助索引中获取主键的值,在拿着主键值到聚集索引中找到sex的值

9、查询速度对比

  聚集索引>覆盖索引>非覆盖索引

案例:

  准备一张表数据量在百万级别

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.有多个匹配条件时 索引的执行顺序  andor
#先看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;

 

面试题

1.以下有2表,一句sql,请描述该sql执行的过程,并写出执行之后的结果

+----+---------------------+--------+--------+
| id | time                | name   | token  |
+----+---------------------+--------+--------+
|  1 | 2019-06-18 23:04:00 | 小明   | token1 |
|  2 | 2019-06-18 23:10:00 | 小白   | token2 |
|  3 | 2019-06-18 23:11:00 | 小华   | token3 |
|  4 | 2019-06-18 23:11:00 | 小红   | token4 |
|  5 | 2019-06-18 23:11:00 | 小黄   | token5 |
|  6 | 2019-06-18 23:11:00 | 小黑   | token6 |
+----+---------------------+--------+--------+
+----+---------------------+-------+--------+
| id | time                | class | token  |
+----+---------------------+-------+--------+
|  1 | 2019-06-17 23:07:00 | A1    | token1 |
|  2 | 2019-06-18 23:12:00 | A1    | token2 |
|  3 | 2019-06-18 23:12:00 | A1    | token3 |
|  4 | 2019-06-17 23:12:00 | A2    | token4 |
|  5 | 2019-06-18 23:12:00 | A2    | token5 |
|  6 | 2019-06-17 23:12:00 | A3    | token6 |
+----+---------------------+-------+--------+

SELECT
z.class,
SUM(IF(z.type = 1, 1, 0)) AS important,
SUM(IF(z.type = 0, 1, 0)) AS unimportant
FROM
(SELECT
b.class,
CASE DATE(a.time) = DATE(b.time)
WHEN TRUE THEN 1
ELSE 0
END type
FROM
a, b
WHERE
a.token = b.token) AS z
GROUP BY z.class;

 
#执行结果
+-------+-----------+-------------+
| class | important | unimportant |
+-------+-----------+-------------+
| A1    |         2 |           1 |
| A2    |         1 |           1 |
| A3    |         0 |           1 |
+-------+-----------+-------------+

 

  

 

  

 

  

 

  

        

  

  

  

  

posted @ 2018-11-19 16:25  Maple_feng  阅读(307)  评论(0编辑  收藏  举报