進擊の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)
!!!注意注意注意:
- 使用視圖以后就無需每次都重寫子查詢的sql,但是這么效率並不高,還不如我們寫子查詢的效率高
- 而且有一個致命的問題:視圖是存放到數據庫里的,如果我們程序中的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 ;
這就寫好了一個封裝在存儲過程中的事務~