首页 > 代码库 > python 全栈 数据库(二)MySQL数据库进阶
python 全栈 数据库(二)MySQL数据库进阶
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 1、视图 (不常用,开发过程中不长用,在开发语句中写,不要在数据库中写) 视图是一个虚拟表(非真实存在),其本质是【根据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; 2、触发器 (查询不会引发此操作!) 当对某表里的某行,做增删改操作时,可在完成之前或是之后去触发执行某个操作 当对某表里的某行,做增删改操作时,可以使用触发器自定义关联行为 当有相关的操作时候,每执行一次相关操作,触发器也会执行一次。 触发器操作,分为执行前触发和执行之后触发! 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 触发器名 3、函数 函数执行方法: 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 函数名 4、存储过程 存储过程是一个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 ; 4. 事务 事务用于将某些操作的多个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) 5. 游标 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 ; 6. 动态执行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内部提供的功能 (性能要求不高!) 5、 索引 索引是表的目录,在查找内容之前可以先在目录中查找索引位置,以此快速定位查询数据。对于索引,会保存在额外的文件中。 作用: - 约束 - 加速查找 索引: - 主键索引:加速查找 + 不能为空 + 不能重复 - 普通索引:加速查找 - 唯一索引:加速查找 + 不能重复 - 联合索引(多列): - 联合主键索引 - 联合唯一索引 - 联合普通索引 加速查找: 快: 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=‘alex8888@163.com‘; select * from userinfo3 where email like ‘alex%‘; 慢 (模糊匹配,索引没用,还是会在原数据中一个一个去找) 主键索引: 主键有两个功能:加速查询 和 唯一约束 普通索引: - 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‘; 不会 组合索引效率 > 索引合并 (具体还是以使用环境决定) 组合索引 (当查询的信息需要多个条件时) - (name,email,) select * from test where name=‘alex666‘ and email=‘alex666@163.com‘; 快 select * from test where name=‘alex666‘; 索引合并:(平常查询就用一个条件判断,多个条件不常用的情况) - name - email select * from test where name=‘alex666‘; 快 select * from test where email=‘alex666@163.com‘; 快 select * from test where name=‘alex666‘ and email=‘alex666@163.com‘; 相对快 名词:虚拟存在的索引 覆盖索引: - 直接在索引文件中获取数据 (不是真实的索引) select的数据列只用从索引中就能够取得,不必读取数据行,换句话说查询列要被所建的索引覆盖 例:select email from test where email="alex2566666@163.com" 索引合并: - 把多个单列索引合并使用 例:select email from test where email="alex2566666@163.com" and tid=2222222 文本索引:对整个数据表做成索引,不常用到,一般是用第三方软件去调用 由于索引是专门用于加速搜索而生,所以加上索引之后,查询效率会快到飞起来。 数据库表中添加索引后确实会让查询速度起飞,但前提必须是正确的使用索引来查询,如果以错误的方式使用,则即使建立索引也会不奏效。 2. 为频繁查找的列创建索引 但是有些语法不会对索引加速 若想快速查询,我们的操作是这样的: - 创建索引 - 命中索引 ***** 大数据量下注意,小数据没事! 但是以下方式不会命中索引: - like ‘%xx‘ 模糊查找 select * from test where email like ‘alex%‘; 可用第三方工具去加速! - 使用函数 取反 倒序 select * from test where reverse(email) = ‘alex235555@163.com‘; 若想翻转查找,就在python中先把要查询的数据反转,再去和数据库内容匹配 - or 索引 or 非索引 就无法命中索引操作 select * from test where tid = 1 or name = ‘alex235555@163.com‘; 特别的:当or条件中有未建立索引的列才失效,以下会走索引 select * from test where tid = 1 or name = ‘alex2666666‘ and email = ‘alex235555@163.com‘ #会忽略非索引 - 查找条件,填写的数据类型不一致 如果列是字符串类型,传入条件是必须用引号引起来,若是写成数字类型,这里边就涉及转码的过程,肯定会拖慢查询速度 select * from test where email = 999; - != 不等于 说白了就是取反 select * from test where email != ‘alex‘ 特别的:如果是主键,则还是会走索引 select * from test where tid != 123 - > 大于 select * from test where email > ‘alex1111111@163.com‘ 特别的:如果是主键或索引是整数类型,则还是会走索引 select * from test where tid > 123 select * from test where num > 123 - order by 排序 注意:当根据索引排序时候,选择的映射(要显示的列)如果不是索引,则不走索引 select name from test order by email desc; 特别的:如果对主键排序,则还是走索引: select * from test order by nid desc; - 组合索引最左前缀 如果组合索引为:(name,email) name and email -- 使用索引 name -- 使用索引 email -- 不使用索引 3. 时间 explain + 查询SQL - 用于显示SQL执行信息参数,根据参考信息可以进行SQL优化 MySQL有执行计划:在真正执行操作之前,先让mysql预估执行操作(一般正确) 看看费时和type类型!好做判断! 语法:explain SQL语句 SQL 预估操作时间(仅作参考) type类型:all < index < range < index_merge < ref_or_null < ref < eq_ref < system/const all 是全局扫描 ref 是索引扫描 id,email 慢: select * from test where name=‘alex2666666‘ explain select * from test where name=‘alex2666666‘ type: ALL(全表扫描) select * from test limit 1; #limit 分页获取是个例外,此种操作获取前面的数据会很快(因为操作的数据少啊),若起始是查找后边好几百万的数据行,照样飞不起来! 快: select * from test where email=‘alex2666666‘ type: const(走索引) 4. DBA工作 慢日志 - 执行时间 > 10 - 未命中索引 - 日志文件路径 配置:(修改配置文件之前,一定要注意先备份!要不然就得跑路!!!) - 程序执行暂存在内存中,同时配置信息也在! - 查看当前配置信息:(内存内的变量) show variables like ‘%query%‘ #查看有 query 的变量 (主要是看配置信息,文件路径) show variables like ‘%queries%‘ #查看有 queries 的变量(主要是看索引) - 修改当前配置: set global 变量名 = 值 set slow_query_log = ON 开启慢日志记录 set slow_query_log_file = D:\mysql\setup\mysql-5.7.18-winx64\data\ZYJ-PC-slow.log 日志文件 默认在data目录中 set long_query_time = 2 时间限制,超过此时间,则记录 set log_queries_not_using_indexes = ON 为使用索引的搜索记录 - 配置文件 写好配置文件,然后再开启数据库的时候一起执行生效,这样就省的在每次开启之后,再去配置! 开启执行: mysqld --defaults-file=‘配置文件的绝对路径+配置文件名‘ 创建配置文件 --->随意 配置文件内容: slow_query_log = ON 开启慢日志记录 slow_query_log_file = D:\mysql\setup\mysql-5.7.18-winx64\data\ZYJ-PC-slow.log 日志文件 默认在data目录中 long_query_time = 2 时间限制,超过此时间,则记录 log_queries_not_using_indexes = ON 为使用索引的搜索记录 注意:修改配置文件之后,需要重启服务生效(修改只是修改在硬盘内的文件,内容内的文件没有修改!) 5. ******分页******* a. 限制页数,只能查看多少页 select * from test limit 20,10; b.三种方法 - 直接不让看 - 索引表中扫:(覆盖索引) SELECT * FROM test WHERE tid in (SELECT N.tid FROM (SELECT * FROM test LIMIT 2900000,10) AS N) - 方案:相对查找 主要是 ------> 记录当前页最大或最小ID 1. 页面只有上一页,下一页 # max_id # min_id 下一页:tid > max_id select * from test where tid > 2900000 limit 10; 上一页:tid < min_id SELECT * FROM (select * from test where tid < 2899991 ORDER BY tid DESC limit 10) AS N ORDER BY N.tid ASC; 2. 跳页 上一页 192 193 [196] 197 198 199 下一页 SELECT * FROM (SELECT * FROM (SELECT * FROM test WHERE tid > 2800000 LIMIT 40) AS N ORDER BY N.tid DESC LIMIT 10) AS S ORDER BY S.tid ASC c. *****为什么不用 between and *****: 最致命的原因:若id不连续,无法直接使用id范围进行查找,获取的数据就不在是定值!
python 全栈 数据库(二)MySQL数据库进阶
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。