1. 程式人生 > 其它 >檢視、觸發器、事務、函式、索引

檢視、觸發器、事務、函式、索引

目錄
MySQL登入註冊
import pymysql
conn = pymysql.connect(
    host='127.0.0.1',
    port=3306,
    user='root',
    password='123',
    db='db5',
    charset='utf8mb4',
    autocommit=True
)
cursor = conn.cursor(cursor=pymysql.cursors.DictCursor)
# 獲取使用者名稱和密碼
username = input('username>>>:').strip()
password = input('password>>>:').strip()

# 2.構造查詢SQL語句
# sql = 'select * from user where name = "%s" and password ="%s"' %(username,password)# 會出現sql注入問題
sql = 'select * from user where name =%s and password = %s'   # 這樣既可以避免
# sql = 'insert into user(name,password) values(%s,%s)'
print(sql)
# 3.執行SQL語句
# affect_rows = cursor.execute(sql)
affect_rows = cursor.execute(sql,(username,password))
# 一次性插入資料
# cursor.executemany(sql,[('tony','456'),('maria','333'),('jenny','567')])
# 4.獲取執行結果
res = cursor.fetchall()
if res:
    print('登入成功')
    print(res)
else:
    print('使用者名稱或者密碼錯誤')

MySQL登入

SQL注入問題

怪像1:輸對使用者名稱就可以登入成功
怪像2:不需要對的使用者名稱和密碼也可以登入成功

SQL注入:利用特殊符合的組合產生特殊的含義 從而避開正常的業務邏輯

select * from userinfo where name='kiki' -- kasdjksajd' and pwd=''
select * from userinfo where name='222' or 1=1 -- aksdjasldj' and pwd='' 
上述也能登入成功

針對上述的SQL注入問題 如何解決
核心在於手動拼接了關鍵資料 交給execute處理即可
sql = " select * from userinfo where name=%s and pwd=%s "
cursor.execute(sql, (username, password))

補充說明
 executemany(sql,[(),(),()...])

檢視

1.什麼檢視

​ 檢視就是通過查詢得到一張虛擬表,然後儲存下來,下次直接使用即可。

2.為什麼要用檢視

​ 如果要頻繁使用一張虛擬表,可以不用重複查詢。

3.如何用檢視

正常兩張表連線情況(拼接)查詢 use db4

create view work2department as
select * from work inner join department on work.dep_id = department.id

4.引入檢視的作用

當我們頻繁的去查詢連表,反覆拼接,會出現繁瑣的現象,影響效率

5.解決方法

我們可以使用檢視來進行操作,將拼接的連表儲存起來

create view work2department as select * from work inner join department on work.dep_id = department.id

6.檢視的應用

6.1 建立檢視 use db4

create view 建立檢視名字 as 執行連表的SQL語句
create view work2department as select * from work inner join department on work.dep_id = department.id;

注意:當欄位名相同時,會產生衝突,導致無法建立檢視
 alter table department change id cid int;  # 修改欄位名

6.2 查詢檢視層

select * from 檢視名字
	eg: select * from work2department;

6.3 查詢Navicat檢視層位置

6.4 刪除是圖表

刪除視圖表格式:
	drop view 檢視名;

6.5 使用檢視注意事項(強調)

強調:

1.在硬碟中,檢視只有表結構檔案,沒有表資料檔案(資料來源於 )

2.檢視通常用於查詢,儘量不要修改檢視中的資料(修改檢視回影響到原表)

3.開發過程中能不會去使用檢視的,因為檢視時MySQL的功能,如果你的專案裡大量使用檢視,那麼意味著你後期想要擴充套件某個功能的時候,這個功能恰好又需要檢視進行修改,意味著你需要在MySQL這邊將檢視修改一下,然後再去應用層修改對應的SQL語句,這就涉及到跨部門溝通的問題,所有通常不會使用檢視,而是通過重新修改sql語句來擴充套件功能。

觸發器

1.什麼是觸發器

​ 在滿足對某張表資料的增、刪、改的情況下,自動觸發的功能稱之為觸發器。

2.為什麼用要觸發器

​ 觸發器專門針對我們對某一張表的增insert,刪delete,改update的行為,這類行為一旦執行 就會觸發觸發器的執行,既自動執行另外一段sql語句。

