视图、触发器、事务、存储过程、函数

视图、触发器、事务、存储过程、函数

一 视图

1 什么是视图

	视图是一个虚拟表(非真实存在) ,其本质是【根据SQL语句获取动态的数据集,并为其命名】,用户使用时只需要使用【名称】即可获得结果集,可以将该结果当作表来使用。

​	使用视图我们可以把查询结果过程中的临时表摘出来,用视图去实现,这样可以以后再想操作该临时表的数据时就无需重写复杂的sql了,直接去视图中查找即可,但视图有明显的效率问题,并且视图是存放在数据库中的,如果我们程序中使用的sql过分依赖数据库中的视图,即强耦合,那就意味着扩展sql极为不便,因此并不推荐使用。

	# 示例:临时表应用
# 两张有关系的表
mysql> select * from course;
+-----+--------+------------+
| cid | cname  | teacher_id |
+-----+--------+------------+
|   1 | 生物   |          1 |
|   2 | 物理   |          2 |
|   3 | 体育   |          3 |
|   4 | 美术   |          2 |
+-----+--------+------------+
4 rows in set (0.00 sec)

mysql> select * from teacher;
+-----+-----------------+
| tid | tname           |
+-----+-----------------+
|   1 | 张磊老师        |
|   2 | 李平老师        |
|   3 | 刘海燕老师      |
|   4 | 朱云海老师      |
|   5 | 李杰老师        |
+-----+-----------------+
5 rows in set (0.00 sec)

# 查询李平老师教授的课程名
mysql> select cname from course where teacher_id = (select tid from teacher where tname='李平老师');
+--------+
| cname  |
+--------+
| 物理   |
| 美术   |
+--------+
2 rows in set (0.00 sec)

# 子查询出临时表,作为teacher_id等判断依据
select tid from teacher where tname='李平老师'

2 创建视图

"语法": CREATE  VIEW  视图名称  AS SQL语句;
# 于是查询李平老师教授的课程名的sql可以改写为
mysql> select cname from course where teacher_id = (select tid from teacher_view);
+--------+
| cname  |
+--------+
| 物理   |
| 美术   |
+--------+
# 注意:"
	1).使用视图后就无需每次都重写子查询的sql,但是这么效率不高,还不如我们写子查询效率高;

	2).而且有一个致命的问题:视图是存放到数据库里的,如果我们程序中的SQL过分依赖于数据库中存放的视图,那么意味着,一旦SQL需要修改且涉及到视图的部分,则必须去数据库中进行修改,而通常在公司中数据库有专门的DBA负责,修改需付出巨大的沟通成本,极不方便。

3 使用视图

# 修改原始表,原始视图也跟着改
mysql> select * from course;
+-----+--------+------------+
| cid | cname  | teacher_id |
+-----+--------+------------+
|   1 | 生物   |          1 |
|   2 | 物理   |          2 |
|   3 | 体育   |          3 |
|   4 | 美术   |          2 |
+-----+--------+------------+

# 创建表course的视图
mysql> create view course_view as select * from course;

mysql> select * from course_view;
+-----+--------+------------+
| cid | cname  | teacher_id |
+-----+--------+------------+
|   1 | 生物   |          1 |
|   2 | 物理   |          2 |
|   3 | 体育   |          3 |
|   4 | 美术   |          2 |
+-----+--------+------------+

# 更新视图中的数据
mysql> update course_view set cname='xxx';

# 往视图中插入数据
mysql> insert into course_view values(5,'yyy',2);

# 发现原始表的记录也跟着修改了
mysql> select * from course;
+-----+-------+------------+
| cid | cname | teacher_id |
+-----+-------+------------+
|   1 | xxx   |          1 |
|   2 | xxx   |          2 |
|   3 | xxx   |          3 |
|   4 | xxx   |          2 |
|   5 | yyy   |          2 |
+-----+-------+------------+

	我们不应该修改视图中的记录,而且在涉及多个表的情况下是根本无法修改视图中的记录的,如下图

