1. 程式人生 > 其它 >09、檢視、觸發器、儲存過程、函式、流程控制

09、檢視、觸發器、儲存過程、函式、流程控制

檢視、觸發器、儲存過程、函式、流程控制

一、檢視(瞭解)

將SQL語句的查詢結果儲存下來,形成的表就叫檢視
檢視是一個虛擬表(非真實存在),其本質是【根據SQL語句獲取動態的資料集,併為其命名】,使用者使用時只需使用【名稱】即可獲取結果集,可以將該結果集當做表來使用。

使用檢視我們可以把查詢過程中的臨時表摘出來,用檢視去實現,這樣以後再想操作該臨時表的資料時就無需重寫複雜的sql了,直接去檢視中查詢即可,但檢視有明顯地效率問題,並且檢視是存放在資料庫中的,如果我們程式中使用的sql過分依賴資料庫中的檢視,即強耦合,那就意味著擴充套件sql極為不便,因此並不推薦使用
# 建立檢視
語法:create view 檢視名稱 as sql語句

use db04_1
案例:create view teacher2course as
select * from teacher inner join course on teacher.tid = course.teacher_id;

# 使用檢視
語法:selelct 欄位名 from 檢視名稱
案例:select * from teacher2course

# 修改檢視
語法:alter view 檢視名稱 as sql語句
案例:alter view teacher2course as select * from course where cid>3

# 刪除檢視
語法:drop view 檢視名稱
案例:drop view teacher2course

修改檢視後,原表的記錄也會跟著修改,因此我們不應該修改檢視中的記錄,而且在涉及多個表的情況下是根本無法修改檢視中的記錄的

二、觸發器trigger

對錶進行增、刪、改操作前後可以自動觸發的功能
觸發器:滿足特點條件之後自動執行
在MySQL只有三種情況下可以觸發
1.針對表的增
  增前 增後
   2.針對表的改
  改前 改後
   3.針對表的刪
  刪前 刪後

1、語法

create trigger 觸發器的名字 before/after insert/update/delete on 表名 for each row
begin
sql語句
end

2、準備表

USE db06
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
);

3、觸發器的建立與觸發

#建立觸發器
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
  ('tony','0755','ls -l /etc',NOW(),'yes'),
  ('tony','0755','cat /etc/passwd',NOW(),'no'),
  ('tony','0755','useradd xxx',NOW(),'no'),
  ('tony','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 |
+----+-----------------+---------------------+
2 rows in set (0.00 sec)
# 檢視觸發器
show triggers;
# 刪除觸發器
drop trigger tri_after_insert_cmd;
# 觸發器名字在命名的時候推薦使用下列的方式
tri_after_insert_t1、tri_before_delete_t1
# 如何臨時修改SQL語句的結束符
delimiter //
# 在MySQL中NEW特指資料物件可以通過點的方式獲取欄位對應的資料
id   name pwd hobby
1     jason  123  read
NEW.name >>> jason

三、事務

# 四大特性(ACID)
	A:原子性(atomicity /ˌætəˈmɪsəti/)
        每個事務都是不可分割的最小單位(同一個事務內的多個操作要麼同時成功要麼同時失敗)
    C:一致性(consistency  /kənˈsɪstənsi/)
        事務必須是使資料庫從一個一致性狀態變到另一個一致性狀態。一致性與原子性是密切相關的
    I:隔離性(isolation /ˌaɪsəˈleɪʃn/)
        事務與事務之間彼此不干擾
    D:永續性(durability /ˌdʊrəˈbɪləti/)
        一個事務一旦提交,它對資料庫中資料的改變就應該是永久性的
     
# 如何開啟事務
start transaction;
# 如何回滾
rollback;
# 如何確認
commit;


#例項展示
create table user(
	id int primary key auto_increment,
	name char(32),
	balance int
);
insert into user(name,balance) values
('jason',1000),
('tony',1000),
('oscar',1000);

# 修改資料之前先開啟事操作
start transaction;
# 修改操作
update user set balance=900 where name='jason'; # 買家支付100元
update user set balance=1010 where name='tony'; # 中介拿走10元
update user set balance=1090 where name='oscar'; # 賣家拿走90元
select * from user;
    +----+-------+---------+
    | id | name  | balance |
    +----+-------+---------+
    |  1 | jason |     900 |
    |  2 | tony  |    1010 |
    |  3 | oscar |    1090 |
    +----+-------+---------+
# 如何回滾
rollback;
select * from user;
    +----+-------+---------+
    | id | name  | balance |
    +----+-------+---------+
    |  1 | jason |    1000 |
    |  2 | tony  |    1000 |
    |  3 | oscar |    1000 |
    +----+-------+---------+
# 如何確認
commit;
rollback;
select * from user;
    +----+-------+---------+
    | id | name  | balance |
    +----+-------+---------+
    |  1 | jason |     900 |
    |  2 | tony  |    1010 |
    |  3 | oscar |    1090 |
    +----+-------+---------+

四、儲存過程(瞭解)

1、介紹

儲存過程包含了一系列可執行的sql語句,儲存過程存放於MySQL中,通過呼叫它的名字可以執行其內部的一堆sql
# 類似於python中的自定義函式

#優點
1. 用於替代程式寫的SQL語句,實現程式與sql解耦
2. 基於網路傳輸,傳別名的資料量小,而直接傳sql資料量大

# 缺點
1. 程式設計師擴充套件功能不方便

2、無參儲存過程

delimiter //
create procedure p1()
begin
	select * from user;
end //
delimiter ;

#在mysql中呼叫
call p1()

#在python中基於pymysql呼叫
cursor.callproc('p1') 
print(cursor.fetchall())

3、有參儲存過程

delimiter //
create procedure p2(
    in m int,  # in表示這個引數必須只能是傳入不能被返回出去
    in n int,  
    out res int  # out表示這個引數可以被返回出去,還有一個inout表示即可以傳入也可以被返回出去
)
begin
    select * from user where id > m and id < n;
    set res=0;  # 用來標誌儲存過程是否執行
end //
delimiter ;

#在mysql中呼叫
set @res=0; #0代表假(執行失敗),1代表真(執行成功)
call p2(1,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())

4、程式碼操作儲存過程

# 程式碼操作儲存過程
import pymysql
conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    passwd='123',
    db='db6',
    charset='utf8',
    autocommit=True
)
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
cursor.callproc('p2',(1,3,10))
# @_p1_0=1,@_p1_1=3,@_p1_2=10;
print(cursor.fetchall())

