day40 mycql 视图,触发器,存储过程,函数

视图,触发器,存储过程,自定义函数

-- 回顾
    1.mysql 约束
        1、非空 not null
        2. 主键约束    primary key
        3. 唯一约束 unique
        4. 外键约束 foreign key
        5. 默认值约束 default
        
    2. 数据库的设计
        1.好处:
            1.有效的存储数据
            2.满足用户的多种需求
        
        2.数据库设计三范式:
            1.确保每列都保持原子性
            2.确保每列都与主键相关
            3.确保每列都与主键直接相关,而非间接相关。
            
        3.数据库表关系、
            1. 1-1 特殊的一对多,通过在字段上添加唯一约束产生一对一关系
            2. 1-n 主表不动。从表中添加主表的主键
            3. n-n 两张主表结构不变,然后利用第三张表表示两张主表的关系
                id  name  。。。     id   name 。。。
                1    华联              1    老王
                2   加利福             2    小王
                
                超市顾客关系表
                id  c_id    u_id
                1    1        1
                2    2        1

-- 今日内容

    视图
        概念:其实就是一个临时表
        
        1.创建视图
            create view 视图名称 as sql查询语句;
        2.使用视图
            select * from 视图名称 where 条件
        3.改变视图
            alter view ren_wei as  sql查询语句;
        4.删除视图
            drop  view ren_view;
    
    触发器
        1.创建触发器
            create trigger t2 AFTER INSERT on order_table 
                for each row  -- 固定写法
            BEGIN
         -- sql 
            update goods set num = num -2 where id = 1;

        end 
        动态获取参数
            create trigger t2 AFTER INSERT on order_table 
                for each row 
            BEGIN
             -- sql 
                update goods set num = num- new.much where id = new.gid;

            end 
        注意: new 表示获得插入的参数
 
        2.删除触发器
            drop  trigger 触发器;
        
    
    存储过程
        1.创建存储过程(体会封装)
            create PROCEDURE p1()
                BEGIN
                    
                    select * from ren;
                    select * from goods;

                end 
        2.创建存储过程(体会参数)
            create PROCEDURE p2(in num int)
            BEGIN
                
            select * from ren where p_sal > num;
            end
        3.创建存储过程(体会控制)
            create PROCEDURE p3(in num int,in flag char(1))
                BEGIN
                    if flag = '1' THEN    
                            select * from ren where p_sal >num;
                    elseif flag ='2' THEN
                            select * from ren where p_sal =num;
                    else 
                            select * from ren where p_sal < num;
                    end if;
                END
         4.创建存储过程(体会循环)
         
            create PROCEDURE p4(inout num int)
            BEGIN
                DECLARE i int DEFAULT 1; -- 声明变量
                DECLARE he int DEFAULT 0;

                while i<= num DO   -- 表示开始循环
                    set he = he +i;
                    set i = i+1;
                end while;
                -- select he; -- 显示结果
                set num = he;
            end
        
        5.删除存储过程
            drop PROCEDURE 名称;
            
        6.调用存储过程
            call 名称(参数值)
            
        7.显示当前库下的所用存储过程
            show PROCEDURE status;
    
    
        作业:http://www.cnblogs.com/wangfengming/p/7891939.html
            要求完成(10-15个sql查询)
    
    谈论表设计
View Code

 一些概念补充:

 视图:

视图就是一个虚拟表(并非真实存在),其本质是根据sql语句获取动态的数据集,为其命名,用户使用时只需要使用名称即可获取结果集,可以将该结果集当做表来使用.它是用来存储查询结果集的,就是我们的一句查询sql存入到视图中,然后再查询该结果的时候就从视图中就能查到,比如我们把一些嵌套的子查询存入到视图中,再查该数据的时候就从视图中查即可.

insert into tw_view values(201,'alex','egon','2017-10-01 12:00:30','风平浪静','','')
-- 这里需要备注一下,我们在使用视图的时候要把你copy的表格完整的copy过来,不能有遗漏(字段),否则在进行修改的时候会报错,

alter view tr_view as select name from tr -- 这里alter是关键字改变字段结构,view是固定用法,然后后面就是加上需要更改的表格的名字再后面就是as关键字,然后是sql语句,这个sql语句查询得到的结果放到前半句里面,就是根据tr_view这个表格把其他的字段都删除掉,只保留sql语句得到结果的字段. 

-- drop view tr_view 删除视图表格


