檢視,觸發器,儲存過程,流程控制等MySQL小知識點
阿新 • • 發佈:2022-11-29
檢視,觸發器,儲存過程,流程控制等MySQL小知識點
一、SQL注入問題
登入: import pymysql conn = pymysql.connect( host='127.0.0.1', port=3306, user='root', password='123', database='db5', charset='utf8', autocommit=True ) cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 1.獲取使用者的使用者名稱和密碼 user = input('username>>>').strip() password = input('password>>>').strip() # 3.構造sql語句 sql = 'select * from user where name = %s and pwd = %s ' print(sql) # 4.傳送給服務端執行sql語句 cursor.execute(sql,(user,password)) # 5.獲取執行結果 res = cursor.fetchall() if res: print('登入成功') print(res) else: print('使用者名稱或者密碼有誤')
註冊: import pymysql conn = pymysql.connect( host='127.0.0.1', port=3306, user='root', password='123', database='db5', charset='utf8', autocommit=True ) cursor = conn.cursor(cursor=pymysql.cursors.DictCursor) # 1.獲取使用者的使用者名稱和密碼 user = input('username>>>').strip() password = input('password>>>').strip() # 3.構造sql語句 sql = "insert into user(name,pwd) values(%s,%s)" print(sql) # 4.傳送給服務端執行sql語句 cursor.executemany(sql,[('wei','123'),('xin','222')]) # 5.獲取執行結果 res = cursor.fetchall() if res: print('登入成功') print(res) else: print('使用者名稱或者密碼有誤')
寫上述的登入註冊的時候記得一定要先建好表
SQL注入:利用特殊符合的組合產生特殊的含義 從而避開正常的業務邏輯
針對上述的SQL注入問題 核心在於手動拼接了關鍵資料 交給execute處理即可
cursor.execute(sql,(user,password))
cursor.executemany(sql,[('wei','123'),('xin','222')])
二、檢視
檢視就是通過查詢得到的一張虛擬表,然後儲存下來,供下次使用 # 建立檢視 create view 檢視名 as SQL語句 # 1.檢視的表只能用來查詢,不能做其他的增刪改操作 # 2.檢視最好少用,會和真表混淆的,造成干擾
mysql> create view student_class_view as select * from student inner join class on student.class_id = class.cid;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from student_class_view;
+-----+--------+----------+--------+-----+--------------+
| sid | gender | class_id | sname | cid | caption |
+-----+--------+----------+--------+-----+--------------+
| 1 | 男 | 1 | 理解 | 1 | 三年二班 |
| 2 | 女 | 1 | 鋼蛋 | 1 | 三年二班 |
| 3 | 男 | 1 | 張三 | 1 | 三年二班 |
| 4 | 男 | 1 | 張一 | 1 | 三年二班 |
| 5 | 女 | 1 | 張二 | 1 | 三年二班 |
| 6 | 男 | 1 | 張四 | 1 | 三年二班 |
| 7 | 女 | 2 | 鐵錘 | 2 | 三年三班 |
| 8 | 男 | 2 | 李三 | 2 | 三年三班 |
| 9 | 男 | 2 | 李一 | 2 | 三年三班 |
| 10 | 女 | 2 | 李二 | 2 | 三年三班 |
| 11 | 男 | 2 | 李四 | 2 | 三年三班 |
| 12 | 女 | 3 | 如花 | 3 | 一年二班 |
| 13 | 男 | 3 | 劉三 | 3 | 一年二班 |
| 14 | 男 | 3 | 劉一 | 3 | 一年二班 |
| 15 | 女 | 3 | 劉二 | 3 | 一年二班 |
| 16 | 男 | 3 | 劉四 | 3 | 一年二班 |
+-----+--------+----------+--------+-----+--------------+
16 rows in set (0.01 sec)
三、觸發器
觸發器:針對資料的增刪改自動出發的功能
主要有六種情況:增前,增後,改前,改後,刪前,刪後
語法結構:
delimiter 自定義結束符號
create trigger 觸發器的名字 before/after insert/update/delete on 表名 for each row
begin
sql語句
end 自定義結束符
delimiter ; # 改為原來的結束符,便於統一
'''觸發器內部的SQL語句需要用到;號,但是分號是SQL語句的預設結束符,為了我們完整的寫語句,我們就需要修改預設的結束符
delimiter &&
delimiter; 最後改為;
'''
on 表 for each :觸發物件,觸發器繫結的是指是表中的所有行,因此當每一行發生指定改變時,觸發器就會發生。
1.觸發器名稱:標識觸發器名稱,使用者自行指定;
2.觸發時間:觸發時間為before或after;
3.觸發事件:表示觸發事件,取值為insert、update、delete;
4.觸發器表名:標識建立觸發器的表名,即在哪張表上建立觸發器;
5.SQL語句:begin和and包含的多條的SQL語句,可以是一條也可以是多條
'''建立六種觸發器:before insert、before update、before delete、after insert、after update、after delete'''
例題:
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
('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');
# 查詢errlog記錄
select * from errlog;
# 檢視所有的觸發器
show trigger;
# 刪除觸發器
drop trigger tri_after_insert_cmd;
# 沒有插入資料到errlog,但是出現了資料,因為觸發器內的有邏輯程式碼
四、事物
1.事物的四大特性(ACID)
事物就是要保證資料的安全
1.原子性:事物中的各項操作是不可分割的整體,要麼同時成功,要麼同時失敗
2.一致性:使資料庫從一個一致性狀態變到另一個一致性狀態
3.隔離性:多個事物之間彼此不干擾
4.永續性:也稱永久性,指一個事物一旦提交,它對資料庫的改變就是永久性的
2.事物的流程
1.開啟:任意一條DML語句(insert,update,delete)執行,標誌事物開啟,或者用begin標誌著開啟
2.commit (提交):成功的結束,將所有的DML語句的操作與硬碟資料同步(真正的存到硬碟中)
rollback(回滾):失敗的結束,將所有的DML語句回滾到進行事物前,也就是相當於沒有對資料進行操作。
3.建立事物
mysql> create table user(
-> id int primary key auto_increment,
-> name char(32),
-> balance int
-> );
Query OK, 0 rows affected (0.02 sec)
mysql>
mysql> insert into user(name,balance)
-> values
-> ('jason',1000),
-> ('kevin',1000),
-> ('tank',1000);
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
# 修改資料之前先開啟事務操作
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> update user set balance=900 where name='jason'; #買支付100元
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> update user set balance=1010 where name='kevin'; #中介拿走10元
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> update user set balance=1090 where name='tank'; #賣家拿到90元
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from user;
+----+-------+---------+
| id | name | balance |
+----+-------+---------+
| 1 | jason | 900 |
| 2 | kevin | 1010 |
| 3 | tank | 1090 |
+----+-------+---------+
3 rows in set (0.00 sec)
mysql> rollback; # 回滾事物
Query OK, 0 rows affected (0.01 sec)
mysql> select * from user;
+----+-------+---------+
| id | name | balance |
+----+-------+---------+
| 1 | jason | 1000 |
| 2 | kevin | 1000 |
| 3 | tank | 1000 |
+----+-------+---------+
3 rows in set (0.00 sec)
4.事物處理的關鍵詞彙
1.事物(transaction):使用者定義的一個數據庫操作序列
2.回退(rollback):事物回退是指撤銷該事物已經完成對該資料庫的操作
3.提交(commit):將資料永久性的儲存到資料庫中
4.保留點(savepoint):為了支援回退部分事物處理,必須能在事物處理塊中合適的位置放置佔位符,這樣如果需要回退,可以回退到某個佔位符(保留點)。建立佔位符可以使用savepoint,插在SQL語句中。
建立佔位符:
savepoint sp01;
回退到保留點:
rollback to sp01;
# 保留點在執行rollback或者commit之後自動釋放
事物預設字典提交
set autocommit = 1; # 自動提交開啟(預設為開啟)
set autocommit = 0; # 自動提交關閉
5.事物的特性之隔離性
1.讀未提交(read uncommitted)
讀未提交(read uncommitted)
事務中修改即使沒有提交,對其他事務都是可見的,事務可以讀取未提交的資料,而導致前後兩次讀取的資料不一致的情況,這一現象也稱為"髒讀"。
當執行一個事物時,修改了資料但是並沒有提交,另一個事物執行時讀取的資料就是修改但是並沒有提交的資料。
2.讀已提交(read committed)
大多資料庫系統預設的隔離級別,一個事物從開始直到提交之前所作的任何修改對其他事務都是不可見的,一個事務讀取到其他事務以提交的資料導致前後兩次讀取的資料不一樣,這種級別也叫做"不可重複讀"。
當執行一個事物時,修改了資料但是沒有提交,另一個事物執行時讀取的資料是硬碟內的資料,就是沒有修改的資料。但是將來要將資料刷到硬碟則需要用到標誌位。
3.可重複讀(repeatable read) # mysql預設隔離級別
能夠解決"髒讀"問題,但是無法解決"幻讀"。
所謂幻讀指的是當某個事物讀取某個範圍內的記錄時另外一個事物又在該範圍內插入了新的記錄,導致前後兩次讀取的資料不一致,當之前的事物再次讀取該範圍的記錄時會產生幻讀。
InnoDB和XtraDB通過多版本併發控制(MVCC)及間隙鎖策略解決幻讀問題。
4.可序列讀(serializable)
強制事物序列執行,很少使用該級別。
通過全域性標誌位加多版本併發控制(MVCC)及間隙鎖策略就能夠實現多個事物併發執行的效果,並且不會出現錯亂。
五、InnoDB的MVCC(Multi-Version Concurrency Control)多版本併發控制
目的:為了查詢一些正在被另一個事務更新的行,並且可以看到它們被更新之前的值。
實現原理:在每一行記錄的後面增加兩個隱藏列,記錄建立版本號和刪除版本號,每開啟一個新事務,事務的版本號就會遞增,並把查詢到的資料複製到記憶體中,後續的所有增刪改查操作都是針對於記憶體中的資料,再把修改行為記錄到持久在硬碟上的事務日誌中(redo log),而不用每次都將修改的資料本身持久到磁碟。在事務提交時進行一次flush操作,根據redo log儲存到磁碟中。
MVCC只能在read committed(提交讀)、repeatable read(可重複讀)兩種隔離級別下工作,其他兩個不相容(read uncommitted:總是讀取最新 serializable:所有的行都加鎖)
InnoDB的MVCC通過在每行記錄後面儲存兩個隱藏的列來實現MVCC:
一個列儲存了行的建立時間
一個列儲存了行的過期時間(或刪除時間) # 本質是系統版本號
每開始一個新的事務版本號都會自動遞增,事務開始時刻的系統版本號會作為事務的版本號用來和查詢到的每行記錄版本號進行比較。
六、儲存過程
儲存過程包含了一系列可執行的SQL語句,儲存過程存放於MySQL中,通過呼叫他的名字可以執行其內部的一堆sql,類似於Python中的自定義函式
delimiter 臨時結束符
# 宣告儲存過程
create procedure 名字(引數,引數)
begin
sql語句
end 臨時結束符
delimiter ;
1.變數賦值
set @變數名 = 1;
2.定義變數
declare 變數名 int unsigned default 100;
3.儲存過程體
create function 儲存函式名
4.呼叫儲存過程
call 儲存過程名
5.刪除儲存過程
drop procedure 名稱
6.檢視某庫中儲存過程
show procedure status where db = '資料庫名'
7.檢視特定的儲存過程
show create procedure 資料庫名.儲存過程名
8.檢視儲存過程具體資訊
show create procedure 名稱;
9.檢視所有的儲存過程
show procedure status;
程式碼演示:
mysql> select left('foobarbar',5);
+---------------------+
| left('foobarbar',5) |
+---------------------+
| fooba |
+---------------------+
1 row in set (0.00 sec)
mysql> delimiter $
mysql> create procedure p1()
-> begin
-> select * from cmd;
-> end $
Query OK, 0 rows affected (0.00 sec)
mysql> delimiter;
-> delimiter ;
-> $
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'delimiter;delimiter' at line 1
mysql> delimiter ;
mysql> call p1();
+----+-------+------+-----------------+---------------------+---------+
| id | USER | priv | cmd | sub_time | success |
+----+-------+------+-----------------+---------------------+---------+
| 1 | kevin | 0755 | ls -l /etc | 2022-11-29 17:39:26 | yes |
| 2 | kevin | 0755 | cat /etc/passwd | 2022-11-29 17:39:26 | no |
| 3 | kevin | 0755 | useradd xxx | 2022-11-29 17:39:26 | no |
| 4 | kevin | 0755 | ps aux | 2022-11-29 17:39:26 | yes |
+----+-------+------+-----------------+---------------------+---------+
4 rows in set (0.00 sec)
Query OK, 0 rows affected (0.01 sec)
# 有參函式
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; 定義
select @res; 檢視
call p1(1,5,@res) 呼叫
select @res 檢視
"""
檢視儲存過程具體資訊
show create procedure pro1;
檢視所有儲存過程
show procedure status;
刪除儲存過程
drop procedure pro1;
"""
七、內建函式
1.移除指定字元
Trim,LTrim,RTrim
2.大小寫轉換
Upper,lower
3.獲取左右起始指定個數字元
Left,Right
4.返回讀音相似值
Soundex
eg:
客戶表中有一個顧客登記的使用者名稱為J.Lee,但如果這是輸入錯誤真名其實叫J.Lie,可以使用soundex匹配發音類似的。
where Soundex(name)=Soundex('J.Lie')
5.日期格式:date_format
'''在MySQL中表示時間格式儘量採用2022-11-11形式'''
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');
mysql> select date_format(sub_time,'%Y-%m'),count(id) from blog group by date_format(sub_time,'%Y-%m');
+-------------------------------+-----------+
| date_format(sub_time,'%Y-%m') | count(id) |
+-------------------------------+-----------+
| 2015-03 | 2 |
| 2016-07 | 4 |
| 2017-03 | 3 |
+-------------------------------+-----------+
3 rows in set (0.00 sec)
1.where Date(sub_time) = '2015-03-01'
2.where Year(sub_time)=2016 AND Month(sub_time)=07;
# 更多日期處理相關函式
adddate 增加一個日期
addtime 增加一個時間
datediff 計算兩個日期差值
八、流程控制
# 分支結構
declare i int default 0;
if i = 1 then
select 1;
elseif i = 2 then
select 2;
else
select 7;
end if;
# 迴圈結構
declare num int;
set num = 0;
while num < 10 do
select num ;
set num = num + 1;
end while;
九、索引
1.索引
1)索引就好比一本書的目錄,它能讓你更快的找到自己想要的內容
2)讓獲取的資料更有目的性,從而提高資料庫檢索資料的效能
索引在MySQL中也叫做“鍵”,是儲存引擎用於快速找到記錄的一種資料結構
* primary key
* unique key
* index key
1.上述的三個key都可以加快資料查詢
2.primary key和unique key除了可以加快查詢本身還自帶限制條件而index key很單一就是用來加快資料查詢
3.外來鍵不屬於索引鍵的範圍 是用來建立關係的 與加快查詢無關
索引加快查詢的本質
id int primary key auto_increment,
name varchar(32) unique,
province varchar(32)
age int
phone bigint
select name from userinfo where phone=18818888888; # 一頁頁的翻
select name from userinfo where id=99999; # 按照目錄確定頁數找
索引可以加快資料查詢 但是會降低增刪的速度
通常情況下我們頻繁使用某些欄位查詢資料
為了提升查詢的速度可以將該欄位建立索引
聚集索引(primary key)
主鍵、主鍵索引
聚集索引其實指的就是表的主鍵,innodb引擎規定一張表中必須要有主鍵。
輔助索引(unique,index)
除主鍵意外的都是輔助索引,查詢資料的時候不可能都是用id作為篩選條件,也可能會用name,password等欄位資訊,那麼這個時候就無法利用到聚集索引的加速度查詢結果。就需要給其他欄位建立索引,這些索引就叫輔助索引。
覆蓋索引
select name from user where name='jason';
非覆蓋索引
select age from user where name='jason';
2.索引底層原理
樹:是一種資料結構,主要用於優化資料查詢的操作。
1.二叉樹
本身是有序樹,數中的各個節點不能超過兩個分支。
特點:
二叉樹中,第i層最多有2^( i-1)個節點
如果二叉樹的深度為K,那麼次二叉樹最多有2^K-1 個節點
二叉樹中,終端結點數(葉子結點數)為n0,度為2的結點數為n2,則n0=n2+1
除了葉子結點可以有多個分支,其他節點最多隻能兩個分支。所有的節點都可以直接存放完整資料(每一個數據塊是有固定大小的)。
2.B樹(B-樹)
除了葉子結點可以有多個分支,其他節點最多隻能兩個分支。所有的節點都可以直接存放完整資料(每一個數據塊是有固定大小的)。
所有的節點都可以存放這個資料的完整資料,有缺點,磁碟塊大小是固定的,意味這一個節點裡儲存的資料很少。樹的層級越高,查詢的次數越多。樹的層級越低,查詢的越快。
特點:
關鍵字集合分佈在整顆樹中;
任何一個關鍵字出現且只出現在一個結點中;
搜尋有可能在非葉子結點結束;
其搜尋效能等價於在關鍵字全集內做一次二分查詢;
自動層次控制;
3.B+樹
只有葉子節點存放真正的資料,其他節點只存主鍵值(輔助索引值)。
節點能夠儲存更多的主鍵值,說明樹的層級越低,查詢的速度越快。
id作為主鍵,因為id是整型,儲存的位元組小,可以在一個磁碟外裡能夠存放更多的主鍵值。
一個磁碟塊的容量是有限的,那麼儘可能儲存更多的資訊,降低樹的層級,提升資料查詢效率,降低查詢時間。
所有關鍵字都出現在葉子結點的連結串列中,且連結串列中的關鍵字恰好是有序的;
不可能在非葉子結點命中;
非葉子結點相當於是葉子結點的索引(稀疏索引),葉子結點相當於是儲存(關鍵字)資料的資料層;
更適合檔案索引系統;
十、慢查詢優化
explain select name,countrycode from city where id=1;
序列 | 常見的索引掃描型別 |
---|---|
1 | index |
2 | range |
3 | ref |
4 | eq_ref |
5 | const |
6 | system |
7 | null |
從上到下,效能從最差到最好,我們認為至少要達到range級別
index:Full Index Scan,index與all區別為index型別只遍歷索引樹。
range:索引範圍掃描,對索引的掃描開始於某一點,返回匹配值域的行,顯而易見的索引範圍掃描是帶有between或者where子句裡帶有<,>查詢。
alter table city add index idx_city(population);
explain select * from city where population>30000000;
ref:使用非唯一索引掃描或者唯一索引的字首掃描,返回匹配某個單獨值的記錄行。
alter table city drop key idx_code;
explain select * from city where countrycode='chn';
explain select * from city where countrycode in ('CHN','USA');
explain select * from city where countrycode='CHN' union all select * from city where countrycode='USA';
eq_ref:類似ref,區別就在使用的索引是唯一索引,對於每個索引鍵值,表中只有一條記錄匹配,簡單來說,就是多表連線中使用primary key或者unique key作為關聯條件A
explain select * from A join B on A.sid=B.sid;
const、system:當MySQL對查詢某部分進行優化,並轉換為一個常量時,使用這些型別訪問。如將主鍵置於where列表中,MySQL就能將該查詢轉換為一個常量。
explain select * from city where id=1000;
null:MySQL在優化過程中分解語句,執行時甚至不用訪問表或索引,例如從一個索引列裡選取最小值可以通過單獨索引查詢完成。
explain select * from city where id=1000000000000000000000000000;
十一、測試索引
#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'; # 速度變快
十二、全文檢索
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;
全部更新失敗
"""
十五、鎖
讀鎖(共享鎖) 多個使用者同一時刻可以同時讀取同一個資源互不干擾寫鎖(排他鎖) 一個寫鎖會阻塞其他的寫鎖和讀鎖死鎖
1.多個事務試圖以不同的順序鎖定資源時就可能會產生死鎖
2.多個事務同時鎖定同一個資源時也會產生死鎖
# Innodb通過將持有最少行級排他鎖的事務回滾
十六、資料庫三大正規化
1.第一正規化(1NF):屬性不可分割,即每個屬性都是不可分割的原子項。(實體的屬性即表中的列)
2.第二正規化(2NF):滿足第一正規化,且不存在部分依賴,即非主屬性必須完全依賴於主屬性
3.第三正規化(3NF):滿足第二正規化;且不存在傳遞依賴,即非主屬性不能與非主屬性之間有依賴關係,非主屬性必須直接依賴於主屬性,不能間接依賴主屬性。(A -> B, B ->C, A -> C)