3.觸發器trigger

滿足條件就會自動執行

MySQL關於增、刪、改的情況有六種情況

3.1 針對表的增加
	增前、增後
3.2 針對表的改
	改前、改後
3.3 針對表的刪
	刪前、刪後

4.觸發器語法結構

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

5.觸發器命名有一定的規律,見名知意

tri_before_insert_t1
tri_after_delete_t2
tri_after_update_t3

6.觸發三種狀態(六種演示)

1.表的增加:
create trigger tri_after_insert_t1 after insert on 表名 for each row
begin
	 sql語句
end	 # 增加之後觸發

create trigger tri_before_insert_t1 before insert on 表名 for each row
begin
	 sql語句
end  # 增加之前觸發


2.表的修改:
create trigger tri_after_update_t1 after update on 表名 for each row
begin
	 sql語句
end  # 修改之後觸發

create trigger tri_before_insert_t1 before insert on 表名 for each row
begin
	sql語句
end # 修改之前觸發


3.表的刪除:
create trigger tri_after_delete_t1 after delete on 表名 for each row
begin
	sql語句
end # 刪除之前觸發

create trigger tri_before_delete_t1 defore delete on 表名 for each row
begin
	sql語句
end # 刪除之後觸發

7.臨時修改(只在當前視窗有用)

在書寫sql程式碼的時候結束符是;而整個觸發器的結束符也是;,這就會出現語法衝突,這個時候需要我們臨時修改結束符號

delimiter $$  #  只在當時視窗有效

記得要修改回來 delimiter;

8.觸發器實戰案例

8.1 MySQL 中NEW作用

在MySQL中NEW特指資料物件可以通過點的方式獲取欄位對應的資料
	id    name	pwd  hobby
	1     kiki  123  sing
	NEW.name  >>>  kiki

8.2 建立cmd命令表與報錯日誌

# 模擬cmd命令表
CREATE TABLE cmd (
    id INT PRIMARY KEY auto_increment,
    USER CHAR (32),
    priv CHAR (10),
    cmd CHAR (64),     # cmd命令欄位
    sub_time datetime, # 提交時間
    success enum ('yes', 'no') #0代表執行失敗
);

# 報錯日誌表
CREATE TABLE errlog (
    id INT PRIMARY KEY auto_increment,
    err_cmd CHAR (64),
    err_time datetime
);

8.3 建立觸發器

1.建立之前修改結束符
delimiter $$  # 將mysql預設的結束符由;換成$$

# 建立觸發器
create trigger tri_after_insert_cmd after insert on cmd for each row
begin
	# 觸發器啟動 判斷cmd中success欄位內容記錄是否為no,為no則執行以下程式碼
    if NEW.success = 'no' then  # 新記錄都會被MySQL封裝成NEW物件
        insert into errlog(err_cmd,err_time) values(NEW.cmd,NEW.sub_time);
    end if;
end $$

2.建立之後修改結束符
delimiter ;  # 結束之後記得再改回來,不然後面結束符就都是$$了。

8.4 cmd表內插入記錄(資料)

#往表cmd中插入記錄,觸發觸發器,根據IF的條件決定是否插入錯誤日誌
INSERT INTO cmd (
    USER,
    priv,
    cmd,
    sub_time,
    success
)
VALUES
    ('kevin','0755','ls -l /etc',NOW(),'yes'),
    ('kevin','0755','cat /etc/passwd',NOW(),'no'),
    ('kevin','0755','useradd xxx',NOW(),'no'),
    ('kevin','0755','ps aux',NOW(),'yes');

8.5 驗證觸發器(查詢結果)

查詢errlog表記錄
select * from errlog;

解析:
1.當cmd命令列後臺被插入資料時,if判斷出現為‘on’ 報錯的資料時
2.會自動觸發 觸發器會執行程式碼,將為‘on’錯誤程式碼 插入到《errlog報錯日誌》
如下圖

8.6 檢視觸發器和刪除觸發器

# 檢視所有的觸發器
show triggers;
# 刪除觸發器
drop trigger tri_after_insert_cmd;

事務

事務知識

1.事務的四大特徵(ACID)

A:原子性

​ 每個事物都是不可分割的最小單位(同一個事務內的多個操作要麼同時成功要麼同時失敗)