-- 视图的存在就是为了增加权限,同样一个表格,每个人的级别不一样,看到的内容也不一样,对于有些内容是需要进行隐藏,但是隐藏之后又不能够影响用户的使用,这里的约束就类似于装饰器,我们需要增加功能之后同时也不可以改变用户的使用方法,
-- 基于这个需求我们就开始创建了视图,视图就相当于一个临时表格,是主表格的映射,对他进行修改增加内容都会影响到主表格,但是如果对试图表格进行删除的话,那么主表格是不会受其影响的,这里就是主副之别.
{要补充的一点是我们的数据库设计出来不单单是我们自己使用,还会牵涉到很多其他人使用,为了大家的使用都更加便捷,我们要尽可能少用到视图操作,把表格设计得更加合理化}

视图示例

create view actor_view as select id,name from actors ;  -- 创建视图

select * from actor_view;  -- 直接从我们所创建的视图中查询数据查所有数据
select name from actor_view;  -- 查单单列字段数据
drop view actor_view;  -- 删除视图
alter view actor_view as select name from actors ;  -- 修改视图
select * from actor_view;  -- 这里是查询我们修改过的视图里面的数据

视图示例二 

create view student_View as select student.id,student.sname,student.gender,
student.class_id,class.cname from student left join 
class on student.class_id=class.id;

这里可以进行update操作
update student_View set sname='shera' where id=2;

但是insert会报错 insert into student_View (id,sname,gender,class_id,cname) values (
5,'paul',1,1,'php');
报错信息如下:
[Err] 1471 - The target table student_View of the INSERT is not insertable-into

 触发器:

create trigger e before delete on order_table for each ROW
begin
update goods set num=num+old.much where id=old.gid;
END;
我们这里的应用场景是在购物过程中,买方购买商品后购物车里增加内容,然后卖方的仓库里面会减少内容,为了实现这个需求我们使用了触发器的功能,
这里解释一下,第一句create是创建一个触发器,使用delete方法是要用到before选项归属于delete方法,
on是固定搭配,不用delete方法还可以使用update方法,查询是不支持的,查询没有触发器可以用,on后面接的是需要使用到delete方法的该表格的名字,
然后就是固定搭配,begin.........end,中间加上sql逻辑语句,update是更改表格的内容,所要更改的表格是仓库里的库存的表格紧跟着就是固定搭配用法,for each ROW,
该库存商品的数量随着
卖出的数量不断的递减,所以我们需要拿到库存商品数量的对应的字段,然后在此基础上进行加减操作,这里因为使用的是delete方法,所以是消费者退货的
时候我们的库存数量逐渐递增,所以这里使用的是加法,然后后面的where是条件设定,要删除那一条数据根据这个where后面的内容来决定,

 触发器示例

-- CREATE TRIGGER tri_before_insert_act AFTER INSERT ON actors for EACH ROW
-- BEGIN
-- IF new.name = 'alex' THEN INSERT INTO USER (name)  
 VALUES ('judy');  # 数据库中的=是一个
-- end if;  # 在触发器中if和endif是对应的,需要加;号
-- END;
-- insert into actors (name) VALUE('alex');
# 如果插入的值name=alex,那么我们的user表中就插入一条数据,name是judy.

-- create TRIGGER tri_after_drop after DELETE on actors for EACH ROW
-- BEGIN
-- DELETE FROM USER where name=old.name; 
-- END;
-- DELETE from actors where name='alex';
# 当我们从actors表中删除一条数据的时候,被删除的名字如果跟user表中的名字一样,那么就把user表中的名字也删除掉

CREATE TRIGGER tri_befor_upd BEFORE UPDATE on actors for EACH ROW 
BEGIN
UPDATE USER set name=new.name where id=old.id;
END;
-- 这里的触发器作用就是我们要在actors表的update操作前增加一条指令,
-- 当我们的actors里面的id和user表中的id值相等的时候,我们的update表中name值就被改为我们的actors里面的更新后的值
UPDATE actors set name='jane' where id=2;

当用户修改一个订单的数量时,我们触发器修改怎么写?

create trigger f after update on order_table
for each ROW
BEGIN
update goods set num=num+old .much-new.much where id=new.gid;
END

-- 这里是update,这个update的意思是我们在购物车原有商品数量的基础上对该数量进行操作,
例如原来的数量是10,我们把它改成了15或者改成了10,这是update操作可以实现的,这才是重点,
而不是insert和delete!!!所以这里的old是原来的数据的样子,而new是改过之后的数据的样子,
所以我们在这里需要让商品库里加上原来的数据,然后再减掉我们更改过后的数据,这样就可以实现
需求------用户不论怎么改变商品数量,都可以保证商品库里面的数量的准确性.

存储过程:

存储过程包含了一系列可执行的sql语句,存储过程存放于MySQL中,通过调用它的名字可以执行其内部的一堆sql

