mysql數據庫高級應用

引言

  • 視圖
  • 觸發器
  • 事務
  • 存儲過程
  • 內置函數
  • 流程控制
  • 索引

視圖

一、什麼是視圖

​ 視圖就是經過查詢獲得一張虛擬表,而後保存下來,下次直接使用便可html

二、爲何要用視圖

​ 若是要頻繁使用一張虛擬表,能夠不用重複查詢python

三、如何用視圖

create view teacher2course as
select * from teacher inner join course on teacher.tid = course.teacher_id;

強調
一、在硬盤中,視圖只有表結構文件,沒有表數據文件
二、視圖一般是用於查詢,儘可能不要修改視圖中的數據mysql

drop view teacher2course;

思考:開發過程當中會不會去使用視圖?sql

不會!視圖是mysql的功能,若是你的項目裏面大量的使用到了視圖,那意味着你後期想要擴張某個功能的時候這個功能恰巧又須要對視圖進行修改,意味着你須要先在mysql這邊將視圖先修改一下,而後再去應用程序中修改對應的sql語句,這就涉及到跨部門溝通的問題,因此一般不會使用視圖,而是經過從新修改sql語句來擴展功能數據庫

觸發器

在知足對某張表數據的增、刪、改的狀況下,自動觸發的功能稱之爲觸發器安全

爲什麼要用觸發器

​ 觸發器專門針對咱們對某一張表數據增insert、刪delete、改update的行爲,這類行爲一旦執行
​ 就會觸發觸發器的執行,即自動運行另一段sql代碼數據結構

建立觸發器語法

# 針對插入
create trigger tri_after_insert_t1 after insert on 表名 for each row
begin
   sql代碼。。。
end 
create trigger tri_after_insert_t2 before insert on 表名 for each row
begin
   sql代碼。。。
end

# 針對刪除
create trigger tri_after_delete_t1 after delete on 表名 for each row
begin
   sql代碼。。。
end
create trigger tri_after_delete_t2 before delete on 表名 for each row
begin
   sql代碼。。。
end

# 針對修改
create trigger tri_after_update_t1 after update on 表名 for each row
begin
   sql代碼。。。
end
create trigger tri_after_update_t2 before update on 表名 for each row
begin
   sql代碼。。。
end

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

事務

什麼是事務

​ 開啓一個事務能夠包含一些sql語句,這些sql語句要麼同時成功
​ 要麼一個都別想成功,稱之爲事務的原子性併發

事務的做用

保證了對數據操做的數據安全性框架

案例:用交行的卡操做建行ATM機給工商的帳戶轉錢函數

事務應該具備4個屬性:原子性、一致性、隔離性、持久性。這四個屬性一般稱爲ACID特性

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

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

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

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

如何用

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;

# 那如何檢測異常?

存儲過程

存儲過程包含了一系列可執行的sql語句,存儲過程存放於MySQL中,經過調用它的名字能夠執行其內部的一堆sql

三種開發模型

第一種

"""
應用程序:只須要開發應用程序的邏輯
mysql:編寫好存儲過程,以供應用程序調用
優勢:開發效率,執行效率都高
缺點:考慮到人爲因素、跨部門溝通等問題,會致使擴展性差
"""

第二種

"""
應用程序:除了開發應用程序的邏輯,還須要編寫原生sql
優勢:比方式1,擴展性高(非技術性的)
缺點:
一、開發效率,執行效率都不如方式1
二、編寫原生sql太過於複雜,並且須要考慮到sql語句的優化問題
"""

第三種

"""
應用程序:開發應用程序的邏輯,不須要編寫原生sql,基於別人編寫好的框架來處理數據,ORM
優勢:不用再編寫純生sql,這意味着開發效率比方式2高,同時兼容方式2擴展性高的好處
缺點:執行效率連方式2都比不過
"""

建立存儲過程

delimiter $$
create procedure p1(
   in m int,  # in表示這個參數必須只能是傳入不能被返回出去
   in n int,  
   out res int  # out表示這個參數能夠被返回出去,還有一個inout表示便可以傳入也能夠被返回出去
)
begin
   select tname from teacher where tid > m and tid < n;
   set res=0;
end $$
delimiter ;

# 小知識點補充,當一張表的字段特別多記錄也不少的狀況下,終端下顯示出來會出現顯示錯亂的問題
select * from mysql.user\G;

如何用存儲過程

# 大前提:存儲過程在哪一個庫下面建立的只能在對應的庫下面才能使用!!!

