MySQL 进阶
关于连表
左右连表: join 上下连表: union #自动去重 (当两张表里的数据,有重复的才会自动去重) union all #不去重
#上下连表示例: select sid,sname from sname union select tid,tname from teacher select sid,sname from student UNION ALL select sid,sname from student
视图 (不常用,开发过程中不长用,在开发语句中写,不要在数据库中写)
视图是一个虚拟表(非真实存在),其本质是【根据SQL语句获取动态的数据集,并为其命名】,用户使用时只需使用【名称】即可获取结果集,并可以将其当作表来使用。
在一张物理表中,通过某种条件查询到的信息,重新生成一个新的虚拟表,这个表是通过创建时的语句,从物理表中实时拿取数据。
视图不能进行增 改 操作,视图对应的原物理表数据变更的话,视图表内的数据也会改变!
#语法 创建视图: 语法: create view 视图名 AS sql语句 例子: create view v1 as select * from teacher where tid >2; 删除视图: drop view 视图名 更改视图: 语法: alter view 视图名 AS sql语句 更改视图其实更改的就是对应的sql语句 使用视图: 使用视图时,将其当作表进行操作即可,由于视图是虚拟表,所以无法使用其对真实表进行创建、更新和删除操作,仅能做查询用。 select * from v1;
触发器 (查询不会引发此操作!)
当对某表里的某行,做增删改操作时,可在完成之前或是之后去触发执行某个操作;
当对某表里的某行,做增删改操作时,可以使用触发器自定义关联行为。
当有相关的操作时候,每执行一次相关操作,触发器也会执行一次。触发器操作,分为执行前触发和执行之后触发!
CREATE TRIGGER tri_after_update_tb1 AFTER UPDATE ON tb1 FOR EACH ROW
#语法: 增 #在执行增加操作前: create trigger 操作名字(自定义,不重复) before insert on 要操作的表名 for each row begin 函数体,执行sql操作的语句 end #在执行增加操作后: create trigger 操作名字(自定义,不重复) after insert on 要操作的表名 for each row begin 函数体,执行sql操作的语句 end 删 #在执行删除操作前: create trigger 操作名字(自定义,不重复) before drop on 要操作的表名 for each row begin 函数体,执行sql操作的语句 end #在执行删除操作后: create trigger 操作名字(自定义,不重复) after drop on 要操作的表名 for each row begin 函数体,执行sql操作的语句 end 改 #在执行更改操作前: create trigger 操作名字(自定义,不重复) before update on 要操作的表名 for each row begin 函数体,执行sql操作的语句 end #在执行更改操作后: create trigger 操作名字(自定义,不重复) after update on 要操作的表名 for each row begin 函数体,执行sql操作的语句 end
在定义触发器之前,可以先把sql语句终止时用分号触发执行的操作改成别的符号,然后再更改回来;当函数体中有多条更改语句的时候,每条语句之后需要用分号结尾。若想不与系统冲突,用 delimiter 更改sql操作终止的标点符号,修改完之后,等操作结束再改回原分号“;” 不能影响别的操作行为。例如:delimiter //
触发器举例:
delimiter //
create trigger t1 BEFORE INSERT on student for EACH ROW
BEGIN
INSERT into teacher(tname) values(NEW.sname);
INSERT into teacher(tname) values(NEW.sname);
INSERT into teacher(tname) values(NEW.sname);
INSERT into teacher(tname) values(NEW.sname);
END //
delimiter ;
insert into student(gender,class_id,sname) values('男',1,'陈XX'),('女',1,'李XX');
NEW 代表触发器外执行对应操作,新插入代码中的数据! 常用于 insert into 插入新数据,new 获取要插入的内容
OLD 代表触发器外执行对应操作,对应的原代码中要变更或是要删除的数据! 常用于 删和改 drop和update
删除触发器: drop trigger 触发器名
函数
函数执行方法:select 函数名
内置函数
时间格式化函数 date_format() 语法:date_format(具体时间,格式化操作时间结构,不同的占位符) 例如: date_format(ctime,"%Y-%m") 显示年月
自定义函数(存在于数据库中,设置好后,在python中直接调用即可)
语法: 数据类型 是强制型的,创建时指定的什么类型,就只能传入什么类型 create function 函数名( 参数1 类型1 参数2 类型2 ... ) returns 数据类型 #指定函数返回值的数据类型 begin declare(声明变量) 变量名 数据类型 default 初始值 函数体,执行sql操作的语句 end 示例: delimiter \\ create function f1( i1 int, i2 int) returns int BEGIN declare num int; set num = i1 + i2; return(num); END \\ delimiter ;
自定义函数时需注意:
函数必须有返回值;
函数内部不能写 select * from tb1 这种sql语句;
删除函数: drop function 函数名
存储过程
存储过程是一个SQL语句集合,当主动去调用存储过程时,其中内部的SQL语句会按照逻辑执行。
存储过程 可以写SQL语句!!!
是保存在MySQL上的一个别名,他代指着很多的SQL语句操作,只要使用这个别名就能查到结果!用于替代程序员写SQL语句。为了职责更明确,可以把SQL语句放在程序里边,有以下几种操作方式:
方式一: MySQL: 存储过程 程序:调用存储过程 方式二: MySQL:。。(不操作) 程序:SQL语句 方式三: MySQL:。。(不操作) 程序:类和对象(SQL语句)(ORM框架)
创建存储过程:
语法: 简单类型: delimiter // create procedure 名字() begin SQL语句 end// delimiter ; 传参数:(in,out,inout)<参数不管是否使用,都必须传值,而且存储过程是没有返回值的!!!> delimiter // create procedure p2( in n1 int, in n2 int ) BEGIN sql语句 END// delimiter ;
执行存储过程:
SQL:call 名字()
py:cursor.callproc("名字",(传参))
cursor.commit() 一定要提交操作
cursor.fetchall() 获取查询到的结果集
删除存储过程:
drop procedure 名字;
分类举例:
1. 简单
create procedure p1()
BEGIN
select * from student;
INSERT into teacher(tname) values("ct");
END
call p1()
cursor.callproc('p1')
2. 传参数 in 调用执行时加括号是为了预留传参(in,out,inout)
delimiter //
create procedure p2(
in n1 int,
in n2 int
)
BEGIN
select * from student where sid > n1;
END //
delimiter ;
sql: call p2(12,2)
py: cursor.callproc('p2',(12,2))
3. 参数 out 用以模拟返回值 只能往回传,获取不到传入的值
delimiter //
create procedure p3(
in n1 int,
inout n2 int
)
BEGIN
set n2 = 123123;
select * from student where sid > n1;
END //
delimiter ;
sql:set @v1 = 10;创建并设置回话级别的变量 创建session级别变量 @ set 设置变量
call p3(12,@v1)
select @v1; 打印 执行结束后的 全局变量
py: cursor.callproc('p3',(12,2)) 传参
r1 = cursor.fetchall() 拿结果集
print(r1)
cursor.execute('select @_p3_0,@_p3_1') 固定写法 拿取存储过程的执行结果
r2 = cursor.fetchall()
print(r2)
(等同于 sql 中的以下操作!
set @_p3_0 = 12 session 级别的变量
ser @_p3_1 = 2
call p3(@_p3_0,@_p3_1)
select @_p3_0,@_p3_1
)
总结:
=======>特性:
a. 可传参: in out inout
b. pymysql
1.拿结果集
cursor.callproc('p3',(12,2))
r1 = cursor.fetchall()
print(r1)
2.拿返回值
cursor.execute('select @_p3_0,@_p3_1')
r2 = cursor.fetchall()
print(r2)
*为什么有结果集又有out伪造的返回值?(主要原因:是用于判断执行SQL语句的结果!)
delimiter //
create procedure p3(
in n1 int,
out n2 int 设置一个值,用于标识存储过程的执行结果 1,2
)
BEGIN -- 执行居多的增加语句
insert into 表名(列名) values();
insert into 表名(列名) values();
insert into 表名(列名) values();
insert into 表名(列名) values();
insert into 表名(列名) values();
END //
delimiter ;
事务
事务用于将某些操作的多个SQL作为原子性操作,一旦有某一个出现错误,即可回滚到原来的状态,从而保证数据库数据完整性。
判断操作完成状态:执行完成就结束,有错误就回滚。
#伪代码:
delimiter //
create procedure p4(
out status int
)
BEGIN
1. 声明如果出现异常则执行{
set status = 1; 检测到错误 返回的状态值
rollback; 必须加上 回滚操作!
}
开始事务
-- 某1账户减去100 操作
-- 某2账户加90
-- 某3账户加10
commit; 事物执行完,正确会提交!
结束
set status = 2; 正常执行成功,返回状态值
END //
delimiter ;
举例:
delimiter //
create PROCEDURE p5(
OUT p_return_code tinyint
)
BEGIN
DECLARE exit handler for sqlexception
BEGIN
-- ERROR
set p_return_code = 1;
rollback;
END;
START TRANSACTION; -- 开始事务
DELETE from tb1;
insert into tb2(name)values('seven');
COMMIT;
-- SUCCESS
set p_return_code = 2;
END//
delimiter ;
获取out返回结果:
sql:
set @v2 = 0;
call p5(@v2);
select @v2;
py: cursor.execute('select @_p5_0')
r5 = cursor.fetchall()
print(r5)
MyISAM 和 InnoDB的区别
MyISAM适合于一些需要大量查询的应用,但其对于有大量写操作并不是很好。甚至你只是需要update一个字段,整个表都会被锁起来,而别的进程,就算是读进程都无法操作直到写操作完成。另外,MyISAM对于SELECT COUNT(*) 统计 这类的计算是超快无比的。
InnoDB的趋势会是一个非常复杂的存储引擎,对于一些小的应用,它会比MyISAM还慢。他是它支持“行锁”,于是在写操作比较多的时候,会更优秀。并且,他还支持更多的高级应用,比如:事务。
游标 cursor (跟循环有关)
注意:性能不高!对每一行数据,分门别类的要进行计算的时候,才会使用!
1、声明游标
2、获取A表中的数据
my_cursor select id ,num from A
3、循环操作!for row_id,row_num in my_cursor: (不智能,不知道什么时候循环结束)
#检测循环时候还有数据,如果没有数据就退出!
insert into B(num) values (row_id+row_num)
4、要想使用变量得先声明
#代码举例 delimiter // create procedure p6() begin #声明变量 declare row_id int; -- 自定义变量1 declare row_num int; -- 自定义变量2 declare done INT DEFAULT FALSE; declare temp int; #创建游标 declare my_cursor CURSOR FOR select id,num from A; -- 声明游标类型 declare CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; -- 游标内部 没有值 done设置为True #开始操作 open my_cursor; -- 打开游标 xxoo: LOOP 语法:循环名:LOOP 开始循环 fetch my_cursor into row_id,row_num; if done then leave xxoo; END IF; set temp = row_id + row_num; insert into B(number) values(temp); end loop xxoo; close my_cursor; -- 关闭游标 end // delimter ;
动态执行SQL(防SQL注入)
伪代码:
delimiter //
create procedure p7(
in tpl varchar(255), # 接收一大堆的字符串
in arg int
)
begin
1. 预检测某个东西 SQL语句合法性 做防sql注入的问题
2. SQL =格式化 tpl + arg 把传入的两个参数进行字符串格式化
3. 执行SQL语句 -----> 拿取结果集
set @xo = arg;
PREPARE xxx(变量,随便起名字) FROM tpl ('select * from student where sid > ?');
EXECUTE xxx USING @xo; -- 注意:格式化的时候,传入的值必须是session级别的变量
DEALLOCATE prepare xxx; -- 执行格式化完成的SQL语句
end //
delimter ;
call p7("select * from tb where id > ?",9) # ?表示占位符
======> 示例 <====== delimiter \\ CREATE PROCEDURE p8 ( in nid int ) BEGIN set @nid = nid; PREPARE prod FROM 'select * from student where sid > ?'; EXECUTE prod USING @nid; DEALLOCATE prepare prod; END\\ delimiter ;
数据库相关操作:
1. SQL语句 ***** 优先使用 (要求速度!)
- select xx() from xx ;
2.先查找内置函数,有就用! 利用MySQL内部提供的功能 (性能要求不高!)
索引
索引是表的目录,在查找内容之前可以先在目录中查找索引位置,以此快速定位查询数据。对于索引,会保存在额外的文件中。
作用:
- 约束
- 加速查找
索引分类:
- 主键索引:加速查找 + 不能为空 + 不能重复
- 普通索引:加速查找
- 唯一索引:加速查找 + 不能重复
- 联合索引(多列):
- 联合主键索引
- 联合唯一索引
- 联合普通索引
1、加速查找:
快: select * from tb where name='asdf' select * from tb where id=999 假设: id name email ... ... .. 无索引:对整个表从前到后依次查找 索引: id 创建额外文件(某种格式存储) name 创建额外文件(某种格式存储) email 创建额外文件(某种格式存储) create index ix_name on userinfo3(email); name email 创建额外文件(某种格式存储)
索引种类(某种格式存储):
hash索引: (存储是对表中的数据进行存储,无序)
单值速度快
范围速度慢 对于连续型的或是有范围的数据会慢,原因是hash表内存储的数据与原表中的数据不对称
btree索引:
二叉树 索引 金字塔结构,从顶到底查找,左支比当前数小,右支比当前数大
========》 结果:快 《========
建立索引:
必须注意的三点:
- a. 额外的文件保存特殊的数据结构 (创建索引会重新创建一个文件,以保存对应的关系)
- b. 查询快;但是执行 插入 更新 删除 操作时慢
- c. 命中索引 (利用创建的索引进行查询,建立了不使用就是浪费)
select * from userinfo3 where email='aaa8888@163.com'; select * from userinfo3 where email like 'new%'; 慢 (模糊匹配,索引没用,还是会在原数据中一个一个去找)
主键索引:
主键有两个功能:加速查询 和 唯一约束
普通索引:
- create index 索引名称 on 表名(列名,) - drop index 索引名称 on 表名
唯一索引:
- create unique index 索引名称 on 表名(列名) - drop unique index 索引名称 on 表名
组合索引:(最左前缀匹配)
组合索引是将n个列组合成一个索引。其应用场景为:频繁的同时使用n列来进行查询
遵循:最左前缀匹配规则,跟索引顺序有关,条件中第一个判断必须是建立组合索引的第一个值。
- create unique index 索引名称 on 表名(列名1,列名2) - drop unique index 索引名称 on 表名
例: - create index ix_name_email on test(name,email,) - 最左前缀匹配 select * from test where name='alex666'; 会 select * from test where name='alex666' and email='alex666@163.com'; 会 select * from test where email='alex666@163.com'; 不会