(在innodb存储引擎中,如果你的字段里面没有指定主键,它系统会自动在内部找一个不为空只唯一的字段来作为主键,如果有好几个不为空且唯一的字段,那么系统就会从上往下依次寻找,遇到一个满足条件的,就选定它作为主键,如果所有的字段都不满足条件,系统会自动生造一个主键出来,由于主键是有非凡意义的存在,有主键使用索引查数据的时候会快很多倍,所以主键一定要自己设定好.必须要有)

 frm是innodb存储引擎的表结构form

ibd是innodb存储引擎的表数据-data(innodb是必须要有主键的,它按照主键聚集)

frm是myisam存储引擎的表结构form

myd是myisam存储引擎的data文件data

myi是myisam存储引擎的索引文件index

frm是blackhole存储引擎的表结构,里面是不会有任何数据的,凡是扔进去的数据都会消失,它是专门清空数据的

frm是memory存储引擎的表结构它的数据都是存放在内存里面

使用存储过程的优点:

#1. 用于替代程序写的SQL语句,实现程序与sql解耦

#2. 基于网络传输,传别名的数据量小,而直接传sql数据量大

使用存储过程的缺点:

#1. 程序员扩展功能不方便

create procedure p11(OUT num int) -- in 是入参,这里的参数需要数据类型来限制它,同理,out是出参,也需要数据类型来加以限制

BEGIN
DECLARE i int default 10; -- 这里是给上面的out定义一个默认值,因为这里是用的out,它只是出参,不接收传入的参数所以需要在这里
-- 给他在内部定义一个参数,
set num = i; -- 这里是把默认的参数赋值给上面的一开始定义的参数里面
END

set @n = 0; -- 这里我们设置一个变量,这个变量是用于接收上面的出参
call p11(@n); -- 这里不能直接用函数名加上空括号会报错,所以我们需要在里面加入一个变量来接收上面的出参,需要跟上面一步相关联,否则也会报错.

select @n; -- 这就类似于print,把结果打印出来

存储过程示例:

简单版:
create procedure actor_pro() -- 这里就是创建了存储过程,固定语法,要在存储过程的名字里加上() BEGIN select name from actors ; END; call actor_pro();
复杂版:

create PROCEDURE us_pro(
in id int,out name varchar(32), inout city varchar(30)
)
BEGIN
if (id>3) then set name='alex';set city='tibet';
end if;
if (id<3) then set name='wusir';set city = 'cingdao';
end if;
END;
set @name='peter';  # 这里是由上面我们的存储过程里面的参数类型决定的,只要是in类型的参数(输入参数)就在传参的时候,直接写入即可,如果是out(输出参数)/inout(输入输出参数)类型的参数就需要在这里使用set设置值,以=赋值的方式,然后把设置的值写入到我们的参数里面去.
set @city='peking';
CALL us_pro(4,@name,@city);
select @name as name,@city as city;

这个存储过程里面封装了一些逻辑判断,不是上面简单的sql语句了,我们在调用这个存储过程的时候,把相应的参数传进去[传参的时候,我们是用的位置参数,要对应上,不能随意调换位置],然后在里面就会执行逻辑判断,然后有返回结果,我们使用select语句就能得到返回值了.

in/out/inout三种参数类型以及declare跟set两种声明变量的方式区别
in/out/inout三种参数类型

create PROCEDURE demo_inout(inout p_inout int) BEGIN SELECT p_inout;
-- 这里的p_inout参数在我们存储过程的形参里面的变量 set p_inout=22; -- 这样写在存储过程里面的变量就不需要加上@符号,直接赋值即可 select p_inout; END; set @p_inout=100; -- 写在存储过程外面就需要加上@符号进行赋值 call demo_inout(@p_inout); -- 这里第一次显示的是我们的存储过程外面设置的值,inout是输入输出参数,可以被修改,也可以传入. -- 第二次再执行就变成了我们的存储过程里面设置的值了, -- 下面的select获取结果集也是一样第一次执行结果是外面设置的set的结果,第二次是里面存储过程里面的值 SELECT @p_inout; create PROCEDURE demo_out(out p_inout int) BEGIN SELECT p_inout; set p_inout=22; select p_inout; END; set @p_inout=100; call demo_out(@p_inout); -- 这里是out参数类型,输出参数,不接收传入的值, SELECT @p_inout;-- 所以这里我们获取的都是存储过程里面的值 DROP PROCEDURE demo_in;
create PROCEDURE demo_in(
in p_inout int) BEGIN SELECT p_inout; set p_inout=22; select p_inout; END; set @p_inout=10; call demo_in(@p_inout);-- 这里是in参数类型,输入参数,我们传入的是什么值就会在这里输出什么值,就执行这一句就可以得到结果 SELECT @p_inout;-- 这里就是我们在存储过程外面设置的值