4 修改视图

"语法":  ALTER  VIEW  视图名称  AS  SQL  语句;
mysql> alter view teacher_view as select * from course where cid>3;

mysql> select * from teacher_view;
+-----+-------+------------+
| cid | cname | teacher_id |
+-----+-------+------------+
|   4 | xxx   |          2 |
|   5 | yyy   |          2 |
+-----+-------+------------+

5 删除视图

"语法" : DROP  VIEW  视图名称;
DROP VIEW teacher_view;

二 触发器

1 为何要使用触发器

	使用触发器可以定制用户对表进行【增、删、改】操作前后的行为。!!注意:没有修改。

2 创建触发器

# 创建触发器的语法:
# 插入前
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
	# 示例:插入后触发触发器"特别的语法:NEW表示即将插入的数据行,OLD表示即将删除的数据行。"

# 准备表
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
);

# 创建触发器
mysql> delimiter //  # 将结束符号修改为//,让MySQL允许我们输入多个含分号";"的语句,最后修改回
来,让前面的争端语句生效。
mysql> CREATE TRIGGER tri_after_insert_cmd AFTER INSERT ON cmd FOR EACH ROW
    -> BEGIN
    ->     IF NEW.success = 'no' THEN  # 等值判断只有一个等号
    ->             INSERT INTO errlog(err_cmd, err_time) VALUES(NEW.cmd, NEW.sub_time) ;  # 必须加分号
    ->       END IF ;  # 必须加分号
    -> END//
Query OK, 0 rows affected (0.10 sec)

mysql> delimiter ;
mysql>


# 往表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');

# 查询错误日志,发现有两条
mysql> select * from errlog;
+----+-----------------+---------------------+
| id | err_cmd         | err_time            |
+----+-----------------+---------------------+
|  1 | cat /etc/passwd | 2020-09-09 16:50:55 |
|  2 | useradd xxx     | 2020-09-09 16:50:55 |
+----+-----------------+---------------------+
2 rows in set (0.00 sec)

3 使用触发器

	触发器无法由用户直接调用,只是由于对表的【增//改】操作被动引发。

4 删除触发器

DROP TRIGGER tri_after_insert_cmd;

三 事务

1 什么是事务

	数据库事务是指作为单个逻辑单元执行的一系列操作(SQL语句) 。这些操作要么全部执行,要么全部不执行。

2 为什么需要事务

	"经典的银行转账行为,A账户转给B账户10元,数据库操作需要两步,第一步A账户减10元,第二步B账户加10元,如果没有事务并且在两步中间发生异常,就会导致A的账户少了10元,但B的账户没有变化,如果不能保证这两步操作统一,银行的转账业务也没法进行展开了。"

​	事务管理是每个数据库(Oracle,MySQL,db等) 都必须实现的。

3 事务特性(4种) :

1).原子性(atomicty) :强调事务的不可分割;

2).一致性(consistency) :事务的执行的前后数据的完整性保持一致;

3).隔离性(isolation) :一个事务执行的过程中,不应该受到其他事务的干扰;

4).持久性(durability) :事务一旦结束,数据就持久到数据库。

4 事务运行模式(3种) :

	1).自动提交事务:默认的事务管理模式。如果一个语句成功地完成,则提交该语句;如果遇到错误,则回滚该语句。

	2).显式事务:以 BEGIN TRANSACTION 显式开始,以 COMMIT 或 ROLLBACK 显式结束。

	3).隐式事务:当连接以此模式进行操作时,sql将在提交或回滚当前事务后自动启动新事务。无须描述事务的开始,只需提交或回滚每个事务。它生成连续的事务链。

5 使用案例

5.1 在MySQL中的使用
# 示例:
create table user(
id int primary key auto_increment,
name char(32),
balance int
);

insert into user(name,balance) values
('wsb',1000),
('egg',1000),
('ysb',1000);

