zoukankan      html  css  js  c++  java
  • 视图、触发器、事务、存储过程、函数、流程控制

    一、视图

    1.什么是视图

    一个查询语句的结果是一张虚拟表,将这种虚拟表保存下来 它就变成了一个视图

    2.为什么要用视图

    当频繁需要用到多张表的连表结果,你就可以事先生成好视图 之后直接调用即可,避免了反复写连表操作的sql语句

    3.如何使用视图

    创建视图语句:

     create view teacher_course as select * from teacher INNER JOIN course
        on teacher.tid = course.teacher_id;

    注意:

    1.视图只有表结构,视图中的数据是来源于原来的表

    2.不要改动视图表中的数据

    3.一般情况下不会频繁的使用视图来写业务逻辑

    二、触发器

    1.什么是触发器

    触发器是达到某个条件自动触发,

    当对数据进行增、删、改的情况下会自动触发触发器的运行

    2.为何要使用触发器

    触发器专门针对我们对某一张表数据增insert、删delete、改update的行为,这类行为一旦执行 就会触发触发器的执行,即自动运行另外一段sql代码

    3.固定语法结构

    create trigger 触发器的名字 after/before insert/update/delete on 表名 for each row
                begin
                    sql语句
                end
       
    
    如下:
    t1为表名
    
    触发器名:tri_befor_t1
    
    在触发器之前或者之后进行插入或更改或删除t1
    
    create trigger tri_before(after)_t1(insert/update/delete) after/before insert/update/delete on t1 for each row
                begin
                    sql语句
                end
    View Code
    delimiter $$  # 只对当前窗口有效
    create trigger tri_after_insert_user after insert on user for each row
    begin
        insert into user(name,password) values('jason','123');
    end $$
    delimiter ;
    插入数据例子

    4.创建触发器

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

    补充:NEW对象指代的就是当前记录 

    delimiter $$ 
    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 $$
     delimiter ;
    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 $$  # 将mysql默认的结束符由;换成$$
    create trigger tri_after_insert_cmd after insert on cmd for each row
    begin
        if NEW.success = 'no' then  # 新记录都会被MySQL封装成NEW对象
            insert into errlog(err_cmd,err_time) values(NEW.cmd,NEW.sub_time);
        end if;
    end $$
    delimiter ;  # 结束之后记得再改回来,不然后面结束符就都是$$了
    
    #往表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');
    
    # 查询errlog表记录
    select * from errlog;
    # 删除触发器
    drop trigger tri_after_insert_cmd;
    案例

    三、事务

    1.什么是事务

    开启一个事务可以包含一些sql语句,这些sql语句要么同时成功 要么一个都别想成功,称之为事务的原子性

    2.事务的作用

    保证了对数据操作的数据安全性

    事务具有的4个特性

    原子性(atomicity):一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。 一致性(consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态。一致性与原子性是密切相关的。 隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。 持久性(durability):持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。

    3.如何使用

    主要操作:

    如何开启事务
        start transaction
    事务回滚
        rollback
    永久性更改
        commit
    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元
    
    # 回滚到上一个状态
    rollback;
    
    # 开启事务之后,只要没有执行commit操作,数据其实都没有真正刷新到硬盘
    commit;
    """开启事务检测操作是否完整,不完整主动回滚到上一个状态,如果完整就应该执行commit操作"""
    
    # 站在python代码的角度,应该实现的伪代码逻辑,
    try:
        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元
    except 异常:
        rollback;
    else:
        commit;

    4.事务的隔离级别

    DB使用的隔离级别不仅影响数据库的并发性,而且影响并发应用程序的性能。通常,使用的隔离级别越严格,并发性就越小,某些应用程序的性能可能会随之越低,因为它们要等待资源上的锁被释放。

    Dirty Read 脏读

    NonRepeatable Read 不可重复读

    Phantom Read 幻读

    Read uncommitted 未提交读

    Possible

    Possible

    Possible

    Read committed 提交读

    Not possible

    Possible

    Possible

    Repeatable read 可重复读

    Not possible

    Not possible

    Possible

    Serializable 可串行化

    Not possible

    Not possible

    Not possible

    未提交读:

    隔离级别是最不严格的隔离级别,现实中应用很少。在使用这个隔离级别时,仅当另一个事务试图删除或更改被检索的行所在的表时,才会锁定一个事务检索的行。因为在使用这种隔离级别时,行通常保持未锁定状态,所以脏读、不可重复读和幻像读都可能会发生。

    提交读是Oracle默认的隔离级别:

    事务中的每一条语句都遵从语句级的读一致性,即一个语句所处理的数据集是在单一时间点上的数据集,这个时间点是这个语句开始的时间,一个语句看不见在它开始执行后提交的修改

    可重复读是Mysql的默认隔离级别:

    在该隔离级别下,一个事务的影响完全与其他并发事务隔离,脏读、不可重复的读、幻像读现象都不会发生。当使用可重复读隔离级别时,在事务执行期间会锁定该事务以任何方式引用的所有行。因此,如果在同一个事务中发出同一个SELECT语句两次或更多次,那么产生的结果数据集总是相同的。因此,使用可重复读隔离级别的事务可以多次检索同一行集,并对它们执行任意操作,直到提交或回滚操作终止该事务。。

    可串行化

    这是数据库最高的隔离级别,这种级别下,事务“串行化顺序执行”,也就是一个一个排队执行。

    这种级别下,“脏读”、“不可重复读”、“幻读”都可以被避免,但是执行效率奇差,性能开销也最大,所以基本没人会用。

    脏读(dirty read):读取未提交的数据。A事务读取B事务尚未提交的数据,此时如果B事务发生错误并执行回滚操作,那么A事务读取到的数据就是脏数据

    非重复读(nonrepeatable read:多次读取,内容不一致。事务A多次读取一条数据,比如第一次读取数据为10,事务B将数据改为20,事务A第二次读取数据就不一样

    幻读(phantom read:前后多次读取,数据总量不一致。事务A在执行读取操作,需要两次统计数据的总量,前一次查询数据总量后,此时事务B执行了新增数据的操作并提交后,这个时候事务A读取的数据总量和之前统计的不一样,就像产生了幻觉一样,平白无故的多了几条数据,成为幻读。

    四、存储过程

    1.什么是存储过程

    存储过程类似于python中的自定义函数,内部封装了操作数据库的sql语句,

    后续先要实现相应的操作,只需要调用存储过程即可

    使用存储过程的优点

    #1. 用于替代程序写的SQL语句,实现程序与sql解耦
    
    #2. 基于网络传输,传别名的数据量小,而直接传sql数据量大

    使用存储过程的缺点

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

    2.三种开发模式

    第一种

    """
    应用程序:只需要开发应用程序的逻辑
    mysql:编写好存储过程,以供应用程序调用
    优点:开发效率,执行效率都高
    缺点:考虑到人为因素、跨部门沟通等问题,会导致扩展性差
    """

    第二种

    """
    应用程序:除了开发应用程序的逻辑,还需要编写原生sql
    优点:比方式1,扩展性高(非技术性的)
    缺点:
    1、开发效率,执行效率都不如方式1
    2、编写原生sql太过于复杂,而且需要考虑到sql语句的优化问题
    """

    第三种

    """
    应用程序:开发应用程序的逻辑,不需要编写原生sql,基于别人编写好的框架来处理数据,ORM
    优点:不用再编写纯生sql,这意味着开发效率比方式2高,同时兼容方式2扩展性高的好处
    缺点:执行效率连方式2都比不过
    """

    3.创建存储过程

       语法结果:
       delimiter $$
        create procedure p1()
        begin
            select * from user;
        end $$
        delimiter ;
        #使用callp1();
        调用即可
        
        
        带参数的:
                delimiter $$
            create procedure p1(
                in m int,  # 不能被返回出去
                in n int,
                out res int,  # 可以被返回
                inout xxx int,  # 既可以进又可以出
            )
            begin
                select * from user;
            end $$
            delimiter ;

    4.如何存储过程

    # 大前提:存储过程在哪个库下面创建的只能在对应的库下面才能使用!!!
    
    # 1、直接在mysql中调用
    set @res=10  # res的值是用来判断存储过程是否被执行成功的依据,所以需要先定义一个变量@res存储10
    call p1(2,4,10);  # 报错
    call p1(2,4,@res);  
    
    # 查看结果
    select @res;  # 执行成功,@res变量值发生了变化
    
    
    
    # 2、在python程序中调用
    import pymysql
    
    conn = pymysql.connect(
        host = '127.0.0.1',
        port = 3306,
        user = 'root',
        password = '123',
        database = 'day38',
        charset = 'utf8',
        autocommit = True
    )
    cursor = conn.cursor(pymysql.cursors.DictCursor)
    # call p1()  mysql中调用
    # callproc()  pymysql中调用
    cursor.callproc('p1',(1,5,10))
    # 内部自动用变量名存储了对应的值
    print(cursor.fetchall())
    """
    @_p1_0=1
    @_p1_1=5
    @_p1_2=10
    
    
    @_存储过程名_索引值
    """
    
    cursor.execute('select @_p1_0')
    print(cursor.fetchall())
    cursor.execute('select @_p1_1')
    print(cursor.fetchall())
    cursor.execute('select @_p1_2')
    print(cursor.fetchall())
    
    
    # 3、存储过程与事务使用举例(了解)
    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;
          update user set balance=900 where id =1;
          update user123 set balance=1010 where id = 2;
          update user set balance=1090 where id =3;
      COMMIT;
    
      -- SUCCESS
      set p_return_code = 0; #0代表执行成功
    
    
    END //
    delimiter ;
    View Code

    五、函数

    MYSQL中提供的内置函数

    一、数学函数
        ROUND(x,y)
            返回参数x的四舍五入的有y位小数的值
            
        RAND()
            返回0到1内的随机值,可以通过提供一个参数(种子)使RAND()随机数生成器生成一个指定的值。
    
    二、聚合函数(常用于GROUP BY从句的SELECT查询中)
        AVG(col)返回指定列的平均值
        COUNT(col)返回指定列中非NULL值的个数
        MIN(col)返回指定列的最小值
        MAX(col)返回指定列的最大值
        SUM(col)返回指定列的所有值之和
        GROUP_CONCAT(col) 返回由属于一组的列值连接组合而成的结果    
        
    三、字符串函数
    
        CHAR_LENGTH(str)
            返回值为字符串str 的长度,长度的单位为字符。一个多字节字符算作一个单字符。
        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)
            变大写
       
        REVERSE(str)
            返回字符串 str ,顺序和字符顺序相反。
            
        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'
                
    四、日期和时间函数
        CURDATE()或CURRENT_DATE() 返回当前的日期
        CURTIME()或CURRENT_TIME() 返回当前的时间
        DAYOFWEEK(date)   返回date所代表的一星期中的第几天(1~7)
        DAYOFMONTH(date)  返回date是一个月的第几天(1~31)
        DAYOFYEAR(date)   返回date是一年的第几天(1~366)
        DAYNAME(date)   返回date的星期名,如:SELECT DAYNAME(CURRENT_DATE);
        FROM_UNIXTIME(ts,fmt)  根据指定的fmt格式,格式化UNIX时间戳ts
        HOUR(time)   返回time的小时值(0~23)
        MINUTE(time)   返回time的分钟值(0~59)
        MONTH(date)   返回date的月份值(1~12)
        MONTHNAME(date)   返回date的月份名,如:SELECT MONTHNAME(CURRENT_DATE);
        NOW()    返回当前的日期和时间
        QUARTER(date)   返回date在一年中的季度(1~4),如SELECT QUARTER(CURRENT_DATE);
        WEEK(date)   返回日期date为一年中第几周(0~53)
        YEAR(date)   返回日期date的年份(1000~9999)
        
        重点:
        DATE_FORMAT(date,format) 根据format字符串格式化date值
    
           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'
            
    五、加密函数
        MD5()    
            计算字符串str的MD5校验和
        PASSWORD(str)   
            返回字符串str的加密版本,这个加密过程是不可逆转的,和UNIX密码加密过程使用不同的算法。
            
    六、控制流函数            
        CASE WHEN[test1] THEN [result1]...ELSE [default] END
            如果testN是真,则返回resultN,否则返回default
        CASE [test] WHEN[val1] THEN [result]...ELSE [default]END  
            如果test和valN相等,则返回resultN,否则返回default
    
        IF(test,t,f)   
            如果test是真,返回t;否则返回f
    
        IFNULL(arg1,arg2) 
            如果arg1不是空,返回arg1,否则返回arg2
    
        NULLIF(arg1,arg2) 
            如果arg1=arg2返回NULL;否则返回arg1 
    View Code
    CREATE TABLE blog (
        id INT PRIMARY KEY auto_increment,
        NAME CHAR (32),
        sub_time datetime
    );
    
    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');
    
    select date_format(sub_time,'%Y-%m'),count(id) from blog group by date_format(sub_time,'%Y-%m');
    需要掌握的函数:date_format

    1.自定义函数

    #!!!注意!!!
    #函数中不要写sql语句(否则会报错),函数仅仅只是一个功能,是一个在sql中被应用的功能
    #若要想在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 ;
    View Code
    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 ;
    View Code

    2.删除函数

    drop function func_name;

    3.执行函数

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

    六、流程控制

    1.条件语句

    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 ;
    if 条件语句

    2.循环语句

    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 ;
    while循环语句
    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 ;
    repeat循环语句
    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
    loop
  • 相关阅读:
    如何在自定义端口上运行 Spring Boot 应用程序?
    FileUpload拦截器
    UI标签
    OGNL
    你对测试最大的兴趣在哪里?为什么?
    举例说明同步和异步。
    git 克隆分支
    JQuery 选择器
    JQuery对象和Dom对象
    简单的JQuery之Ready
  • 原文地址:https://www.cnblogs.com/xiongying4/p/11401257.html
Copyright © 2011-2022 走看看