# 一、直接在mysql中調用
set @res=10  # res的值是用來判斷存儲過程是否被執行成功的依據,因此須要先定義一個變量@res存儲10
call p1(2,4,10);  # 報錯
call p1(2,4,@res);  

# 查看結果
select @res;  # 執行成功,@res變量值發生了變化

# 二、在python程序中調用
pymysql連接mysql
產生的遊表cursor.callproc('p1',(2,4,10))  # 內部原理:@_p1_0=2,@_p1_1=4,@_p1_2=10;
cursor.excute('select @_p1_2;')


# 三、存儲過程與事務使用舉例(瞭解)
delimiter //
create PROCEDURE p5(
   OUT p_return_code tinyint
)
BEGIN
   DECLARE exit handler for sqlexception
   BEGIN
       -- ERROR
       set p_return_code = 1;
       rollback;
   END;


 DECLARE exit handler for sqlwarning
 BEGIN
     -- WARNING
     set p_return_code = 2;
     rollback;
 END;

 START TRANSACTION;
     update user set balance=900 where id =1;
     update user123 set balance=1010 where id = 2;
     update user set balance=1090 where id =3;
 COMMIT;

 -- SUCCESS
 set p_return_code = 0; #0表明執行成功


END //
delimiter ;

函數

注意與存儲過程的區別,mysql內置的函數只能在sql語句中使用!

參考博客:http://www.cnblogs.com/linhaifeng/articles/7495918.html#_label2

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

流程控制

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

END //
delimiter ;

索引與慢查詢優化

知識回顧:數據都是存在硬盤上的,那查詢數據不可避免的須要進行IO操做

索引在MySQL中也叫作「鍵」,是存儲引擎用於快速找到記錄的一種數據結構。

  • primary key
  • unique key
  • index key

注意foreign key不是用來加速查詢用的,不在咱們研究範圍以內,上面三種key前兩種除了有加速查詢的效果以外還有額外的約束條件(primary key:非空且惟一,unique key:惟一),而index key沒有任何約束功能只會幫你加速查詢

索引就是一種數據結構,相似於書的目錄。意味着之後再查數據應該先找目錄再找數據,而不是用翻頁的方式查詢數據

本質都是:經過不斷地縮小想要獲取數據的範圍來篩選出最終想要的結果,同時把隨機的事件變成順序的事件,也就是說,有了這種索引機制,咱們能夠老是用同一種查找方式來鎖定數據。

索引的影響:

  • 在表中有大量數據的前提下,建立索引速度會很慢
  • 在索引建立完畢後,對錶的查詢性能會大幅度提高,可是寫的性能會下降

b+樹

https://images2017.cnblogs.com/blog/1036857/201709/1036857-20170912011123500-158121126.png

只有葉子結點存放真實數據,根和樹枝節點存的僅僅是虛擬數據

查詢次數由樹的層級決定,層級越低次數越少

一個磁盤塊兒的大小是必定的,那也就意味着能存的數據量是必定的。如何保證樹的層級最低呢?一個磁盤塊兒存放佔用空間比較小的數據項

思考咱們應該給咱們一張表裏面的什麼字段字段創建索引可以下降樹的層級高度>>> 主鍵id字段

彙集索引(primary key)

彙集索引其實指的就是表的主鍵,innodb引擎規定一張表中必需要有主鍵。先來回顧一下存儲引擎。

myisam在建表的時候對應到硬盤有幾個文件(三個)?

innodb在建表的時候對應到硬盤有幾個文件(兩個)?frm文件只存放表結構,不可能放索引,也就意味着innodb的索引跟數據都放在idb表數據文件中。

特色:葉子結點放的一條條完整的記錄

輔助索引(unique,index)

輔助索引:查詢數據的時候不可能都是用id做爲篩選條件,也可能會用name,password等字段信息,那麼這個時候就沒法利用到彙集索引的加速查詢效果。就須要給其餘字段創建索引,這些索引就叫輔助索引

特色:葉子結點存放的是輔助索引字段對應的那條記錄的主鍵的值(好比:按照name字段建立索引,那麼葉子節點存放的是:{name對應的值:name所在的那條記錄的主鍵值})

select name from user where name='jason';

上述語句叫覆蓋索引:只在輔助索引的葉子節點中就已經找到了全部咱們想要的數據

select age from user where name='jason';

上述語句叫非覆蓋索引,雖然查詢的時候命中了索引字段name,可是要查的是age字段,因此還須要利用主鍵纔去查找

測試索引

準備

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

相關文章
相關標籤/搜索