# 原子操作
start transaction;
update user set balance=900 where name='wsb';  # 买支付100元
update user set balance=1010 where name='egg';  # 中介拿走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='egg';  # 中介拿走10元
update user set balance=1090 where name='ysb';  # 卖家拿到90元,出现异常,没有拿到
rollback;
commit;

mysql> select * from user;
+----+------+---------+
| id | name | balance |
+----+------+---------+
|  1 | wsb  |    1000 |
|  2 | egg  |    1000 |
|  3 | ysb  |    1000 |
+----+------+---------+
3 rows in set (0.00 sec)
5.2 在pymysql中实现事务处理
"pymysql执行的所有sql语句,都会默认放入一个事务中去。针对查询语句,事务并没有什么影响,更多的是针对修改语句。"
import pymysql  # pip3 install pymysql

connect = pymysql.connect(host="127.0.0.1", port=3306, user="root", password="123", db="db13", charset="utf8mb4")
cursor = connect.cursor(cursor=pymysql.cursors.DictCursor)

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)  # 关闭连接

connect.close()

6 总结

	事务用于将某些操作的多个SQL语句作为原子性操作,一旦有某一个错误,即可回滚到原来的状态,从而保证数据的完整性。

四 存储过程

1 什么是存储过程

	存储过程包含了一系列可执行的sql语句,存储过程存放于MySQL中,通过调用它的名字可以执行其内部的一堆sql语句。相当于一个封装好的接口,里面有各种业务逻辑。
1.1 补充:程序于数据库结合使用的三种方式
# 方式一:
	MySQL:存储过程
    程序:调用存储过程
	
# 方式二:
	MySQL:建库、表及关系
    程序:纯SQL语句(开发人员写) 
	
# 方式三:
	MySQL:建库、表及关系
    程序:类和对象,即ORM(本质还是纯SQL语句) (对象关系映射;类映射成表,对象映射成表的记录)

2 存储过程的优缺点

# 使用存储过程的优点:
    1) 基于替代程序写的SQL语句,实现程序与SQL解耦;
    2) 基于网络传输,传别名的数据量小,而直接传SQL数据量大。

# 使用存储过程的缺点:
    1) 程序扩展功能不方便

3 创建简单存储过程(无参)

# 创建无参存储过程
mysql> delimiter $$
mysql> create procedure p1()
    -> begin
    ->     select * from emp;
    -> end $$
Query OK, 0 rows affected (0.00 sec)

mysql>
mysql> delimiter ;
mysql>
# 在MySQL中调用:
mysql> call p1();
+----+------------+--------+------+--------+
| id | name       | sex    | age  | dep_id |
+----+------------+--------+------+--------+
|  1 | egon       | male   |   18 |    200 |
|  2 | alex       | female |   48 |    201 |
|  3 | wupeiqi    | male   |   38 |    201 |
|  4 | yuanhao    | female |   28 |    202 |
|  5 | liwenzhou  | male   |   18 |    200 |
|  6 | jingliyang | female |   18 |    204 |
|  7 | xxx        | male   |   66 |   NULL |
+----+------------+--------+------+--------+

# 在python中基于pymysql调用:
cursor.callproc('p1')
print(cursor.fetchall())

4 创建存储过程(有参)

	对于存储过程,可以接收参数,其参数有三类:

	(1)  in	  	————>仅用于接收传入参数用;

	(2)  out   	————>仅用于返回值用;

	(3)  inout	————>既可以传入又可以当作返回值。
# 创建有参存储过程
delimiter $$
create procedure p2(
    in n int,  # 指定多个字段,必须加逗号隔开,最后一个不加逗号
    out res int
)
begin
    select * from emp where id > n;  # 查找id>n 的记录
    set res=1;  # 自己设定,控制返回值,代表本条成功与否
end $$

delimiter ;

# 在MySQL中调用
mysql> set @x=1111;  # 必须事先定义一个变量,后面要查看变量的结果
Query OK, 0 rows affected (0.00 sec)