五、函式

1、介紹

# 相當於python中內建函式
"ps:可以通過help 函式名    檢視幫助資訊!"
# 1.移除指定字元
Trim、LTrim、RTrim

select trim('   bar   ')
	'bar'
select trim(leading 'x' from 'xxxbarxxx')
	'barxxx'
select trim(both 'x' from 'xxxbarxxx')
	'bar'
select trim(trailing 'xyz' from 'barxxyz')
	'barx'

# 2.大小寫轉換
Lower、Upper

# 3.獲取左右起始指定個數字元
Left、Right

# 4.返回讀音相似值(對英文效果)
Soundex
"""
eg:客戶表中有一個顧客登記的使用者名稱為J.Lee
		但如果這是輸入錯誤真名其實叫J.Lie,可以使用soundex匹配發音類似的
		where Soundex(name)=Soundex('J.Lie')
"""
# 5.日期格式:date_format
'''在MySQL中表示時間格式儘量採用2022-11-11形式'''

2、案例

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');

1.where Date(sub_time) = '2015-03-01'
2.where Year(sub_time)=2016 AND Month(sub_time)=07;
# 更多日期處理相關函式 
	adddate	增加一個日期 
	addtime	增加一個時間
	datediff計算兩個日期差值
  ...

3、刪除函式

drop function func_name;

六、流程控制

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、迴圈語句

# 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 ;

# 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 ;

#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

七、索引與慢查詢優化

1、介紹

索引:簡單的理解為可以幫助你加快資料查詢速度的工具
    也可以把索引比喻成書的目錄
   
演算法:解決事物的辦法
    入門級演算法:二分法
   	二分法前提:資料集必須有序

資料結構
	二叉樹(只能分兩個叉)
	b樹 b+樹(葉子節點添加了指標) b*樹(枝節點也添加了指標)
    # 新增指標是為了加快範圍查詢的速度
 
將某個欄位新增成索引就相當於依據該欄位建立了一顆b+樹從而加快查詢速度
如果某個欄位沒有新增索引 那麼依據該欄位查詢資料會非常的慢(一行行查詢)

2、建立索引

# 1、為user表的id欄位建立索引,會以每條記錄的id欄位值為基礎生成索引結構
create index 索引名 on user(id);
# 使用索引
select * from user where id = xxx;

# 2、為user表的name欄位建立索引,會以每條記錄的name欄位值為基礎生成索引結構
create index 索引名 on user(name);
# 使用索引
select * from user where name = xxx;

3、索引的分類

1.primary key
	主鍵索引除了有加速查詢的效果之外 還具有一定的約束條件
2.unique key
	唯一鍵索引 除了有加速查詢的效果之外 還具有一定的約束條件
3.index key
	普通索引 只有加速查詢的效果 沒有額外約束
4.foreign key 
	# 注意外來鍵不是索引 它僅僅是用來建立表與表之間關係的

4、如何操作索引

# 準備
use db05
create table t1(
	id int,
	name varchar(32),
	pwd int
);

insert into t1 values
(1,'momo',123),
(2,'lily',456),
(3,'tony',789),
(4,'jason',666);

建立唯一索引需要提前排查是否有重複資料
	select count(欄位) from t1;
  	select count(distinct(欄位)) from t1;
檢視當前表內部索引值
	show index from t1;
