zoukankan      html  css  js  c++  java
  • mysql-视图、触发器、函数、存储过程

    一.视图

    ​ 临时表 : select ... from (select ... from t1)

    ​ 某个查询语句设置别名即是创建视图

    1.创建

    可以在查询中 create view 创建视图

    create view 视图名 as (查询后的表)

    CREATE VIEW v1 as 
    SELECT *  FROM score WHERE  num > 10;
    

    2.查询

    临时表

    -- CREATE VIEW v1 as 
    -- SELECT *  FROM score WHERE  num > 10;
    
    select  num  from v1
    

    3.更新数据

    • 若原表数据变化,那么视图内的数据也会动态变化
    • 但是不能再视图里面插入数据

      -- 错误用法:v1(student_id,course_id,num) values()
      
    • 视图是虚拟的

    4.修改

    alter view 视图名称 as SQL

    CREATE VIEW v1 as SELECT *  FROM score WHERE  num > 10;
    -- 修改v1视图数据
    alter view v1 as  
    SELECT * from class  WHERE cid>2
    

    5.删除视图

    drop view 视图名称

    DROP view v1
    

    6.应用

    实际应用中,代码中读取包含视图sql语句可读性差,往往会选择直接读取数据库;涉及到修改时,也需要协同修改数据库中的视图,不推荐使用

    二、触发器

    当对某张表做增删改时,可以使用触发器自定义关联行为

    应用在增、删、改中

    1.设置触发器

    基本框架

    create trigger 触发器名称 执行操作(before insert、after insert...) for each row (表示每一行) begin 执行操作 END

    --  写死
    delimiter //
    CREATE TRIGGER t1 BEFORE INSERT on student for each row 
    begin
    insert  into teacher(tname) VALUES('王毅老师');
    insert  into teacher(tname) VALUES('李毅老师');
    insert  into teacher(tname) VALUES('黄健翔老师');
    insert  into teacher(tname) VALUES('王毅老师');
    -- 这里需要delimiter;  否则触发器不生效
    END //
    delimiter ;
    
    --  动态设置 OLD NEW
    -- insert 时new 新数据 
    delimiter //
    CREATE TRIGGER t1 BEFORE INSERT on student for each row 
    begin
    insert  into teacher(tname) VALUES(NEW.sname); 
    -- 这里sname表示insert其他表所需要传入的数据
    END //
    delimiter ;
    -- insert 操作后此时sname输入也会传入到teacher表中
    insert into student(gender,class_id,sname)VALUES('女',1,'阿宁'),('男',2,'吴邪');
    
    • 注:由于begin 内需要是执行语句,那么触发器语句块会由于;不能整个执行

      故需要通过delimiter 设定结尾符号

      -- 默认分号;结尾表示语句解释
      -- 可以通过delimiter 符号  对结束符号进行修改
      
    • 注:NEW表示即将插入的数据行(新数据),OLD表示即将删除的数据行(老数据),update 既有NEW也有OLD

    2.执行操作

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

    • 执行insert操作后,原teacher表会在insert 前自动插入'王毅老师'这条数据

    • 当该语句insert两条数据,那么teacher也会插入两条数据

    insert into student(gender,class_id,sname)VALUES('女',1,'阿宁');
    
    • 触发器语法

      # 插入前
      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
      

    三、函数

    聚合函数 count 、sum、avg...

    内置函数

    1.如char_length(字符长度)、concat(字符串拼接)
    2.应用场景 -时间格式化操作

    官方文档

    • curdata()目前时间

    • data_format

    -- 当前时间
    SELECT CURRENT_DATE()
    -- 时间格式化
    SELECT DATE_FORMAT(CURRENT_DATE,'%Y-%m')
    

    文档示例

    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'
    
    案例:

    获取该表blog内时间的年月,以及文章数量

    id titile ctime
    1 adsd 2020-11-28
    2 dsa 2020-11-27
    3 touday 2020-11-15
    4 youcao 2020-12-15
     select data_format(ctime,'%Y-%m'),count(1) from blog group by data_format(ctime,'%Y-%m')
    

    自定义函数

    delimiter \
    create function f1(
        i1 int,
        i2 int)
    returns int
    BEGIN
        declare num int;
        set num = i1 + i2;
        return(num);
    END \
    delimiter ;
    
    • 自定义函数报错
    ERROR 1418 (HY000): This function has none of DETERMINISTIC, NO SQL, or READS SQL DATA in its declaration and binary logging is enabled (you *might* want to use the less safe log_bin_trust_function_creators variable)
    
    • 解决方式
    mysql>set global log_bin_trust_function_creators=1; 
    

    删除函数

    drop FUNCTION 函数名
    

    执行函数

    # 获取返回值
    declare @i VARCHAR(32);
    select UPPER('alex') into @i;
    SELECT @i;
    
    # 在查询中使用
    select f1(11,nid) ,name from tb2;
    

    补充:mysql内置函数

    更多函数

    CHAR_LENGTH(str)
            返回值为字符串str 的长度,长度的单位为字符。一个多字节字符算作一个单字符。
            对于一个包含五个二字节字符集, LENGTH()返回值为 10, 而CHAR_LENGTH()的返回值为5。
    
        CONCAT(str1,str2,...)
            字符串拼接
            如有任何一个参数为NULL ,则返回值为 NULL。
        CONCAT_WS(separator,str1,str2,...)
            字符串拼接(自定义连接符)
            CONCAT_WS()不会忽略任何空字符串。 (然而会忽略所有的 NULL)。
    
        CONV(N,from_base,to_base)
            进制转换
            例如:
                SELECT CONV('a',16,2); 表示将 a 由16进制转换为2进制字符串表示
    
        FORMAT(X,D)
            将数字X 的格式写为'#,###,###.##',以四舍五入的方式保留小数点后 D 位, 并将结果以字符串的形式返回。若  D 为 0, 则返回结果不带有小数点,或不含小数部分。
            例如:
                SELECT FORMAT(12332.1,4); 结果为: '12,332.1000'
        INSERT(str,pos,len,newstr)
            在str的指定位置插入字符串
                pos:要替换位置其实位置
                len:替换的长度
                newstr:新字符串
            特别的:
                如果pos超过原字符串长度,则返回原字符串
                如果len超过原字符串长度,则由新字符串完全替换
        INSTR(str,substr)
            返回字符串 str 中子字符串的第一个出现位置。
    
        LEFT(str,len)
            返回字符串str 从开始的len位置的子序列字符。
    
        LOWER(str)
            变小写
    
        UPPER(str)
            变大写
    
        LTRIM(str)
            返回字符串 str ,其引导空格字符被删除。
        RTRIM(str)
            返回字符串 str ,结尾空格字符被删去。
        SUBSTRING(str,pos,len)
            获取字符串子序列
    
        LOCATE(substr,str,pos)
            获取子序列索引位置
    
        REPEAT(str,count)
            返回一个由重复的字符串str 组成的字符串,字符串str的数目等于count 。
            若 count <= 0,则返回一个空字符串。
            若str 或 count 为 NULL,则返回 NULL 。
        REPLACE(str,from_str,to_str)
            返回字符串str 以及所有被字符串to_str替代的字符串from_str 。
        REVERSE(str)
            返回字符串 str ,顺序和字符顺序相反。
        RIGHT(str,len)
            从字符串str 开始,返回从后边开始len个字符组成的子序列
    
        SPACE(N)
            返回一个由N空格组成的字符串。
    
        SUBSTRING(str,pos) , SUBSTRING(str FROM pos) SUBSTRING(str,pos,len) , SUBSTRING(str FROM pos FOR len)
            不带有len 参数的格式从字符串str返回一个子字符串,起始于位置 pos。带有len参数的格式从字符串str返回一个长度同len字符相同的子字符串,起始于位置 pos。 使用 FROM的格式为标准 SQL 语法。也可能对pos使用一个负值。假若这样,则子字符串的位置起始于字符串结尾的pos 字符,而不是字符串的开头位置。在以下格式的函数中可以对pos 使用一个负值。
    
            mysql> SELECT SUBSTRING('Quadratically',5);
                -> 'ratically'
    
            mysql> SELECT SUBSTRING('foobarbar' FROM 4);
                -> 'barbar'
    
            mysql> SELECT SUBSTRING('Quadratically',5,6);
                -> 'ratica'
    
            mysql> SELECT SUBSTRING('Sakila', -3);
                -> 'ila'
    
            mysql> SELECT SUBSTRING('Sakila', -5, 3);
                -> 'aki'
    
            mysql> SELECT SUBSTRING('Sakila' FROM -4 FOR 2);
                -> 'ki'
    
        TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str) TRIM(remstr FROM] str)
            返回字符串 str , 其中所有remstr 前缀和/或后缀都已被删除。若分类符BOTH、LEADIN或TRAILING中没有一个是给定的,则假设为BOTH 。 remstr 为可选项,在未指定情况下,可删除空格。
    
            mysql> SELECT TRIM('  bar   ');
                    -> 'bar'
    
            mysql> SELECT TRIM(LEADING 'x' FROM 'xxxbarxxx');
                    -> 'barxxx'
    
            mysql> SELECT TRIM(BOTH 'x' FROM 'xxxbarxxx');
                    -> 'bar'
    
            mysql> SELECT TRIM(TRAILING 'xyz' FROM 'barxxyz');
                    -> 'barx'
    
    部分内置函数
    

    四、存储过程

    直接调用名称()即可获得相应数据,可以代替程序员写SQL

    • 存储过程就是具有名字的一段代码,用来完成一个特定的功能。
    • 创建的存储过程保存在数据库的数据字典中

    区别于视图:

    • 存储过程可以直接调用
    • 存储过程中同时增、查

    1.创建

    (1)查询-运行即可,可在数据库-函数内查看存储过程

    a、简单示例
    delimiter \
    create PROCEDURE p1()
    BEGIN
    	SELECT  *  from student;
    	insert into teacher(tname)values('王毅老师');
    END \
    delimiter ;
    

    调用

    -- mysql终端调用
    clall p1()
    
    #python 调用
    import pymysql
    
    conn=pymysql.connect(host='localhost',user='root',password='password',database='db666')
    cursor=conn.cursor()
    cursor.callproc('p1')
    #涉及修改要提交
    conn.commit()
    
    #获取查询数据
    result=cursor.fetchall()
    print(result) #乱码需要在connect 设置charset='utf-8'
    conn.close()
    cursor.close()
    
    b、传参
    • 区别于函数: 内部语句可以select * from ...

    • 传参数内部有关键字(in,out,inout)

    • IN输入参数:表示调用者向过程传入值(传入值可以是字面量或变量)

    • OUT输出参数:表示过程向调用者传出值(可以返回多个值)(传出值只能是变量)

    • INOUT输入输出参数:既表示调用者向过程传入值,又表示过程向调用者传出值(值只能是变量)

      • in
      delimiter \
      create procedure p2(
      in n1 INT,
      in n2 INT)
      BEGIN
      select  *  from student where sid>n1;
      END \
      delimiter ;
      
      -- 调用,这里n2不能为空也需要传入一个
      call p2(15,2)
      
      #python 调用时,在callproc中传入参数即可
      import pymysql
      
      conn=pymysql.connect(host='localhost',user='root',password='password',database='db666')
      cursor=conn.cursor()
      cursor.callproc('p2',(15,2))
      #涉及修改要提交
      conn.commit()
      
      #获取查询数据
      result=cursor.fetchall()
      print(result) #乱码需要在connect 设置charset='utf-8'
      conn.close()
      cursor.close()
      
      • out 存储过程中无返回值,使用out
      -- 这里set了n2的值
      delimiter \
      create procedure  p3(
      in n1 int ,
      out n2 int)
      BEGIN
      select  *  from sid <n1;
      set n2=12356;
      END\
      delimiter ;
      
      -- 调用p3,获取v1的值
      -- 先定义全局中的v1,参数传入后,获得函数后的v1值
      set @v1= 3; 
      call p3(5,@v1);
      select @v1;
      
      -- 另一种表示:
      set @_p3_0=15
      set @_p3_1=2
      call p3(@_p3_0,@_p3_1)
      
      
      #python调用
      import pymysql
      
      conn=pymysql.connect(host='localhost',user='root',password='password',database='db666')
      cursor=conn.cursor()
      cursor.callproc('p3',(15,2))
      #涉及修改要提交
      conn.commit()
      #获取查询数据
      result=cursor.fetchall()
      print(result) #乱码需要在connect 设置charset='utf-8'
      
      
      #out获取n2的结果,需要再执行一次  @_存储过程名_参数index
      cursor.execute('select @_p3_0,@_p3_1')
      #获取查询数据
      result=cursor.fetchall()
      print(result) #乱码需要在connect 设置charset='utf-8'
      conn.close()
      cursor.close()
      
      • inout

        out中p3传入的2将是有意义的值了

    2.调用存储过程

    call sp_name[(传参)];
    

    3.补充

    • 可传参:in out inout

    • 调用call

    • out 用于标识存储过程的执行结果 这样可以通过返回的值,查看是否成功

    4、事务

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

    存储过程中使用事务的思想,类似python中try except的结构

    1.基本框架
    delimiter \
    create procedure p1()
    begin 
      ----a加10
      ----b减10  
    end //
    delimiter ;
    
    2.声明异常
    delimiter \
    create procedure p1(
    out status int
    )
    begin 
    	1.声明如果出现异常则执行{操作     
    	 set status=1 这里通过out 返回status的值如果返回1说明有异常
    	}
    		
      	----a加10
      	----b减10  
    end //
    delimiter ;
    
    3.异常实现,开始事务和结束事务,commit提交操作 
    delimiter \
    create procedure p1(
    out status int
    )
    begin 
    	1.声明如果出现异常则执行{操作     
    	 set status=1 这里通过out 返回status的值如果返回1说明有异常
    	}
    	开始事务	
      	----a加10
      	----b减10
      	commitl;
        结束
    end //
    delimiter ;
    
    
    4.回滚 rollback
    
    delimiter \
    create procedure p1(
    out status int
    )
    begin 
    	1.声明如果出现异常则执行{操作     
    	 set status=1 这里异常out一个值并且回滚
    	 rollback;
    	}
    	开始事务	
      	----a加10
      	----b减10
      	commitl;
        结束
        set status=2 成功执行out 一个值
    end //
    delimiter ;
    
    
    总结结构

    delimiter \

    create procedure 存储过程名(in 参数1名 参数类型 ,out return参数名 参数类型)

    begin

    declare exit handler for sqlexception

    begin

    set return 参数名 = 返回数值;

    rollback;

    END

    start transaction ;(开始事件)
    ​ delete ...;
    ​ insert...;
    update...;

    commit;

    set return参数值= 返回数值;

    end

    delimiter ;

    mysql 代码实现
    -- 支持事务的存储过程
    
    delimiter \
    create PROCEDURE p1(
        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 tb2(name)values('seven');
      COMMIT; 
     
      -- SUCCESS 
      set p_return_code = 0; 
     
      END\
    delimiter ;
    
    -- 
    set @v2=5
    call p1(@v2)
    select @v2
    

    5.游标(cursor)

    应用情景:对每一行每一列数据进行处理

    案例

    将A表数据按要求填入B表

    A表

    id num
    1 9
    2 8
    3 7

    B表

    id num
    1 1(A表id)+ A表num
    2 2 +A表num
    3 3+A表num
    delimiter \
    create procedure my_procedure() -- 创建存储过程
    begin -- 
    declare my_id int; -- 自定义变量1
    declare my_num varchar(50); -- 自定义变量2
    declare done int default false; -- 自定义控制游标循环变量,默认false
    declare temp int;
    
    declare My_Cursor CURSOR FOR ( SELECT id, num FROM A ); -- 定义游标并输入结果集
    declare CONTINUE HANDLER FOR NOT FOUND SET done = TRUE; -- 绑定控制变量到游标,游标循环结束自动转true
    
    
    OPEN My_Cursor; -- 打开游标
      myLoop: LOOP -- 开始循环体,myLoop为自定义循环名,结束循环时用到
        FETCH My_Cursor into my_id, my_num; -- 将游标当前读取行的数据顺序赋予自定义变量12
        IF done THEN -- 判断是否继续循环
          LEAVE myLoop; -- 结束循环
        END IF;
        -- 自己要做的事情,在 sql 中直接使用自定义变量即可
       set temp= my_id+my_num;
       insert into B(num)values(temp);
        COMMIT; -- 提交事务
      END LOOP myLoop; -- 结束自定义循环体
      CLOSE My_Cursor; -- 关闭游标
    END\ -- 结束存储过程
    delimiter ;
    

    6.动态执行sql,防sql注入

    伪代码及解析
    delimiter //
    create procedure p7(
    	in tql varchar(255),
        in arg int
    )
    begin 
    	1.预检测传入的sql语句的合法性
    	2.sql=格式化tql+arg
    	3.执行sql
    end //
    delimiter ;
    
    call p7('select * from tb where id>?',9) 
    
    代码
    delimiter \
    CREATE PROCEDURE p7 (
       in arg int )
    BEGIN
        set @xx =arg;
        -- prod 变量名
        PREPARE prod FROM 'select * from student where sid > ?';
        -- 格式化传入
        EXECUTE prod USING @xx;
        DEALLOCATE prepare prod; 
    END\
    delimiter ;
    

    7.删除存储过程与执行存储过程

    -- 删除
    drop procedure proc_name;
    
    -- 执行
    
    -- 无参数
    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)
    
    执行存储过程
    
    
    #python 执行存储过程
    #!/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)
    
    pymysql执行存储过程
    
    
    
    
  • 相关阅读:
    How to use Log4j 2 with Spring Boot
    SpringBoot使用Redis缓存
    win10下安装redis
    mysql 8.0.15修改密码
    spring-boot集成redis实现缓存功能
    Spring Boot(三):Spring Boot 中 Redis 的使用
    关于 iView-cdn模式
    HTML5 Audio-使用 Media 事件添加进度栏
    vue-cli中自定义路径别名 assets和static文件夹的区别
    CH9001: 各浏览器对常用或者错误的 Content-Type 类型处理方式不一致
  • 原文地址:https://www.cnblogs.com/yescarf/p/14080998.html
Copyright © 2011-2022 走看看