mysql> call p2(3,x);
ERROR 1414 (42000): OUT or INOUT argument 2 for routine d47.p2 is not a variable or NEW pseudo-variable in BEFORE trigger

mysql> call p2(3,@x);
+----+------------+--------+------+--------+
| id | name       | sex    | age  | dep_id |
+----+------------+--------+------+--------+
|  4 | yuanhao    | female |   28 |    202 |
|  5 | liwenzhou  | male   |   18 |    200 |
|  6 | jingliyang | female |   18 |    204 |
|  7 | xxx        | male   |   66 |   NULL |
+----+------------+--------+------+--------+
4 rows in set (0.00 sec)

mysql> select @3;
+------+
| @3   |
+------+
| NULL |
+------+
1 row in set (0.00 sec)

mysql> select @x;
+------+
| @x   |
+------+
|    1 |
+------+
1 row in set (0.00 sec)

# 在python中基于pyMySQL调用

import pymysql  # pip3 install pymysql

connect = pymysql.connect(host="127.0.0.1", port=3306, user="root", password="123", db="day47", charset="utf8mb4")
cursor = connect.cursor(cursor=pymysql.cursors.DictCursor)

cursor.callproc('p2', (3, 0))  # @_p2_0=3,@_p2_1=0  # 0相当于set @res=0
'''
set @_p2_0=3  【底层组织成的一个变量的格式】
set @_p2_1=0

call p2(@_p2_0,@_p2_1);  #@_p2_0代表第一个参数,@_p2_1代表第二个参数,即返回值
'''
print(cursor.fetchall())  # 查询select的查询结果 
# [{'id': 4, 'name': 'yuanhao', 'sex': 'female', 'age': 28, 'dep_id': 202}, {'id': 5, 'name': 'liwenzhou', 'sex': 'male', 'age': 18, 'dep_id': 200}, {'id': 6, 'name': 'jingliyang', 'sex': 'female', 'age': 18, 'dep_id': 204}, {'id': 7, 'name': 'xxx', 'sex': 'male', 'age': 66, 'dep_id': None}]

cursor.execute('select @_p2_0,')  # 执行一个sql语句查看返回值
print(cursor.fetchall())  # [{'@_p2_0': 3}]  【3这个值不会变,一直是3】

cursor.execute('select @_p2_1')
print(cursor.fetchall())  # [{'@_p2_1': 1}]  【会变,查询成功返回值是1,这个是我们自己在存储过程中设定的那个值】

cursor.close()
connect.close()

5 执行存储过程

5.1 在MySQL中执行存储过程
-- 无参数
call proc_name()

-- 有参数,全in
call proc_name(1,2)

-- 有参数,有in,out, inout
set @t1=0;
set @t2=3;
call proc_name(1,2,@t1,@t2)
5.2 在python中基于pymysql执行存储过程
#!/usr/bin/env python
# -*- coding:utf-8 -*-
import pymysql

conn = pymysql.connect(host='127.0.0.1', port=3306, user='root', passwd='123', db='t1')
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)

# 执行存储过程
cursor.callproc('p1', args=(1, 22, 3, 4))

# 获取执行完存储的参数
cursor.execute("select @_p1_0,@_p1_1,@_p1_2,@_p1_3")
result = cursor.fetchall()

conn.commit()
cursor.close()
conn.close()

print(result)

6 删除存储过程

DROP procedure prco_name;

五 函数

1 内置函数

1.1 需要掌握函数:date_format
# 1) 基本使用
mysql> SELECT DATE_FORMAT('2009-10-04 22:23:00', '%W %M %Y');
        -> 'Sunday October 2009'
mysql> SELECT DATE_FORMAT('2007-10-04 22:23:00', '%H:%i:%s');
        -> '22:23:00'
mysql> SELECT DATE_FORMAT('1900-10-04 22:23:00',
    ->                 '%D %y %a %d %m %b %j');
        -> '4th 00 Thu 04 10 Oct 277'