C:一致性

​ 事務必須是資料庫從一個一致性狀態變到另一個一致性狀態,一致性與原子性是密切相關的

I:隔離性

​ 事務與事務之間彼此不干擾

D:永續性

​ 一個事務一旦提交,它會資料庫中的資料的改變應該就是永久性,如,A支付寶轉錢給另一個人B,錢的擁有者就是另一個人B

2.事物存在的必要性

比如:kiki要給kimi轉賬1000元。銀行向資料庫傳送請求,將kiki的賬戶減少了1000元,在傳送的請求過程出現了資訊錯亂(斷電/斷網/不可抗力),導致請求失敗。

總結:kiki的賬戶減少了1000元,但是kimi的賬戶並沒有新增1000元,導致無法正常轉賬。

解決方法: 事物的必要性,要麼同時成功要麼同時失敗

3.事務關鍵字transaction、rollbackcommit****

開啟事物:start transaction;
回滾:    rollback;
確認      commit;

如果在使用修改表述資料num=100>>>:num=88,如果沒有再次確認的話,可以通過回滾rollback,拿到修改資料之前的資料num =100,
底層原理就是:我們修改的資料只是在記憶體修改的,回滾之後能拿到之前資料num=100,但是如果確認資料庫裡的num=88,那麼我們拿到的就是資料庫裡num=88

4.事物案例

4.1 建立事務

create table user(
	id int primary key auto_increment,
	name char(32),
	balance int
);

4.2 插入資料

insert into user(name,balance)
values
('kiki',1000),
('kevin',1000),
('kimi',1000);

4.3 開啟事物 transaction

# 修改資料之前先開啟事務操作
start transaction;

4.4 修改操作 update

# 修改操作
update user set balance=900 where name='kiki'; #買支付100元
update user set balance=1010 where name='kevin'; #中介拿走10元
update user set balance=1090 where name='kimi'; #賣家拿到90元

4.5 回滾 rollback

# 回滾到上一個狀態
rollback;

4.5 再次確認commit

# 開啟事務之後,只要沒有執行commit操作,資料其實都沒有真正重新整理到硬碟
commit;

4.6 總結事務

​ 開啟事務之後,只要沒有執行commit操作,資料其實都沒有真正重新整理到硬碟。開啟事務檢測操作是否完整,不完整主動滾回上一個狀態,如果完整就應該執行commit操作。

4.7 使用python,來完善的虛擬碼的邏輯

try:  # 異常捕獲(判斷一旦以下程式碼出現一行報錯則執行)
	update user set balance=900 where name='jason';  # 買家支付100元
	update user set balance=1010 where name='egon';  # 中介拿走10元
	update user set balance=1090 where name='tank';  # 賣家拿到90元
except 異常:  # 如果有異常就滾回去
	rolback;
else:  # 無異常則儲存解釋事務
	commit;

SQL事務四種隔離級別

1.事務隔離級別介紹

​ 在SQL標準中定義了四種隔離級別,每一種級別都規定了一個事務中所做的修改

InnoDB支援所有隔離級別
	set transaction isolation level 級別

2.四種隔離級別

2.1 read uncommitted(未提交讀)

​ 事務中的修改即使沒有提交,對其他事務也都是可見的,事務可以讀取未提交的資料,這一現象也稱之為"髒讀"

​ 簡單理解:事務中資料修改即使沒有提交,在記憶體中,沒有提交到硬碟,別人可能也是引用的我記憶體中的資料,讀的是修改的但是沒有提交的,該現象稱之為“髒讀”

2.2 read committed(提交讀)

 大多數資料庫系**統預設的隔離級別

​ 一個事務從開始直到提交之前所作的任何修改對其他事務都是不可見的,這種級別也叫做"不可重複讀"

​ 簡單理解:該事務修改了,只要沒有提交,別人是不可讀的,其他人用的就是沒有改之前原表裡面的資料,兩者之間不會發生衝突,該現象稱之為“不可重複讀”

2.3 repeatable read(可重複讀) # MySQL預設隔離級別

	能夠解決"髒讀"問題,但是無法解決"幻讀"

所謂幻讀指的是當某個事務在讀取某個範圍內的記錄時另外一個事務又在該範圍內插入了新的記錄,當之前的事務再次讀取該範圍的記錄會產生幻行,InnoDB和XtraDB通過多版本併發控制(MVCC)及間隙鎖策略解決該問題

