MySQL基礎(六):視圖、觸發器、事務、存儲過程、函數、流程控制

下面是小凰凰的簡介,看下吧!
💗人生態度:珍惜時間,渴望學習,熱愛音樂,把握命運,享受生活
💗學習技能:網絡 -> 雲計算運維 -> python全棧( 當前正在學習中)
💗您的點贊、收藏、關注是對博主創作的最大鼓勵,在此謝過!
有相關技能問題可以寫在下方評論區,我們一起學習,一起進步。
後期會不斷更新python全棧學習筆記,秉着質量博文爲原則,寫好每一篇博文。

一、視圖(瞭解)

1、什麼是視圖?

一個查詢語句的結果是一張虛擬表,將這種虛擬表保存下來 它就變成了一個視圖

2、爲什麼要用視圖?

當頻繁需要用到多張表的連表結果,你就可以事先生成好視圖 之後直接調用即可,避免了反覆寫連表操作的sql語句

3、如何使用視圖

語法:
	create view 視圖名 as 查詢語句
 	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、觸發器的創建

(1)創建語法
							在觸發器之前或者之後進行插入或更改或刪除t1
create trigger 觸發器的名字 after/before insert/update/delete on 表名 for each row
	begin
		sql語句
	end
觸發器命名規範:在t1表前設置觸發器 觸發器名應爲:tri_before_insert_t1
(2)delimiter設置語句終止符
delimiter $$  # 只對當前窗口有效,使sql語句的終止符由;變爲$$
create trigger tri_after_insert_user after insert on user for each row
begin
    insert into user(name,password) values('jason','123');
end $$ # 因爲上面的sql語句有一個;,不改終止符,那麼命令只會運行到sql語句,不會再往下運行!
delimiter ; # 注意
(3)NEW對象

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 ;
(4)觸發器案例
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、事務的作用

保證了對數據操作的數據安全性

3、事務的ACID特性

原子性(atomicity):一個事務是一個不可分割的工作單位,事務中包括的諸操作要麼都做,要麼都不做。 

一致性(consistency):事務必須是使數據庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的。 

隔離性(isolation):一個事務的執行不能被其他事務干擾。即一個事務內部的操作及使用的數據對併發的其他事務是隔離的,併發執行的各個事務之間不能互相干擾。

持久性(durability):持久性也稱永久性(permanence),指一個事務一旦提交,它對數據庫中數據的改變就應該是永久性的。接下來的其他操作或故障不應該對其有任何影響。

4、創建事務

(1)語法
如何開啓事務:start transaction
事務回滾:rollback
永久性更改:commit
(2)案例
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;

注意:

  • 事務的回滾只有在還沒有commit提交寫入到硬盤之前,在內存中才能回滾事務!
  • 事務所做的操作寫入硬盤用commit,pymysql在對數據庫進行增刪改操作時,默認也是修改的內存中的數據,也需要commit提交寫入硬盤。我們在數據庫進行grant授權等操作還有一個flush privileges也可以將信息從內存寫入硬盤

5、事務的隔離級別

(1)簡介

DB使用的隔離級別不僅影響數據庫的併發性,而且影響併發應用程序的性能。通常,使用的隔離級別越嚴格,併發性就越小,某些應用程序的性能可能會隨之越低,因爲它們要等待資源上的鎖被釋放。
在這裏插入圖片描述mysql默認的事務隔離級別爲repeatable-read
在這裏插入圖片描述

(2)理解三種由於併發訪問導致的數據讀取問題
  • 髒讀(dirty read):讀取未提交的數據。A事務讀取B事務尚未提交的數據,此時如果B事務發生錯誤並執行回滾操作,那麼A事務讀取到的數據就是髒數據
  • 非重複讀(nonrepeatable read):多次讀取,內容不一致。事務A多次讀取一條數據,比如第一次讀取數據爲10,事務B將數據改爲20,事務A第二次讀取數據就不一樣
  • 幻讀(phantom read):前後多次讀取,數據總量不一致。事務A在執行讀取操作,需要兩次統計數據的總量,前一次查詢數據總量後,此時事務B執行了新增數據的操作並提交後,這個時候事務A讀取的數據總量和之前統計的不一樣,就像產生了幻覺一樣,平白無故的多了幾條數據,成爲幻讀。
(3)解決併發訪問下的數據讀取問題的方法

這裏的方法就是四種事務隔離級別:未提交讀、提交讀、可重複讀、可串行化

1. 未提交讀
隔離級別是最不嚴格的隔離級別,現實中應用很少。在使用這個隔離級別時,僅當另一個事務試圖刪除或更改被檢索的行所在的表時,纔會鎖定一個事務檢索的行。因爲在使用這種隔離級別時,行通常保持未鎖定狀態,所以髒讀、不可重複讀和幻像讀都可能會發生。