mysql> SELECT DATE_FORMAT('1997-10-04 22:23:00',
    ->                 '%H %k %I %r %T %S %w');
        -> '22 22 10 10:23:00 PM 22:23:00 00 6'
mysql> SELECT DATE_FORMAT('1999-01-01', '%X %V');
        -> '1998 52'
mysql> SELECT DATE_FORMAT('2006-06-00', '%d');
        -> '00'
# 2) 代码示例 :用于按月份进行分组
mysql> CREATE TABLE blog (
    ->     id INT PRIMARY KEY auto_increment,
    ->     NAME CHAR (32),
    ->     sub_time datetime
    -> );

mysql> INSERT INTO blog (NAME, sub_time)
    -> VALUES
    ->     ('第1篇','2015-03-01 11:31:21'),
    ->     ('第2篇','2015-03-11 16:31:21'),
    ->     ('第3篇','2016-07-01 10:21:31'),
    ->     ('第4篇','2016-07-22 09:23:21'),
    ->     ('第5篇','2016-07-23 10:11:11'),
    ->     ('第6篇','2016-07-25 11:21:31'),
    ->     ('第7篇','2017-03-01 15:33:21'),
    ->     ('第8篇','2017-03-01 17:32:21'),
    ->     ('第9篇','2017-03-01 18:31:21');

mysql> select date_format(sub_time,"%Y-%m") as t,count(id) from blog group by t;
+---------+-----------+
| t       | count(id) |
+---------+-----------+
| 2015-03 |         2 |
| 2016-07 |         4 |
| 2017-03 |         3 |
+---------+-----------+
3 rows in set (0.00 sec)
1.2 其他内置函数

http://doc.mysql.cn/mysql5/refman-5.1-zh.html-chapter/functions.html#encryption-functions

2 自定义函数

# 1.注意事项:

(1) 函数中不要写SQL语句(否则会报错) ,函数仅仅是一个功能,是一个在SQL中被应用的功能;

(2) 若想在begin···end···中写SQL,需要使用存储过程。
delimiter //
create function f1(
    i1 int,
    i2 int)
returns int
BEGIN
    declare num int;
    set num = i1 + i2;
    return(num);
END //
delimiter ;
delimiter //
create function f5(
    i int
)
returns int
begin
    declare res int default 0;
    if i = 10 then
        set res=100;
    elseif i = 20 then
        set res=200;
    elseif i = 30 then
        set res=300;
    else
        set res=400;
    end if;
    return res;
end //
delimiter ;

3 删除函数

DROP function func_name;

4 执行函数

# 获取返回值
select UPPER('egon') into @res;
SELECT @res;

# 在查询中使用
select f1(11,nid),name from tb2;

六 流程控制

1 if条件语句

delimiter //
CREATE PROCEDURE proc_if ()
BEGIN
    
    declare i int default 0;
    if i = 1 THEN
        SELECT 1;
    ELSEIF i = 2 THEN
        SELECT 2;
    ELSE
        SELECT 7;
    END IF;

END //
delimiter ;

2 循环语句

2.1 while循环
delimiter //
CREATE PROCEDURE proc_while ()
BEGIN

    DECLARE num INT ;
    SET num = 0 ;
    WHILE num < 10 DO
        SELECT
            num ;
        SET num = num + 1 ;
    END WHILE ;

END //
delimiter ;
2.2 repeat循环"
delimiter //
CREATE PROCEDURE proc_repeat ()
BEGIN

    DECLARE i INT ;
    SET i = 0 ;
    repeat
        select i;
        set i = i + 1;
        until i >= 5
    end repeat;

END //
delimiter ;
2.3.loop
BEGIN
    
    declare i int default 0;
    loop_label: loop
        
        set i=i+1;
        if i<8 then
            iterate loop_label;
        end if;
        if i>=10 then
            leave loop_label;
        end if;
        select i;
    end loop loop_label;

END
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值