2.4 serializable(可序列讀)

​ 強制事務序列執行,很少使用該級別

3.案例理解

MVCC只能在read committed(提交讀)、repeatable read(可重複讀)兩種隔離級別下工作,其他兩個不相容(read uncommitted:總是讀取最新  serializable:所有的行都加鎖)

InnoDB的MVCC通過在每行記錄後面儲存兩個隱藏的列來實現MVCC
	一個列儲存了行的建立時間
  一個列儲存了行的過期時間(或刪除時間)  # 本質是系統版本號
每開始一個新的事務版本號都會自動遞增,事務開始時刻的系統版本號會作為事務的版本號用來和查詢到的每行記錄版本號進行比較

例如
剛插入第一條資料的時候,我們預設事務id為1,實際是這樣儲存的
    username		create_version		delete_version
    jason						1					
可以看到,我們在content列插入了kobe這條資料,在create_version這列儲存了1,1是這次插入操作的事務id。
然後我們將jason修改為jason01,實際儲存是這樣的
    username		create_version		delete_version
    jason						1									2
    jason01					2
可以看到,update的時候,會先將之前的資料delete_version標記為當前新的事務id,也就是2,然後將新資料寫入,將新資料的create_version標記為新的事務id
當我們刪除資料的時候,實際儲存是這樣的
		username		create_version		delete_version
    jason01					2									 3
"""
由此當我們查詢一條記錄的時候,只有滿足以下兩個條件的記錄才會被顯示出來:
   1.當前事務id要大於或者等於當前行的create_version值,這表示在事務開始前這行資料已經存在了。
   2.當前事務id要小於delete_version值,這表示在事務開始之後這行記錄才被刪除。
"""

儲存過程

1.什麼是儲存過程

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

2.儲存過程語法格式

關鍵字: procedure
格式:
create procedure 函式名(引數)
begin
	功能體程式碼
end

呼叫其內部sql程式碼格式
call 函式名()

3.無參儲存過程

# 無參函式
delimiter $$  # 修改結束符
create procedure p1()
begin
	select * from cmd;
end $$
delimiter ;  # 修改回來結束符

# 呼叫  通過p1()呼叫其內部sql程式碼
call p1()

4.有參儲存過程

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

# 針對res需要先提前定義
set @res=10;  # 定變數名為res,變數值為10
select @res;  # 檢視res變數值為10
call p2(1,4,@res);  # 呼叫p2
select @res;  # 檢視儲存過程是否執行,滿足條件執行res=0

"""
 大前提:儲存過程在哪個庫下面建立的只能在對應的庫下面才能使用!!!
 # 1、直接在mysql中呼叫
set @res=10  # res的值是用來判斷儲存過程是否被執行成功的依據,所以需要先定義一個變數@res儲存10
call p1(2,4,10);  # 報錯
call p1(2,4,@res);  

# 檢視結果
select @res;  # 執行成功,@res變數值發生了變化
"""

5.檢視、刪除儲存過程

檢視儲存過程具體資訊
	show create procedure pro1;
檢視所有儲存過程
	show procedure status;
刪除儲存過程
	drop procedure pro1;

6.pyMySQL呼叫儲存過程

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)
# 呼叫儲存過程固定語法callproc('p2',(1,3,10))
cursor.callproc('p2',(1,3,10))  # 內部原理 @_p1_0=1,@_p1_1=3,@_p1_2=10;
# 檢視結果
print(cursor.fetchall())

# 檢視變數對應的值
cursor.execute('select @_p1_2')
# 檢視結果
print(cursor.fetchall())

MySQL函式

1.MySQL什麼是函式

相當於python中內建函式 注意與儲存的過程的區別,MySQL內建的函式只能在sql語句中使用。

2.通過help檢視函式幫助

help 函式名   檢視幫助資訊

3.移除指定字元

Trim	: 移出左右兩邊指定字元
LTrim	: 移出左側指定字元
RTrim	: 移出右側指定字元

4.大小寫轉換

Lower	: 全部轉換為小寫
Upper	: 全部轉換為大寫

5.獲取左右起始指定個數字元

Left	: 從左起始
Right	: 從右起始

