數據庫(十三):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 ;

這就寫好了一個封裝在存儲過程中的事務~


*****
*****


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM