zoukankan      html  css  js  c++  java
  • 数据库(十三):MySQL内置方法

    进击のpython

    *****

    数据库——MySQL内置方法


    视图

    视图是一个虚拟表(非真实存在),其本质是【根据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)
    
    

    李平老师的课程名是不是出来一张表,但是这个表我以前说过,这是假的,只是给你看的,不是真实存在的

    也就意味着我想用这个表是不可以的,那我要是想用就用到了视图:

    增加

    #语法:CREATE VIEW 视图名称 AS  SQL语句
    create view teacher_view as select tid from teacher where tname='李平老师';
    
    #于是查询李平老师教授的课程名的sql可以改写为
    mysql> select cname from course where teacher_id = (select tid from teacher_view);
    +--------+
    | cname  |
    +--------+
    | 物理   |
    | 美术   |
    +--------+
    rows in set (0.00 sec)
    
    

    !!!注意注意注意:

    1. 使用视图以后就无需每次都重写子查询的sql,但是这么效率并不高,还不如我们写子查询的效率高
    2. 而且有一个致命的问题:视图是存放到数据库里的,如果我们程序中的sql过分依赖于数据库中存放的视图,那么意味着,一旦sql需要修改且涉及到视图的部分,则必须去数据库中进行修改,而通常在公司中数据库有专门的DBA负责,你要想完成修改,必须付出大量的沟通成本DBA可能才会帮你完成修改,极其地不方便
    #修改视图,原始表也跟着改
    mysql> select * from course;
    +-----+--------+------------+
    | cid | cname  | teacher_id |
    +-----+--------+------------+
    |   1 | 生物   |          1 |
    |   2 | 物理   |          2 |
    |   3 | 体育   |          3 |
    |   4 | 美术   |          2 |
    +-----+--------+------------+
    rows in set (0.00 sec)
    
    mysql> create view course_view as select * from course; #创建表course的视图
    Query OK, 0 rows affected (0.52 sec)
    
    mysql> select * from course_view;
    +-----+--------+------------+
    | cid | cname  | teacher_id |
    +-----+--------+------------+
    |   1 | 生物   |          1 |
    |   2 | 物理   |          2 |
    |   3 | 体育   |          3 |
    |   4 | 美术   |          2 |
    +-----+--------+------------+
    rows in set (0.00 sec)
     
    mysql> update course_view set cname='xxx'; #更新视图中的数据
    Query OK, 4 rows affected (0.04 sec)
    Rows matched: 4  Changed: 4  Warnings: 0
    
    mysql> insert into course_view values(5,'yyy',2); #往视图中插入数据
    Query OK, 1 row affected (0.03 sec)
    
    mysql> select * from course; #发现原始表的记录也跟着修改了
    +-----+-------+------------+
    | cid | cname | teacher_id |
    +-----+-------+------------+
    |   1 | xxx   |          1 |
    |   2 | xxx   |          2 |
    |   3 | xxx   |          3 |
    |   4 | xxx   |          2 |
    |   5 | yyy   |          2 |
    +-----+-------+------------+
    rows in set (0.00 sec)
    

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

    修改

    语法:ALTER VIEW 视图名称 AS SQL语句
    mysql> alter view teacher_view as select * from course where cid>3;
    Query OK, 0 rows affected (0.04 sec)
    
    mysql> select * from teacher_view;
    +-----+-------+------------+
    | cid | cname | teacher_id |
    +-----+-------+------------+
    |   4 | xxx   |          2 |
    |   5 | yyy   |          2 |
    +-----+-------+------------+
    rows in set (0.00 sec)
    

    删除

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

    触发器

    触发器,就是一碰就执行的意思

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

    创建

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

    使用

    #准备表
    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 // # 规定终止符号,否则就是;结尾就算结束了
    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 ;
    
    
    #往表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 | 2017-09-14 22:18:48 |
    |  2 | useradd xxx     | 2017-09-14 22:18:48 |
    +----+-----------------+---------------------+
    rows in set (0.00 sec)
    
    

    ①刚开始规定结尾,最后要还原成;

    ②创建触发器一定记得分号

    ③new表示即将插进去的数据,old表示即将要删掉的数据

    删除

    drop trigger tri_after_insert_cmd;
    

    存储过程

    mysql一直在做一件事,力求让应用层面的开发和数据库层面的开发解耦开

    所以在数据库层面处理过的数据本质上还是拿给应用层面使用

    那就需要一种方法,可以将输入的sql语句封装起来,然后发给应用层

    而这就是存储过程,就相当于你是用内置方法写好后用存储过程封装

    (那其实跟函数很像,但是他不叫函数哦)

    无参

    delimiter //
    create procedure p1()
    BEGIN
        select * from blog;
        INSERT into blog(name,sub_time) values("xxx",now());
    END //
    delimiter ;
    
    

    那这存储过程写好了怎么用呢?

    在MySQL是这么用的:

    #在mysql中调用
    call p1() 
    
    

    在pymysql是这么用的:

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

    有参

    mysql的有参存储过程里面的参数不像python那么自由

    他需要定义参数的类型,以及要明确他是传入还是传出的参数

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

    in 仅用于传入参数用

    delimiter //
    create procedure p2(
        in n1 int,
        in n2 int
    )
    BEGIN
        
        select * from blog where id > n1;
    END //
    delimiter ;
    
    #在mysql中调用
    call p2(3,2)
    
    #在python中基于pymysql调用
    cursor.callproc('p2',(3,2))
    print(cursor.fetchall())
    
    in:传入参数
    

    out 仅用于返回值用

    delimiter //
    create procedure p3(
        in n1 int,
        out res int
    )
    BEGIN
        select * from blog where id > n1;
        set res = 1;
    END //
    delimiter ;
    
    #在mysql中调用
    set @res=0; #0代表假(执行失败),1代表真(执行成功)
    call p3(3,@res);
    select @res;
    
    #在python中基于pymysql调用
    cursor.callproc('p3',(3,0)) #0相当于set @res=0
    print(cursor.fetchall()) #查询select的查询结果
    
    cursor.execute('select @_p3_0,@_p3_1;') #@p3_0代表第一个参数,@p3_1代表第二个参数,即返回值
    print(cursor.fetchall())
    
    out:返回值
    

    inout 既可以传入又可以当作返回值

    delimiter //
    create procedure p4(
        inout n1 int
    )
    BEGIN
        select * from blog where id > n1;
        set n1 = 1;
    END //
    delimiter ;
    
    #在mysql中调用
    set @x=3;
    call p4(@x);
    select @x;
    
    
    #在python中基于pymysql调用
    cursor.callproc('p4',(3,))
    print(cursor.fetchall()) #查询select的查询结果
    
    cursor.execute('select @_p4_0;') 
    print(cursor.fetchall())
    
    inout:既可以传入又可以返回
    

    事务

    事务用于将某些操作的多个SQL作为原子性操作

    一旦有某一个出现错误,即可回滚到原来的状态,从而保证数据库数据完整性

    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元
    commit;
    
    #出现异常,回滚到初始状态
    start transaction;
    update user set balance=900 where name='wsb'; #买支付100元
    update user set balance=1010 where name='egon'; #中介拿走10元
    uppdate user set balance=1090 where name='ysb'; #卖家拿到90元,出现异常没有拿到
    rollback;
    commit;
    mysql> select * from user;
    +----+------+---------+
    | id | name | balance |
    +----+------+---------+
    |  1 | wsb  |    1000 |
    |  2 | egon |    1000 |
    |  3 | ysb  |    1000 |
    +----+------+---------+
    rows in set (0.00 sec)
    

    这是事务所要达到的效果,那真正的事务是怎么建的呢?

    其实你也可以想到,事物的回滚就有点像捕捉错误异常,当捕捉到错误异常,就进行回滚操作

    那什么时候执行回滚呢?是不是就应该是程序执行到错误才回滚,你正确执行还会滚个毛线

    所以这伪代码就出来了~

    delimiter //
    create procedure p4(out status int)
    BEGIN
    1.声明如果出现异常则执行{
    set status = 1;
    rollback;
    }
    开始事务
    ...
    commit;
    结束
    set status = 2;
    END//
    delimter ;
    

    代码实现

    基于上面的伪代码,现在实现一个真正的事务:

    delimiter //
    create procedure p4(out p5_return_code int)
    BEGIN
        DECLARE exit handler for  sqlexception
        BEGIN
            set p5_return_code = 1;
            rollback;
        END;
        DECLARE exit handler for sqlwarning
        BEGIN
            set p5_return_code = 2;
            rollback;
        END;
        START TRANSACTION;
            DELETE from tb1;
            insert into blog(name,sub_time) values('yyy',now());
        commit;
        set p5_return_code = 0;
    END//
    delimter ;
    

    这就写好了一个封装在存储过程中的事务~


    *****
    *****
  • 相关阅读:
    java web项目打包.war格式
    version 1.4.2-04 of the jvm is not suitable for thi
    Sugarcrm Email Integration
    sharepoint 2010 masterpage中必须的Content PlaceHolder
    微信开放平台
    Plan for caching and performance in SharePoint Server 2013
    使用自定义任务审批字段创建 SharePoint 顺序工作流
    Technical diagrams for SharePoint 2013
    To get TaskID's Integer ID value from the GUID in SharePoint workflow
    how to get sharepoint lookup value
  • 原文地址:https://www.cnblogs.com/jevious/p/11466843.html
Copyright © 2011-2022 走看看