视图:
视图,虚拟表
创建虚拟表:
# 语法: # create view 虚拟表名称 as 虚拟表; create view course_and_teacher as select * from course inner join teacher on course.teacher_id = teacher.tid; # 虚拟表在硬盘上存储时,只有 表结构, 没有 表数据 那张表,即 只有 course_and_teacher.frm 这个文件;因为虚拟表的数据来自于其它表 # 创建的虚拟表可以直接使用: select * from course_and_teacher; # 每次使用 course_and_teacher 这张表时,都会触发 select * from course inner join teacher on course.teacher_id = teacher.tid; # 虚拟表不建议使用,因为 如果 你在数据库建了大量的视图,不利用扩展(数据库可能经常需要扩展;即强耦合);所以尽量用原生的 SQL # 另外,视图是用来查询的,不能用来修改(不要修改视图中的记录) # 修改视图: # 语法: alert view 视图名称 as SQL语句; alert view course_and_teacher as select * from course where cid>3; # 删除视图: # 语法: drop view 视图名称; drop view course_and_teacher;
视图 WITH CHECK OPTION 参数参考链接: https://blog.csdn.net/gumengkai/article/details/51185085
触发器:
一碰就动;使用触发器可以定制用户对表 【增、删、改】操作时前后的行为(注意:没有查询)
# 一、创建触发器: # 针对 insert 可以是 before insert(对于每一行,在 insert 行为之前,去触发一个 begin.. end 之间的 SQL语句), 也可以是 after insert (对于每一行,在 insert 行为之后,去触发一个 begin.. end 之间的 SQL语句) # 语法(插入前): # create trigger 触发器名称 before insert on 表名 for each row # begin ... end # 插入前: create trigger tri_before_insert_tb1 before insert on tb1 for each row begin ... end
# 插入后: create trigger tri_after_insert_tb1 after insert on tb1 for each row begin ... end # 删除前: create trigger tri_before_delete_tb1 before delete on tb1 for each row begin ... end # 删除后: create trigger tri_after_delete_tb1 after delete on tb1 for each row begin ... end # 更新前: create trigger tri_before_update_tb1 before update on tb1 for each row begin ... end # 更新后: create trigger tri_after_update_tb1 after update on tb1 for each row begin ... end
示例:
# 准备表: 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 // # delimiter 用于声明 SQL语句的结束符 是 “//”,而不是 “;” create trigger tri_after_insert_cmd after insert on cmd for each row BEGIN # 触发器提供了两个对象:NEW 和 OLD;NEW代表新增的记录,OLD代表以前老的记录(插入行为没有 OLD 一说,因为插入的永远都是 新的;修改的情况下,才有老的记录,同 NEW 也能派上用场) if NEW.success = 'no' THEN # 等值判断只有一个等号; NEW.success 表示 NEW对象的 success 属性 insert into errlog(err_cmd,err_time) values(NEW.cmd,NEW.sub_time); # 必须加分号 end if; # 必须加分号 END// # 此处的 “//” 表示 触发器定义完了 delimiter ; # 重新声明 SQL语句 的结束符 为 “;” # NEW 表示即将插入的数据行,OLD 表示即将删除的数据行 # 触发器是 MySQL 的一个内置功能;我们也可以在 应用程序级别 自己去实现 触发器的功能,如利用 python 代码自己去实现 # 建议在 应用程序级别去实现 触发器的功能;方便以后扩展功能 # 使用触发器: 触发器无法由用户直接调用,而是由于对 表的 【增、删、改】 操作被动引发的 # 删除触发器: drop trigger tri_after_insert_cmd; # 查看触发器: show triggers;
存储过程:
存储过程包含了一系列可执行的 sql语句,存储过程存放于MySQL中,通过调用它的名字可以执行其内部的一堆 sql 优点: 1. 用于替代程序写的SQL语句,实现程序与sql解耦 2. 基于网络传输,传别名的数据量小,而直接传sql数据量大 缺点: 程序扩展功能不方便
示例:
# 调用存储过程 # MySQL中调用: call p1(); # python中调用: cursor.callproc('p1') # 用 cursor 去调用 callproc('存储过程的名称') 方法 # 如果 存储过程中是查询语句,利用相应的方法去获取相应的结果: print(cursor.fetchall()) # 有参 delimiter // create procedure p2(in n1 int,in n2 int,out res int) # 1. MySQL中的存储过程,参数必须指定 参数类型;2. 参数必须指明是用来接收值的,还是用来返回值的:in 代表传入参数, out表示返回参数,inout表示可进可出 BEGIN select * from db7.student where tid>n1 and tid<n2; set res = 1; # set 设置返回值 END // delimiter ; # MySQL 中调用: set @x=0 # 设置变量的初始值 call p2(2,5,@x); # 传参 select @x; # 查看 返回值 # python 中调用: cursor.callproc('p2',(2,5,0)) # callproc 在执行该存储过程的时候会把参数做转换: @_p2_0=2, @_p2_1=5, @_p2_2=0 # 如果 存储过程中是查询语句,利用相应的方法去获取相应的结果: print(cursor.fetchall()) # 查看返回结果 cursor.execute('select @_p2_2') print(cursor.fetchone())
应用程序与数据库结合使用的三种方式:
方式一:
MySQL:编写存储过程
Python:调用存储过程
方式二:
Python:编写纯生SQL
MySQL:啥也不干
方式三:
Python:ORM
MySQL:啥也不干
事务:
事务用于将某些操作的多个SQL作为原子性操作,一旦有某一个出现错误,即可回滚到原来的状态,从而保证数据库数据完整性
示例:
# 建表 create table user( id int primary key auto_increment, name char(32), balance int ); # 插入数据 insert into user(name,balance) values ('wsb',1000), ('egon',1000), ('ysb',1000); #原子操作 start transaction; # 开启事务 update user set balance=900 where name='wsb'; #买支付100元 update user set balance=1010 where name='egon'; #中介拿走10元 update user set balance=1090 where name='ysb'; #卖家拿到90元 commit; #出现异常,回滚到初始状态 start transaction; update user set balance=900 where name='wsb'; #买支付100元 update user set balance=1010 where name='egon'; #中介拿走10元 uppdate user set balance=1090 where name='ysb'; #卖家拿到90元,出现异常没有拿到 # rollback; # 上述 update 操作的数据只是保存在内存中,此时 rollback 能让数据回滚到最初的状态;commit之后 rollback 就没有作用了 commit; # 想让内存的数据保存到 数据库中,则需要 commit mysql> select * from user; +----+------+---------+ | id | name | balance | +----+------+---------+ | 1 | wsb | 1000 | | 2 | egon | 1000 | | 3 | ysb | 1000 | +----+------+---------+ rows in set (0.00 sec)
使用:
#思路: delimiter // create procedure p4( out status int ) BEGIN 1. 声明如果出现异常则执行{ set status = 1; rollback; } 开始事务 -- 由秦兵账户减去100 -- 方少伟账户加90 -- 张根账户加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; DECLARE exit handler for sqlwarning # 声明警告处理 BEGIN -- WARNING set p_return_code = 2; rollback; # 有警告则回滚 END; START TRANSACTION; # 开启事务 DELETE from tb1; insert into blog(name,sub_time) values('yyy',now()); COMMIT; -- SUCCESS set p_return_code = 0; #0代表执行成功 END // delimiter ;
python的事务开启:
示例一:
# coding=utf-8 import pymysql #添加数据 conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='', db='yyy') cursor = conn.cursor() try: insertSQL0="INSERT INTO ACCOUNT2 (name,balance) VALUES ('bart',1000)" insertSQL1="UPDATE account2 set balance=balance-30 WHERE name='alex'" insertSQL2="UPDATE account2 set balance=balance+30 WHERE name='ego'" cursor.execute(insertSQL0) conn.commit() cursor.execute(insertSQL1) raise Exception #模拟出现错误 cursor.execute(insertSQL2) cursor.close() conn.commit() except Exception as e: conn.rollback() #回滚 conn.commit() cursor.close() conn.close()
示例二:
try: cursor.execute(sql_1) cursor.execute(sql_2) cursor.execute(sql_3) except Exception as e: connect.rollback() # 事务回滚 print('事务处理失败', e) else: connect.commit() # 事务提交 print('事务处理成功', cursor.rowcount) # cursor.rowcount 是一个只读属性,返回执行execute()方法后影响的行数。 # 关闭连接 cursor.close() connect.close()