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执行存储过程



原文地址:https://www.cnblogs.com/yescarf/p/14080998.html