6.返回讀音相似值(英文效果)

解析:
客戶表中有一個顧客登記的使用者名稱為J.Lee
但如果這是輸入錯誤真名其實叫J.Lie,可以使用soundex匹配發音類似的
where Soundex(name)=Soundex('J.Lie')

關鍵字:
Soundex

時間格式實戰案例

日期格式:date_format
'''在MySQL中表示時間格式儘量採用2022-11-11形式'''
日期格式: date_format
%Y: 年
%m: 月
%d: 日
%H: 時
%M: 分
%S: 秒
%X: 時分秒

1.建立表

CREATE TABLE blog (
    id INT PRIMARY KEY auto_increment,
    NAME CHAR (32),
    sub_time datetime
);

2.插入時間資料

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

3.查詢資料

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;


# 更多日期處理相關函式 
	date	年月日
    where date(sub_time) = '2015-03-01'
    year	年份
    where year(sub_time) = 2016
    month	月份
    where month(sub_time) = 07
    
	adddate	增加一個日期 
	addtime	增加一個時間
	datediff計算兩個日期差值
    

流程控制

if 分支

# 分支結構
declare i int default 0;
IF i = 1 THEN
	SELECT 1;
ELSEIF i = 2 THEN
	SELECT 2;
ELSE
	SELECT 7;
END IF;

while分支

# 迴圈結構
DECLARE num INT ;
SET num = 0 ;
WHILE num < 10 DO
	SELECT num ;
	SET num = num + 1 ;
END WHILE ;

索引與慢查詢優化

索引相關概念

1.什麼是索引?

​ 簡單的理解為可以幫助你加快資料查詢速度的工具 也可以把索引比喻成書的目錄,它能讓你更快的找到自己想要的內容;讓獲取的資料更有目的性,從而提高資料庫檢索資料的效能。

2.在MySQL種索引的作用

​ 索引在MySQL中也叫做“鍵”,是儲存引擎用於快速找到記錄的一種資料結構

primary key
unique key
index key

1.上述的三個key都可以加快資料查詢
2.primary key和unique key除了可以加快查詢本身還自帶限制條件而index key很單一就是用來加快資料查詢
3.外來鍵不屬於索引鍵的範圍 是用來建立關係的 與加快查詢無關  foreign key 

3.索引加快查詢的本質

	id int primary key auto_increment, # 主鍵
 	name varchar(32) unique,
  	province varchar(32)
 	age int
 	phone bigint
 	
	select name from user where phone=18818888888;  # 一頁頁的翻
	select name from user where id=99999;  # 按照目錄確定頁數找

​ 雖然索引可以加快資料查詢,但是會降低增刪的速度。

4.建立索引的作用

​ 通常情況下我們頻繁使用某些欄位查詢資料,為了提升查詢的速度可以將該欄位建立索引。

5.索引的分類

聚集索引(primary key)
主鍵、主鍵索引

輔助索引(unique,index)
除主鍵意外的都是輔助索引
覆蓋索引

select name from user where name='jason';

非覆蓋索引

select age from user where name='jason';

索引資料結構

索引底層其實是樹結構>>:樹是計算機底層的資料結構

樹有很多種型別:二叉樹、b樹、b+樹、b*數 .......

二叉樹
	二叉樹裡面還可以細分成很多領域 我們簡單的瞭解即可 
  	二叉意味著每個節點最大隻能分兩個子節點
B樹
	所有的節點都可以存放完整的資料
B+\*樹
	只有葉子節點才會存放真正的資料 其他節點只存放索引資料
 	B+葉子節點增加了指向其他葉子節點的指標
  	B*葉子節點和枝節點都有指向其他節點的指標

輔助索引在查詢資料的時候最會還是需要藉助於聚集索引
	輔助索引葉子節點存放的是資料的主鍵值

有時候就算採用索引欄位查詢資料 也可能不會走索引!!!
	最好能記三個左右的特殊情況
等值查詢與範圍查詢都快
二叉樹->平衡二叉樹->B樹->B+樹

HASH索引

將資料打散再去查詢
等值查詢快,範圍查詢慢

FULLTEXT:全文索引

