1. 程式人生 > >MySQL:檢視、觸發器、儲存過程、事務

MySQL:檢視、觸發器、儲存過程、事務

檢視:

檢視,虛擬表

建立虛擬表:

# 語法:
# create view 虛擬表名稱 as 虛擬表;
create view course_and_teacher as select * from course inner join teacher on course.teacher_id = teacher.tid;

# 虛擬表在硬碟上儲存時,只有 表結構, 沒有 表資料  那張表,即 只有 course_and_teacher.frm 這個檔案;因為虛擬表的資料來自於其它表

# 建立的虛擬表可以直接使用:
select * from course_and_teacher;
# 每次使用 course_and_teacher 這張表時,都會觸發  
select * from course inner join teacher on course.teacher_id = teacher.tid; # 虛擬表不建議使用,因為 如果 你在資料庫建了大量的檢視,不利用擴充套件(資料庫可能經常需要擴充套件;即強耦合);所以儘量用原生的 SQL # 另外,檢視是用來查詢的,不能用來修改(不要修改檢視中的記錄) # 修改檢視: # 語法: alert view 檢視名稱 as SQL語句; alert view course_and_teacher as select * from course where cid>3; # 刪除檢視:
# 語法:
drop view 檢視名稱; drop view course_and_teacher;

 

觸發器:

一碰就動;使用觸發器可以定製使用者對錶 【增、刪、改】操作時前後的行為(注意:沒有查詢)

# 一、建立觸發器:

# 針對 insert 可以是 before insert(對於每一行,在 insert 行為之前,去觸發一個 begin.. end 之間的 SQL語句), 也可以是 after insert (對於每一行,在 insert 行為之後,去觸發一個 begin.. end 之間的 SQL語句)

# 語法(插入前):
# create trigger
觸發器名稱 before insert on 表名 for each row # begin ... end # 插入前: 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 //  # delimiter 用於宣告 SQL語句的結束符 是 “//”,而不是 “;”
create trigger tri_after_insert_cmd after insert on cmd for each row
BEGIN
    # 觸發器提供了兩個物件:NEW 和 OLD;NEW代表新增的記錄,OLD代表以前老的記錄(插入行為沒有 OLD 一說,因為插入的永遠都是 新的;修改的情況下,才有老的記錄,同 NEW 也能派上用場)
    if NEW.success = 'no' THEN  # 等值判斷只有一個等號; NEW.success 表示 NEW物件的 success 屬性
            insert into errlog(err_cmd,err_time) values(NEW.cmd,NEW.sub_time);  # 必須加分號
    end if;   # 必須加分號
END//   # 此處的 “//” 表示 觸發器定義完了 
delimiter ;  # 重新宣告 SQL語句 的結束符 為 “;”

# NEW 表示即將插入的資料行,OLD 表示即將刪除的資料行
# 觸發器是 MySQL 的一個內建功能;我們也可以在 應用程式級別 自己去實現 觸發器的功能,如利用 python 程式碼自己去實現

# 建議在 應用程式級別去實現 觸發器的功能;方便以後擴充套件功能

# 使用觸發器: 觸發器無法由使用者直接呼叫,而是由於對 表的 【增、刪、改】 操作被動引發的
# 刪除觸發器:
drop trigger tri_after_insert_cmd;
# 檢視觸發器:
show triggers;

 

儲存過程:

儲存過程包含了一系列可執行的 sql語句,儲存過程存放於MySQL中,通過呼叫它的名字可以執行其內部的一堆 sql
    優點:
        1. 用於替代程式寫的SQL語句,實現程式與sql解耦
        2. 基於網路傳輸,傳別名的資料量小,而直接傳sql資料量大
    缺點:
        程式擴充套件功能不方便 

示例:

# 呼叫儲存過程 
# MySQL中呼叫:
call p1();

# python中呼叫:
cursor.callproc('p1')   # 用 cursor 去呼叫 callproc('儲存過程的名稱') 方法
# 如果 儲存過程中是查詢語句,利用相應的方法去獲取相應的結果:
print(cursor.fetchall())  

# 有參
delimiter //
create procedure p2(in n1 int,in n2 int,out res int)  # 1. MySQL中的儲存過程,引數必須指定 引數型別;2. 引數必須指明是用來接收值的,還是用來返回值的:in 代表傳入引數, out表示返回引數,inout表示可進可出
BEGIN
    select * from db7.student where tid>n1 and tid<n2;
    set res = 1;  # set 設定返回值
END //
delimiter ;
# MySQL 中呼叫:
set @x=0  # 設定變數的初始值
call p2(2,5,@x);  # 傳參
select @x;  # 檢視 返回值
# python 中呼叫:
cursor.callproc('p2',(2,5,0))  # callproc 在執行該儲存過程的時候會把引數做轉換: @_p2_0=2, @_p2_1=5, @_p2_2=0
# 如果 儲存過程中是查詢語句,利用相應的方法去獲取相應的結果:
print(cursor.fetchall()) 
# 檢視返回結果
cursor.execute('select @_p2_2')
print(cursor.fetchone())

 

應用程式與資料庫結合使用的三種方式:

方式一:
    MySQL:編寫儲存過程
    Python:呼叫儲存過程
    
方式二:
    Python:編寫純生SQL
    MySQL:啥也不幹
    
方式三:
    Python:ORM
    MySQL:啥也不幹

 

事務:

事務用於將某些操作的多個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;  # 上述 update 操作的資料只是儲存在記憶體中,此時 rollback 能讓資料回滾到最初的狀態;commit之後 rollback 就沒有作用了
commit;   # 想讓記憶體的資料儲存到 資料庫中,則需要 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;
                }

                開始事務
                    -- 由秦兵賬戶減去100
                    -- 方少偉賬戶加90
                    -- 張根賬戶加10
                    commit;
                結束

                set status = 2;


            END //
            delimiter ;

#實現
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; # 開啟事務
        DELETE from tb1; 
        insert into blog(name,sub_time) values('yyy',now());
    COMMIT; 

    -- SUCCESS 
    set p_return_code = 0; #0代表執行成功

END //
delimiter ;

python的事務開啟:

# coding=utf-8
import pymysql
 
#新增資料
 
conn = pymysql.connect(host='localhost', port=3306, user='root', passwd='', db='yyy')
 
cursor = conn.cursor()
 
 
try:
    insertSQL0="INSERT INTO ACCOUNT2 (name,balance) VALUES ('bart',1000)"
    insertSQL1="UPDATE account2 set balance=balance-30 WHERE name='alex'"
    insertSQL2="UPDATE account2 set balance=balance+30 WHERE name='ego'"
 
    cursor = conn.cursor()
 
    cursor.execute(insertSQL0)
    conn.commit()
 
     #  insertSQL1 和 insertSQL2 是事務中的操作
    cursor.execute(insertSQL1)
    raise Exception        #模擬出現錯誤
    cursor.execute(insertSQL2)
    cursor.close()
    conn.commit()
 
except Exception as e:
 
    conn.rollback()      #回滾
    conn.commit()
 
 
cursor.close()
conn.close()