主鍵索引
	alter table t1 add primary key pri_id(id);
唯一索引
	alter table t1 add unique key uni_age(age)
普通索引
	alter table t1 add index idx_name(name)
字首索引(屬於普通索引)
	避免對大列建索引,如果有就使用字首索引
    	eg:部落格內容 百度搜索內容等
	alter table t1 add index idx_name(name(4))
聯合索引(屬於普通索引)
	    相親平臺 搜尋心儀物件的時候
    	# 最左匹配原則
    	gender money height beautiful 
	alter table t1 add index idx_all(name,age,sex)
刪除索引
	alter table t1 drop index 索引名(idx_name、idx_all...)

5、explain句式

全表掃描
	不走索引 一行行查詢資料 效率極低 生產環境下儘量不要書寫類似SQL
索引掃描
	走索引 加快資料查詢 建議書寫該型別SQL

explain就是幫助我們檢視SQL語句屬於那種掃描

# 常見的索引掃描型別:
    1)index
    2)range
    3)ref
    4)eq_ref
    5)const
    6)system
    7)null
從上到下,效能從最差到最好,我們認為至少要達到range級別


# 不走索引情況(起碼記憶四條及以上)
	1.沒有查詢條件,或者查詢條件沒有建立索引
    2.查詢結果集是原表中的大部分資料(25%以上)
    3.索引本身失效,統計資料不真實
    4.查詢條件使用函式在索引列上或者對索引列進行運算,運算包括(+,-,*等)
    5.隱式轉換導致索引失效
    	eg:欄位是字元型別 查詢使用整型
    6.<> ,not in 不走索引
    	單獨的>,<,in 有可能走,也有可能不走,和結果集有關,儘量結合業務新增limit、or或in儘量改成union
    7.like "%_" 百分號在最前面不走
    8.單獨引用聯合索引裡非第一位置的索引列
 
"""
索引的建立會加快資料的查詢速度 但是一定程度會拖慢資料的插入和刪除速度
"""

6、隔離級別

在SQL標準中定義了四種隔離級別,每一種級別都規定了一個事務中所做的修改
InnoDB支援所有隔離級別
	set transaction isolation level 級別

1.read uncommitted(未提交讀)
	事務中的修改即使沒有提交,對其他事務也都是可見的,事務可以讀取未提交的資料,這一現象也稱之為"髒讀"
2.read committed(提交讀)
	大多數資料庫系統預設的隔離級別
  一個事務從開始直到提交之前所作的任何修改對其他事務都是不可見的,這種級別也叫做"不可重複讀"
3.repeatable read(可重複讀)		# MySQL預設隔離級別
	能夠解決"髒讀"問題,但是無法解決"幻讀"
  所謂幻讀指的是當某個事務在讀取某個範圍內的記錄時另外一個事務又在該範圍內插入了新的記錄,當之前的事務再次讀取該範圍的記錄會產生幻行,InnoDB和XtraDB通過多版本併發控制(MVCC)及間隙鎖策略解決該問題
4.serializable(可序列讀)
	強制事務序列執行,很少使用該級別
 
https://www.cnblogs.com/Dominic-Ji/p/15560680.html

 

作業

1.自行百度搜索並提煉"資料庫設計三大正規化",分別是哪三大正規化以及各自有何特徵
"""
正規化:關係型資料庫中規範稱為正規化

第一正規化(1NF):
1、資料表中的每一列(欄位),必須是不可拆分的最小單元,也就是確保每一列的原子性。滿足第一正規化是關係模式規範化的最低要求,否則,將有很多基本操作在這樣的關係模式中實現不了。
2、兩列的屬性相近或相似或一樣,儘量合併屬性一樣的列,確保不產生冗餘資料。

第二正規化(2NF):
滿足1NF後要求表中的所有列,每一行的資料只能與其中一列相關,即一行資料只做一件事。只要資料列中出現數據重複,就要把表拆分開來。

第三正規化(3NF):
滿足2NF後,要求:表中的每一列都要與主鍵直接相關,而不是間接相關(表中的每一列只能依賴於主鍵)。

注意事項:
1.第二正規化與第三正規化的本質區別:在於有沒有分出兩張表。
第二正規化是說一張表中包含了多種不同實體的屬性,那麼必須要分成多張表,第三正規化是要求已經分好了多張表的話,一張表中只能有另一張標的ID,而不能有其他任何資訊,(其他任何資訊,一律用主鍵在另一張表中查詢)。

2.必須先滿足第一正規化才能滿足第二正規化,必須同時滿足第一第二正規化才能滿足第三正規化。
三大正規化只是一般設計資料庫的基本理念,可以建立冗餘較小、結構合理的資料庫。如果有特殊情況,當然要特殊對待,資料庫設計最重要的是看需求跟效能,需求>效能>表結構。所以不能一味的去追求正規化建立資料庫。
"""
    
2.整理今日內容及部落格
3.自行復習python基礎