只可以用在MyISAM引擎
通過關鍵字的匹配來進行查詢,類似於like的模糊匹配
like + %在文字比較少時是合適的
但是對於大量的文字資料檢索會非常的慢
全文索引在大量的資料面前能比like快得多,但是準確度很低
百度在搜尋文章的時候使用的就是全文索引,但更有可能是ES

RTREE:R樹索引

索引測試

資料準備

#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();
# 表沒有任何索引的情況下
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欄位一劍封喉 

聯合索引

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語句,然後針對性的進行優化!

全文檢索

MySQL的全文檢索功能MYISAM儲存引擎支援而InnoDB儲存引擎不支援
一般在建立表的時候啟用全文檢索功能
create table t1(
	id int primary key auto_increment,
  content text
	fulltext(content)
)engine=MyISAM;

# match括號內的值必須是fulltext括號中定義的(單個或者多個)
select content from t1 where match(content) against('jason')
'''上述語句可以用like實現但是查詢出來的結果順序不同 全文檢索會以文字匹配的良好程度排序資料再返回效果更佳'''

# 查詢擴充套件
select note_text from productnotes where Math(note_text) Against('jason' with query expansion);
"""
返回除jason外以及其他jason所在行相關文字內容行資料
eg:
	jason is handsome and cool,every one want to be cool,tony want to be more handsome;
	二三句雖然沒有jason關鍵字 但是含有jason所在行的cool和handsome
"""

# 布林文字搜尋
即使沒有定義fulltext也可以使用,但是這種方式非常緩慢效能低下
select note_text from productnotes where Match(note_text) Against('jason' in boolean mode);

# 注意事項
1.三個及三個以下字元的詞視為短詞,全文檢索直接忽略且從索引中排除
2.MySQL自身自帶一個非用詞列表,表內詞預設均被忽略(可以修改該列表)
3.出現頻率高於50%的詞自動作為非用詞忽略,該規則不適用於布林搜尋
4.針對待搜尋的文字內容不能少於三行,否則檢索不返回任何結果
5.單引號預設忽略

插入資料

資料庫經常被多個使用者訪問,insert操作可能會很耗時(特別是有很多索引需要更新的時候)而且還可能降低等待處理的select語句效能
如果資料檢索是最重要的(一般都是),則可以通過在insert與into之間新增關鍵字low_priority指示MySQL降低insert語句優先順序
	insert low_priority  into 
  
insert還可以將一條select語句的結果插入表中即資料匯入:insert select
eg:想從custnew表中合併資料到customers表中
  insert into customers(contact,email) select contact,email from custnew;

更新資料

如果使用update語句更新多列值,並且在更新這些列中的一列或者多列出現一個錯誤會導致整個update操作被取消,如果想發生錯誤也能繼續執行沒有錯誤的更新操作可以採用
	update ignore custmoers ...
  """
  update ignore  set name='jason1',id='a' where id=1;
  	name欄位正常修改
  update set name='jason2',id='h' where id=1;
  	全部更新失敗
  """

主鍵

檢視當前表主鍵自增到的值(表當前主鍵值減一)
	select last_insert_id();

外來鍵

MySQL儲存引擎可以混用,但是外來鍵不能跨引擎即使用一個引擎的表不能引用具有使用不同引擎表的外來鍵

重命名錶

rename關鍵字可以修改一個或者多個表名
	rename table customer1 to customer2;
  rename table back_cust to b_cust,
  						 back_cust1 to b_cust1,
   						 back_cust2 to b_cust2;

事務

MySQL提供兩種事務型儲存引擎InnoDB和NDB cluster及第三方XtraDB、PBXT

事務處理中有幾個關鍵詞彙會反覆出現
  事務(transaction)
  回退(rollback)
  提交(commit)
  保留點(savepoint)
		為了支援回退部分事務處理,必須能在事務處理塊中合適的位置放置佔位符,這樣如果需要回退可以回退到某個佔位符(保留點)
    建立佔位符可以使用savepoint
    	savepoint sp01;
    回退到佔位符地址
    	rollback to sp01;
    # 保留點在執行rollback或者commit之後自動釋放

安全管理

1.建立使用者
	create user 使用者名稱 identified by '密碼';
 	"""修改密碼"""
  	set password for 使用者名稱 = Password('新密碼');
    set password = Password('新密碼');  # 針對當前登入使用者
