### 視圖(了解)
* 什么是視圖
```python
"""
視圖就是通過查詢得到一張虛擬表,然后保存下來,下次可以直接使用
其實視圖也是表
"""
```
* 為什么要用視圖
```python
"""
如果要頻繁的操作一張虛擬表(拼表組成的),你就可以制作成視圖 后續直接操作
"""
```
* 如何操作
```python
# 固定語法
create view 表名 as 虛擬表的查詢sql語句
# 具體操作
create view teacher2course as
select * from teacher INNER JOIN course
on teacher.tid = course.teacher_id
;
```
* 注意
```python
"""
1 創建視圖在硬盤上只會有表結構 沒有表數據(數據還是來自於之前的表)
2 視圖一般只用來查詢 里面的數據不要繼續修改 可能會影響真正的表
"""
```
* 視圖到底使用頻率高不高呢?
```python
"""
不高
當你創建了很多視圖之后 會造成表的不好維護
"""
# 總結
視圖了解即可 基本不用!!!
```
### 觸發器(了解)
在滿足對表數據進行增、刪、改的情況下,自動觸發的功能
使用觸發器可以幫助我們實現監控、日志...
觸發器可以在六種情況下自動觸發 增前 增后 刪前刪后 改前改后
#### 基本語法結構
```python
create trigger 觸發器的名字 before/after insert/update/delete on 表名
for each row
begin
sql語句
end
# 具體使用 針對觸發器的名字 我們通常需要做到見名知意
# 針對增
create trigger tri_before_insert_t1 before insert on t1
for each row
begin
sql語句
end
create trigger tri_after_insert_t1 after insert on t1
for each row
begin
sql語句
end
"""針對刪除和修改 書寫格式一致"""
ps:修改MySQL默認的語句結束符 只作用於當前窗口
delimiter $$ 將默認的結束符號由;改為$$
delimiter ;
# 案例
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
);
"""
當cmd表中的記錄succes字段是no那么就觸發觸發器的執行去errlog表中插入數據
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 ;
# 朝cmd表插入數據
INSERT INTO cmd (
USER,
priv,
cmd,
sub_time,
success
)
VALUES
('jason','0755','ls -l /etc',NOW(),'yes'),
('jason','0755','cat /etc/passwd',NOW(),'no'),
('jason','0755','useradd xxx',NOW(),'no'),
('jason','0755','ps aux',NOW(),'yes');
# 刪除觸發器
drop trigger tri_after_insert_cmd;
```
### 事務
* 什么是事務
```python
"""
開啟一個事務可以包含多條sql語句 這些sql語句要么同時成功
要么一個都別想成功 稱之為事務的原子性
"""
```
* 事務的作用
```python
"""
保證了對數據操作的安全性
"""
eg:還錢的例子
egon用銀行卡給我的支付寶轉賬1000
1 將egon銀行卡賬戶的數據減1000塊
2 將jason支付寶賬戶的數據加1000塊
你在操作多條數據的時候可能會出現某幾條操作不成功的情況
```
* 事務的四大特性
```python
"""
ACID
A:原子性
一個事務是一個不可分割的單位,事務中包含的諸多操作
要么同時成功要么同時失敗
C:一致性
事務必須是使數據庫從一個一致性的狀態變到另外一個一致性的狀態
一致性跟原子性是密切相關的
I:隔離性
一個事務的執行不能被其他事務干擾
(即一個事務內部的操作及使用到的數據對並發的其他事務是隔離的,並發執行的事務之間也是互相不干擾的)
D:持久性
也叫"永久性"
一個事務一旦提交成功執行成功 那么它對數據庫中數據的修改應該是永久的
接下來的其他操作或者故障不應該對其有任何的影響
"""
```
* 如何使用事務
```python
# 事務相關的關鍵字
# 1 開啟事務
start transaction;
# 2 回滾(回到事務執行之前的狀態)
rollback;
# 3 確認(確認之后就無法回滾了)
commit;
"""模擬轉賬功能"""
create table user(
id int primary key auto_increment,
name char(16),
balance int
);
insert into user(name,balance) values
('jason',1000),
('egon',1000),
('tank',1000);
# 1 先開啟事務
start transaction;
# 2 多條sql語句
update user set balance=900 where name='jason';
update user set balance=1010 where name='egon';
update user set balance=1090 where name='tank';
"""
總結
當你想讓多條sql語句保持一致性 要么同時成功要么同時失敗
你就應該考慮使用事務
"""
```
### 存儲過程(了解)
存儲過程就類似於python中的自定義函數
它的內部包含了一系列可以執行的sql語句,存儲過程存放於MySQL服務端中,你可以直接通過調用存儲過程觸發內部sql語句的執行
**基本使用**
```python
create procedure 存儲過程的名字(形參1,形參2,...)
begin
sql代碼
end
# 調用
call 存儲過程的名字();
```
#### 三種開發模型
第一種
```python
"""
應用程序:程序員寫代碼開發
MySQL:提前編寫好存儲過程,供應用程序調用
好處:開發效率提升了 執行效率也上去了
缺點:考慮到認為元素、跨部門溝通的問題 后續的存儲過程的擴展性差
"""
```
第二種
```python
"""
應用程序:程序員寫代碼開發之外 設計到數據庫操作也自己動手寫
優點:擴展性很高
缺點:
開發效率降低
編寫sql語句太過繁瑣 而且后續還需要考慮sql優化的問題
"""
```
第三種
```python
"""
應用程序:只寫程序代碼 不寫sql語句 基於別人寫好的操作MySQL的python框架直接調用操作即可 ORM框架
優點:開發效率比上面兩種情況都要高
缺點:語句的擴展性差 可能會出現效率低下的問題
"""
```
第一種基本不用。一般都是第三種,出現效率問題再動手寫sql
* 存儲過程具體演示
```python
delimiter $$
create procedure p1(
in m int, # 只進不出 m不能返回出去
in n int,
out res int # 該形參可以返回出去
)
begin
select tname from teacher where tid>m and tid<n;
set res=666; # 將res變量修改 用來標識當前的存儲過程代碼確實執行了
end $$
delimiter ;
# 針對形參res 不能直接傳數據 應該傳一個變量名
# 定義變量
set @ret = 10;
# 查看變量對應的值
select @ret;
```
在pymysql模塊中如何調用存儲過程呢?
```python
import pymysql
conn = pymysql.connect(
host = '127.0.0.1',
port = 3306,
user = 'root',
passwd = '123456',
db = 'day48',
charset = 'utf8',
autocommit = True
)
cursor = conn.cursor(pymysql.cursors.DictCursor)
# 調用存儲過程
cursor.callproc('p1',(1,5,10))
"""
@_p1_0=1
@_p1_1=5
@_p1_2=10
"""
# print(cursor.fetchall())
cursor.execute('select @_p1_2;')
print(cursor.fetchall())
```
### 函數
跟存儲過程是有區別的,存儲過程是自定義函數,函數就類似於是內置函數
```python
('jason','0755','ls -l /etc',NOW(),'yes')
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');
```
### 流程控制(了解)
```python
# if判斷
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 ;
# 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 ;
```
### 索引
ps:數據都是存在與硬盤上的,查詢數據不可避免的需要進行IO操作
索引:就是一種數據結構,類似於書的目錄。意味着以后在查詢數據的應該先找目錄再找數據,而不是一頁一頁的翻書,從而提升查詢速度降低IO操作
索引在MySQL中也叫“鍵”,是存儲引擎用於快速查找記錄的一種數據結構
* primary key
* unique key
* index key
注意foreign key不是用來加速查詢用的,不在我們的而研究范圍之內
上面的三種key,前面兩種除了可以增加查詢速度之外各自還具有約束條件,而最后一種index key沒有任何的約束條件,只是用來幫助你快速查詢數據
**本質**
通過不斷的縮小想要的數據范圍篩選出最終的結果,同時將隨機事件(一頁一頁的翻)
變成順序事件(先找目錄、找數據)
也就是說有了索引機制,我們可以總是用一種固定的方式查找數據
一張表中可以有多個索引(多個目錄)
索引雖然能夠幫助你加快查詢速度但是也有缺點
```python
"""
1 當表中有大量數據存在的前提下 創建索引速度會很慢
2 在索引創建完畢之后 對表的查詢性能會大幅度的提升 但是寫的性能也會大幅度的降低
"""
索引不要隨意的創建!!!
```
### b+樹
```python
"""
只有葉子節點存放的是真實的數據 其他節點存放的是虛擬數據 僅僅是用來指路的
樹的層級越高查詢數據所需要經歷的步驟就越多(樹有幾層查詢數據就需要幾步)
一個磁盤塊存儲是有限制的
為什么建議你將id字段作為索引
占得空間少 一個磁盤塊能夠存儲的數據多
那么久降低了樹的高度 從而減少查詢次數
"""
```
### 聚集索引(primary key)
```python
"""
聚集索引指的就是主鍵
Innodb 只有兩個文件 直接將主鍵存放在了idb表中
MyIsam 三個文件 單獨將索引存在一個文件
"""
```
### 輔助索引(unique,index)
查詢數據的時候不可能一直使用到主鍵,也有可能會用到name,password等其他字段
那么這個時候你是沒有辦法利用聚集索引。這個時候你就可以根據情況給其他字段設置輔助索引(也是一個b+樹)
```python
"""
葉子節點存放的是數據對應的主鍵值
先按照輔助索引拿到數據的主鍵值
之后還是需要去主鍵的聚集索引里面查詢數據
"""
```
### 覆蓋索引
在輔助索引的葉子節點就已經拿到了需要的數據
```python
# 給name設置輔助索引
select name from user where name='jason';
# 非覆蓋索引
select age from user where name='jason';
```
### 測試索引是否有效的代碼
感興趣就自己試一試 不感興趣直接忽略
```python
**准備**
```mysql
#1. 准備表
create table s1(
id int,
name varchar(20),
gender char(6),
email varchar(50)
);
#2. 創建存儲過程,實現批量插入記錄
delimiter $$ #聲明存儲過程的結束符號為$$
create procedure auto_insert1()
BEGIN
declare i int default 1;
while(i<3000000)do
insert into s1 values(i,'jason','male',concat('jason',i,'@oldboy'));
set i=i+1;
end while;
END$$ #$$結束
delimiter ; #重新聲明分號為結束符號
#3. 查看存儲過程
show create procedure auto_insert1\G
#4. 調用存儲過程
call auto_insert1();
```
``` mysql
# 表沒有任何索引的情況下
select * from s1 where id=30000;
# 避免打印帶來的時間損耗
select count(id) from s1 where id = 30000;
select count(id) from s1 where id = 1;
# 給id做一個主鍵
alter table s1 add primary key(id); # 速度很慢
select count(id) from s1 where id = 1; # 速度相較於未建索引之前兩者差着數量級
select count(id) from s1 where name = 'jason' # 速度仍然很慢
"""
范圍問題
"""
# 並不是加了索引,以后查詢的時候按照這個字段速度就一定快
select count(id) from s1 where id > 1; # 速度相較於id = 1慢了很多
select count(id) from s1 where id >1 and id < 3;
select count(id) from s1 where id > 1 and id < 10000;
select count(id) from s1 where id != 3;
alter table s1 drop primary key; # 刪除主鍵 單獨再來研究name字段
select count(id) from s1 where name = 'jason'; # 又慢了
create index idx_name on s1(name); # 給s1表的name字段創建索引
select count(id) from s1 where name = 'jason' # 仍然很慢!!!
"""
再來看b+樹的原理,數據需要區分度比較高,而我們這張表全是jason,根本無法區分
那這個樹其實就建成了“一根棍子”
"""
select count(id) from s1 where name = 'xxx';
# 這個會很快,我就是一根棍,第一個不匹配直接不需要再往下走了
select count(id) from s1 where name like 'xxx';
select count(id) from s1 where name like 'xxx%';
select count(id) from s1 where name like '%xxx'; # 慢 最左匹配特性
# 區分度低的字段不能建索引
drop index idx_name on s1;
# 給id字段建普通的索引
create index idx_id on s1(id);
select count(id) from s1 where id = 3; # 快了
select count(id) from s1 where id*12 = 3; # 慢了 索引的字段一定不要參與計算
drop index idx_id on s1;
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx';
# 針對上面這種連續多個and的操作,mysql會從左到右先找區分度比較高的索引字段,先將整體范圍降下來再去比較其他條件
create index idx_name on s1(name);
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx'; # 並沒有加速
drop index idx_name on s1;
# 給name,gender這種區分度不高的字段加上索引並不難加快查詢速度
create index idx_id on s1(id);
select count(id) from s1 where name='jason' and gender = 'male' and id = 3 and email = 'xxx'; # 快了 先通過id已經講數據快速鎖定成了一條了
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 慢了 基於id查出來的數據仍然很多,然后還要去比較其他字段
drop index idx_id on s1
create index idx_email on s1(email);
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 快 通過email字段一劍封喉
```
#### 聯合索引
```mysql
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx';
# 如果上述四個字段區分度都很高,那給誰建都能加速查詢
# 給email加然而不用email字段
select count(id) from s1 where name='jason' and gender = 'male' and id > 3;
# 給name加然而不用name字段
select count(id) from s1 where gender = 'male' and id > 3;
# 給gender加然而不用gender字段
select count(id) from s1 where id > 3;
# 帶來的問題是所有的字段都建了索引然而都沒有用到,還需要花費四次建立的時間
create index idx_all on s1(email,name,gender,id); # 最左匹配原則,區分度高的往左放
select count(id) from s1 where name='jason' and gender = 'male' and id > 3 and email = 'xxx'; # 速度變快
```
慢查詢日志
設定一個時間檢測所有超出該時間的sql語句,然后針對性的進行優化!
```