2. 提交讀
它是Oracle默認的隔離級別,事務中的每一條語句都遵從語句級的讀一致性,即一個語句所處理的數據集是在單一時間點上的數據集,這個時間點是這個語句開始的時間,一個語句看不見在它開始執行後提交的修改

3. 可重複讀
它是Mysql的默認隔離級別,在該隔離級別下,一個事務的影響完全與其他併發事務隔離,髒讀、不可重複的讀、幻像讀現象都不會發生。當使用可重複讀隔離級別時,在事務執行期間會鎖定該事務以任何方式引用的所有行。因此,如果在同一個事務中發出同一個SELECT語句兩次或更多次,那麼產生的結果數據集總是相同的。因此,使用可重複讀隔離級別的事務可以多次檢索同一行集,並對它們執行任意操作,直到提交或回滾操作終止該事務。。

4. 可串行化
這是數據庫最高的隔離級別,這種級別下,事務“串行化順序執行”,也就是一個一個排隊執行。
這種級別下,“髒讀”、“不可重複讀”、“幻讀”都可以被避免,但是執行效率奇差,性能開銷也

四、存儲過程(推薦看下)

1、簡介

存儲過程類似於python中的自定義函數,內部封裝了操作數據庫的sql語句,
後續先要實現相應的操作,只需要調用存儲過程即可

使用存儲過程的優點:

#1. 用於替代程序寫的SQL語句,實現程序與sql解耦

#2. 基於網絡傳輸,傳別名的數據量小,而直接傳sql數據量大

使用存儲過程的缺點:

#1. 程序員擴展功能不方便

2、創建存儲過程

語法結果:
   delimiter $$
   create procedure p1()
   	begin
    	select * from user;
    end $$
   delimiter ;
調用存儲過程:call p1();
    
    
帶參數的:
    delimiter $$
    create procedure p1(
        in m int,  # in,只能將參數傳進來,不能被返回出去
        in n int,
        out res int,  # 可以被返回
        inout xxx int,  # 既可以進又可以出
    )
    begin
        select * from t1 where id > m and id < n
        res = 0
    end $$
    delimiter ;
# 注意有人會問爲什麼沒有類似return這種返回語句?因爲它是根據select來查看返回結果的。
call p1(2,4,10) # 報錯

set @res=10 # 創建變量
call p1(2,4,@res) # 可以返回值的需要傳變量名,不能直接傳實參

set @m=10
call p1(@m,4,@res) # 即使你這裏採用了形參傳值,也無法select獲得返回結果,因爲m是in不能返回

# 查看結果
select @res; # 存儲過程裏面,res值被修改這裏就可以看到修改後的值。

3、pymysql中調用存儲過程

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())

五、函數(推薦看下)

MySQL提供了許多內置函數任君採擷,我把比較常見的已經篩選出來了!

1. 聚合函數
聚合函數(常用於GROUP BY從句的SELECT查詢中)
    AVG(col)返回指定列的平均值
    COUNT(col)返回指定列中非NULL值的個數
    MIN(col)返回指定列的最小值
    MAX(col)返回指定列的最大值
    SUM(col)返回指定列的所有值之和
    GROUP_CONCAT(col) 返回由屬於一組的列值連接組合而成的結果 

2. 加密函數
MD5()    # 計算字符串str的MD5校驗和
PASSWORD(str)   # 返回字符串str的加密版本,這個加密過程是不可逆轉的。

3. 字符串函數
CHAR_LENGTH(str) # 返回值爲字符串str 的長度,長度的單位爲字符。一個多字節字符算作一個單字符。
CONCAT(str1,str2,...) # 字符串拼接,如有任何一個參數爲NULL ,則返回值爲 NULL。
LOWER(str) # 變小寫
UPPER(str) # 變大寫
REVERSE(str) # 返回字符串 str ,順序和字符順序相反。

4. 日期函數
NOW()    # 返回當前的日期和時間
DATE_FORMAT(date,format) # 根據format字符串格式化date值
示例:
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');


5. 數學函數
ROUND(x,y) # 返回參數x的四捨五入的有y位小數的值
RAND() # 返回0到1內的隨機值,可以通過提供一個參數(種子)使RAND()隨機數生成器生成一個指定的值。

mysql也可以自定義函數,但是我覺得我對我沒用,因此就不總結了!

六、流程控制(瞭解)

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 ;

2、循環語句

(1)while循環
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 ;
(2)repeat循環
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 ;
(3)loop循環
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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章