2.重新命名
	rename user 新使用者名稱 to 舊使用者名稱; 
3.刪除使用者
	drop user 使用者名稱;
4.檢視使用者訪問許可權
	show grants for 使用者名稱;
5.授予訪問許可權
	grant select on db1.* to 使用者名稱;
  # 授予使用者對db1資料庫下所有表使用select許可權
6.撤銷許可權
	revoke select on db1.* from 使用者名稱;
"""
整個伺服器
	grant all/revoke all
整個資料庫
	on db.*
特定的表
	on db.t1
"""

讀鎖(共享鎖)
	多個使用者同一時刻可以同時讀取同一個資源互不干擾
寫鎖(排他鎖)
	一個寫鎖會阻塞其他的寫鎖和讀鎖
死鎖
	1.多個事務試圖以不同的順序鎖定資源時就可能會產生死鎖
  2.多個事務同時鎖定同一個資源時也會產生死鎖
	# Innodb通過將持有最少行級排他鎖的事務回滾

事務日誌

事務日誌可以幫助提高事務的效率 
	儲存引擎在修改表的資料時只需要修改其記憶體拷貝再把該修改記錄到持久在硬碟上的事務日誌中,而不用每次都將修改的資料本身持久到磁碟
  事務日誌採用的是追加方式因此寫日誌操作是磁碟上一小塊區域內的順序IO而不像隨機IO需要次哦按的多個地方移動磁頭所以採用事務日誌的方式相對來說要快的多
  事務日誌持久之後記憶體中被修改的資料再後臺可以慢慢刷回磁碟,目前大多數儲存引擎都是這樣實現的,通常稱之為"預寫式日誌"修改資料需要寫兩次磁碟

MVCC多版本控制

MVCC只能在read committed(提交讀)、repeatable read(可重複讀)兩種隔離級別下工作,其他兩個不相容(read uncommitted:總是讀取最新  serializable:所有的行都加鎖)

InnoDB的MVCC通過在每行記錄後面儲存兩個隱藏的列來實現MVCC
	一個列儲存了行的建立時間
  一個列儲存了行的過期時間(或刪除時間)  # 本質是系統版本號
每開始一個新的事務版本號都會自動遞增,事務開始時刻的系統版本號會作為事務的版本號用來和查詢到的每行記錄版本號進行比較

例如
剛插入第一條資料的時候,我們預設事務id為1,實際是這樣儲存的
    username		create_version		delete_version
    jason						1					
可以看到,我們在content列插入了kobe這條資料,在create_version這列儲存了1,1是這次插入操作的事務id。
然後我們將jason修改為jason01,實際儲存是這樣的
    username		create_version		delete_version
    jason						1									2
    jason01					2
可以看到,update的時候,會先將之前的資料delete_version標記為當前新的事務id,也就是2,然後將新資料寫入,將新資料的create_version標記為新的事務id
當我們刪除資料的時候,實際儲存是這樣的
		username		create_version		delete_version
    jason01					2									 3
"""
由此當我們查詢一條記錄的時候,只有滿足以下兩個條件的記錄才會被顯示出來:
   1.當前事務id要大於或者等於當前行的create_version值,這表示在事務開始前這行資料已經存在了。
   2.當前事務id要小於delete_version值,這表示在事務開始之後這行記錄才被刪除。
"""

轉換表的引擎

主要有三種方式,並各有優缺點!
# 1.alter table
	alter table t1 engine=InnoDB;
  """
  	適用於任何儲存引擎 但是需要執行很長時間 MySQL會按行將資料從原表賦值到一張新的表中,在複製期間可能會消耗系統所有的IO能力,同時原表會加讀鎖
  """
# 2.匯入匯出
	"""
	使用mysqldump工具將資料匯出到檔案,然後修改檔案中相應的SQL語句
		1.引擎選項
		2.表名
	""" 	
# 3.insert ... select
	"""
	綜合了第一種方案的高效和第二種方案的安全
		1.先建立一張新的表
		2.利用insert ... select語法導資料
	資料量不大這樣做非常合適 資料量大可以考慮分批處理 針對每一段資料執行事務提交操作避免產生過多的undo
	"""
  ps:上述操作可以使用pt-online-schema-change(基於facebook的線上schema變更技術)工具,簡單方便的執行上述過程