declare跟set两种方式声明变量的区别
drop PROCEDURE demo_declare ; create PROCEDURE demo_declare() BEGIN declare s1 int default 0;
-- 这里我们使用declare声明一个变量,declare只是声明一个变量,然后set是对它进行赋值操作, set s1=10;-- set在这里赋值的只能是declare所声明的变量,或者是我们的存储过程里面所接收的参数,它不能自己声明变量并赋值 set @s2=@s2+2;-- 这里我们的set使用@符号语法就可以一步操作在声明变量的同时对其进行并赋值, -- SELECT s1,@s2; -- 我们在存储过程里面使用select语法就类似于python里面的print操作,我们在调用该存储过程的时候就可以直接得到这一句的结果 END; set @s2=5; -- 而加上@符号的就是全局变量,就可以在存储过程外部进行修改操作 set s1=22; -- 这里的s1是我们的存储过程里面的变量,不能在外部进行修改,相当于是局部变量 call demo_declare(); SELECT @s2,s1;-- [Err] 1054 - Unknown column 's1' in 'field list' 这里在外部是无法调用到我们的s1变量的,它是存储过程里面的局部变量

带事物的存储过程示例

-- 带事物的存储过程
create PROCEDURE p1(
out s8 TINYINT
)
BEGIN  -- 我们在这里创建事物,如果事物执行成功之后就会返回一个数值类似于状态码

DECLARE EXIT HANDLER for SQLEXCEPTION
BEGIN
-- ERRORS  -- 如果执行结果状态是errors就直接返回我们这里的errors里面设置的s8=1
set s8=1;
rollback;
END;

DECLARE EXIT HANDLER for sqlwarning 
BEGIN
-- WARNINGS  如果执行结果是warnings就返回这里的s8=10的结果值
set s8=10;
ROLLBACK;
END;

START TRANSACTION;  -- 这里是开启事物的语法,
DELETE from actors;-- 这里是把我们的表中所有数据清空
INSERT INTO USER (name,age,city) VALUES ('fred',39,'tibet'),('alvin',29,'hongkong');
-- 以上两个操作同时执行,要么同时提交,要么就是都回滚,
commit;

-- success  如果事物执行结果是success,就返回我们的s8=21的结果值
set s8=21;
END;
DROP PROCEDURE p1;
set @p=20;  -- 这里在存储过程外部设置一个全局变量,用于给我们的存储过程传参,这里我们的存储过程用的是out类型的参数,
call p1(@p);  -- 这里把参数传进去,因为是out类型的输出参数,所以即便传入进去也不会被接收,
select @p;  -- 所以这里我们打印的就是我们的存储过程里面的变量s8

存储过程中的游标用法

在检索出来的行中,前进或者后推一行或者多行,就需要用到所谓的"游标",游标不是某一个select语句,而死该语句检索出来的结果集,在mysql数据库中只能用于存储过程和函数

创建游标使用关键字,declare 和 cursor

游标是局限于存储过程,所以如果存储过程处理完成后,游标就消失了.在存储过程中要关键字open进行打开,于游标相关的select查询语句在定义时不执行,在open时才执行,存储检索出的数据以供浏览和滚动.游标使用完之后,close进行关闭.

使用游标数据,要用到关键字fetch访问数据,fetch从第一行开始,获取当前行的数据,每次执行后会移动内部行指针,再次调用fetch会检测到下一行,不会重复读取同一行数据.


create PROCEDURE ps() BEGIN DECLARE ssid int; DECLARE ssname varchar(
20); DECLARE done int DEFAULT false;-- 这里相当于定义一个flag, DECLARE my_cursor CURSOR for select name from user; -- 这里是设置游标 DECLARE CONTINUE HANDLER for not found set done = TRUE;-- 当我们获取完数据之后,改变标志位的值 open my_cursor; xxoo:LOOP -- LOOP关键字,这里是类似于for循环遍历每一条数据 FETCH my_cursor into ssname; -- 把游标取到的值赋值给我们上面声明的变量里面 if done then leave xxoo;-- 如果标志位值被改变就退出循环 end if; insert into actors (name) VALUES(ssname);-- 把我们取到的值insert到另一张表格中 end LOOP xxoo;-- 退出循环的完整步骤 close my_cursor;-- 关闭游标 end; call ps;

自定义函数示例:

create function f1(
q1 int,-- 这里是函数的参数
q2 int
)
returns INT  -- 返回的是我们的函数名加上参数
BEGIN
declare num int;  -- 函数体内声明变量
set num =q1+q2;-- 写逻辑代码
return (num);-- 具体的返回值,上面两个参数相加的结果值
END;
SELECT f1(1,15);-- 函数的调用使用select关键字+函数名(函数所需要的参数)


select f1(0,id),name from user; -- 在sql查询中使用我们的自定义函数

得到的结果如下图:

原文地址:https://www.cnblogs.com/2012-dream/p/8027856.html