MySQL之三:索引及執行計劃

一.擴展php

https://www.yiibai.com/mysql/stored-function.htmlhtml

1.子查詢

  • 定義python

 子查詢容許把一個查詢嵌套在另外一個查詢當中。
 子查詢,又叫內部查詢,相對於內部查詢,包含內部查詢的就稱爲外部查詢。
 子查詢能夠包含普通select能夠包括的任何子句,好比:distinct、 group by、order by、limit、join和union等;可是對應的外部查詢必須是如下語句之一:select、insert、update、delete、set或 者do。
 子查詢的位置:
 select 中、from 後、where 中.group by 和order by 中無實用意義

分類:mysql

可使用的操做符:= > < >= <= <> ANY IN SOME ALL EXISTS 一個子查詢會返回一個標量(就一個值)、一個行、一個列或一個表,這些子查詢稱之爲標量、行、列和表子查詢。 若是子查詢返回一個標量值(就一個值),那麼外部查詢就可使用:=、>、<、>=、<=和<>符號進行比較判斷;若是子查詢返回的不是一個標量值,而外部查詢使用了比較符和子查詢的結果進行了比較,那麼就會拋出異常。linux

1.1標量子查詢

定義git

子查詢返回的是單一值的標量,如一個數字或一個字符串,也是子查詢中最簡單的返回形式。 可使用 = > < >= <= <> 這些操做符對子查詢的標量結果進行比較,一般子查詢的位置在比較式的右側 github

例子:

 SELECT * FROM article WHERE uid = (SELECT uid FROM user WHERE status=1 ORDER BY uid DESC LIMIT 1)
 SELECT * FROM t1 WHERE column1 = (SELECT MAX(column2) FROM t2)
 SELECT * FROM article AS t WHERE 2 = (SELECT COUNT(*) FROM article WHERE article.uid = t.uid)

1.2 列子查詢

定義面試

指子查詢返回的結果集是 N 行一列,該結果一般來自對錶的某個字段查詢返回。 可使用 = > < >= <= <> 這些操做符對子查詢的標量結果進行比較,一般子查詢的位置在比較式的右側 ;可使用 IN、ANY、SOME 和 ALL 操做符,不能直接使用 = > < >= <= <> 這些比較標量結果的操做符。 算法

 SELECT * FROM article WHERE uid IN(SELECT uid FROM user WHERE status=1)
 SELECT s1 FROM table1 WHERE s1 > ANY (SELECT s2 FROM table2)
 SELECT s1 FROM table1 WHERE s1 > ALL (SELECT s2 FROM table2) 

注意

NOT IN 是 <> ALL 的別名,兩者相同。 若是 table2 爲空表,則 ALL 後的結果爲 TRUE; 若是子查詢返回如 (0,NULL,1) 這種儘管 s1 比返回結果都大,但有空行的結果,則 ALL 後的結果爲 UNKNOWN 。 對於 table2 空表的狀況,下面的語句均返回 NULL: sql

 SELECT s1 FROM table1 WHERE s1 > (SELECT s2 FROM table2)
 SELECT s1 FROM table1 WHERE s1 > ALL (SELECT MAX(s1) FROM table2)

1.3 行子查詢

定義:

指子查詢返回的結果集是一行 N 列,該子查詢的結果一般是對錶的某行數據進行查詢而返回的結果集。

例子:

 SELECT * FROM table1 WHERE (1,2) = (SELECT column1, column2 FROM table2)
 注:(1,2) 等同於 row(1,2)
 SELECT * FROM article WHERE (title,content,uid) = (SELECT title,content,uid FROM blog WHERE bid=2)

1.4 表子查詢

指子查詢返回的結果集是 N 行 N 列的一個表數據。

例子:

SELECT * FROM article WHERE (title,content,uid) IN (SELECT title,content,uid FROM blog)

不能優化的子查詢

http://www.cnblogs.com/loveyouyou616/archive/2012/12/21/2827655.html

 1.mysql不支持子查詢合併和聚合函數子查詢優化,mariadb對聚合函數子查詢進行物化優化;
   2.mysql不支持from子句子查詢優化,mariadb對from子句子查詢進行子查詢上拉優化;
   3.mysql和mariadb對子查詢展開提供有限的支持,如對主鍵的操做才能進行上拉子查詢優化;
   4.mysql不支持exists子查詢優化,mariadb對exists關聯子查詢進行半鏈接優化,對exists非關聯子查詢沒有進一步進行優化;
   5.mysql和mariadb不支持not exists子查詢優化;
   6.mysql和mariadb對in子查詢,對知足半鏈接語義的查詢進行半鏈接優化,再基於代價評估進行優化,二者對半鏈接的代價評估選擇方式有差別;
   7.mysql不支持not in子查詢優化,mariadb對非關聯not in子查詢使用物化優化,對關聯not in子查詢不作優化;
   8.mysql和mariadb對>all非關聯子查詢使用max函數,<all非關聯子查詢使用min函數,對=all和非關聯子查詢使用exists優化;
   9.對>some和>any非關聯子查詢使用min函數,對<some和<any非關聯子查詢使用max函數,=any 和=some子查詢使用半鏈接進行優化,對>some和>any關聯子查詢以及<some和<any關聯子查詢只有exists 優化。

2.內置函數

2.1 數學函數

 abs(x)
 pi()
 mod(x,y)
 sqrt(x)
 ceil(x)或者ceiling(x)
 rand(),rand(N):返回0-1間的浮點數,使用不一樣的seed N能夠得到不一樣的隨機數
 round(x, D):四捨五入保留D位小數,D默認爲0, 能夠爲負數, 如round(19, -1)返回20
 truncate(x, D):截斷至保留D位小數,D能夠爲負數, 如trancate(19,-1)返回10
 sign(x): 返回x的符號,正負零分別返回1, -1, 0
 pow(x,y)或者power(x,y)
 exp(x):e^x
 log(x):天然對數
 log10(x):以10爲底的對數
 radians(x):角度換弧度
 degrees(x):弧度換角度
 sin(x)和asin(x):
 cos(x)和acos(x):
 tan(x)和atan(x):
 cot(x):

2.2 字符串函數

 
char_length(str):返回str所包含的字符數,一個多字節字符算一個字符
 length(str): 返回字符串的字節長度,如utf8中,一個漢字3字節,數字和字母算一個字節
 concat(s1, s1, ...): 返回鏈接參數產生的字符串
 concat_ws(x, s1, s2, ...): 使用鏈接符x鏈接其餘參數產生的字符串
 INSERT(str,pos,len,newstr):返回str,其起始於pos,長度爲len的子串被newstr取代。
 1. 若pos不在str範圍內,則返回原字符串str
 2. 若str中從pos開始的子串不足len,則將從pos開始的剩餘字符用newstr取代
 3. 計算pos時從1開始,若pos=3,則從第3個字符開始替換
 lower(str)或者lcase(str):
 upper(str)或者ucase(str):
 left(s,n):返回字符串s最左邊n個字符
 right(s,n): 返回字符串最右邊n個字符
 lpad(s1, len, s2): 用s2在s1左邊填充至長度爲len, 若s1的長度大於len,則截斷字符串s1至長度len返回
 rpad(s1, len, s2):
 ltrim(s):刪除s左側空格字符
 rtrim(s):
 TRIM([{BOTH | LEADING | TRAILING} [remstr] FROM] str)或TRIM([remstr FROM] str):從str中刪除remstr, remstr默認爲空白字符
 REPEAT(str,count):返回str重複count次獲得的新字符串
 REPLACE(str,from_str,to_str): 將str中的from_str所有替換成to_str
 SPACE(N):返回長度爲N的空白字符串
 STRCMP(str1,str2):若str1和str2相同,返回0, 若str1小於str2, 返回-1, 不然返回1.
 SUBSTRING(str,pos), SUBSTRING(str FROM pos), SUBSTRING(str,pos,len), SUBSTRING(str FROM pos FOR len),MID(str,pos,len): 獲取特定位置,特定長度的子字符串
 LOCATE(substr,str), LOCATE(substr,str,pos),INSTR(str,substr),POSITION(substr IN str): 返回字符串中特定子串的位置,注意這裏INSTR與其餘函數的參數位置是相反的
 REVERSE(str)
 ELT(N,str1,str2,str3,...):返回參數strN, 若N大於str參數個數,則返回NULL
 FIELD(str,str1,str2,str3,...): 返回str在後面的str列表中第一次出現的位置,若找不到str或者str爲NULL, 則返回0
 FIND_IN_SET(str,strlist):strlist是由','分隔的字符串,若str不在strlist或者strlist爲空字符串,則返回0;若任意一個參數爲NULL則返回NULL
 MAKE_SET(bits,str1,str2,...): 由bits的做爲位圖來選取strN參數,選中的參數用','鏈接後返回

2.3日期和時間函數

 
CURDATE(), CURRENT_DATE, CURRENT_DATE():用於獲取當前日期,格式爲'YYYY-MM-DD'; 若+0則返回YYYYMMDD
 UTC_DATE, UTC_DATE():返回當前世界標準時間
 CURTIME([fsp]), CURRENT_TIME, CURRENT_TIME([fsp]): 用於獲取當前時間, 格式爲'HH:MM:SS' 若+0則返回 HHMMSS
 UTC_TIME, UTC_TIME([fsp])
 CURRENT_TIMESTAMP, CURRENT_TIMESTAMP([fsp]), LOCALTIME, LOCALTIME([fsp]), SYSDATE([fsp]), NOW([fsp]): 用於獲取當前的時間日期,格式爲'YYYY-MM-DD HH:MM:SS',若+0則返回YYYYMMDDHHMMSS
 UTC_TIMESTAMP, UTC_TIMESTAMP([fsp])
 UNIX_TIMESTAMP(), UNIX_TIMESTAMP(date):返回一個unix時間戳('1970-01-01 00:00:00' UTC至今或者date的秒數),這其實是從字符串到整數的一個轉化過程
 FROM_UNIXTIME(unix_timestamp), FROM_UNIXTIME(unix_timestamp,format):從時間戳返回'YYYY-MM-DD HH:MM:SS' 或者YYYYMMDDHHMMSS,加入format後根據所需的format顯示。
 MONTH(date)
 MONTHNAME(date)
 DAYNAME(date)
 DAY(date),DAYOFMONTH(date):1-31或者0
 DAYOFWEEK(date):1-7==>星期天-星期六
 DAYOFYEAR(date): 1-365(366)
 WEEK(date[,mode]):判斷是一年的第幾周,若是1-1所在周在新的一年多於4天,則將其定爲第一週;不然將其定爲上一年的最後一週。mode是用來人爲定義一週從星期幾開始。
 WEEKOFYEAR(date):相似week(date,3),從週一開始計算一週。
 QUARTER(date):返回1-4
 HOUR(time):返回時間中的小時數,能夠大於24
 MINUTE(time):
 SECOND(time):
 EXTRACT(unit FROM date):提取日期時間中的要素
 TIME_TO_SEC(time)
 SEC_TO_TIME(seconds)
 TO_DAYS(date): 從第0年開始的天數
 TO_SECNDS(expr):從第0年開始的秒數
 ADDDATE(date,INTERVAL expr unit), ADDDATE(expr,days),DATE_ADD(date,INTERVAL expr unit)
 DATE_SUB(date,INTERVAL expr unit), DATE_SUB(date,INTERVAL expr unit)
 ADDTIME(expr1,expr2)
 SUBTIME(expr1,expr2)
 DATE_FORMAT(date,format):
 DATEDIFF(expr1,expr2):返回相差的天數
 TIMEDIFF(expr1,expr2):返回相隔的時間

注意:時間日期的加減也能夠直接用+/-來進行

 date + INTERVAL expr unit
 date - INTERVAL expr unit
 如:
 SELECT '2008-12-31 23:59:59' + INTERVAL 1 SECOND;##'2009-01-01 00:00:00'
 SELECT INTERVAL 1 DAY + '2008-12-31';##'2009-01-01'
 SELECT '2005-01-01' - INTERVAL 1 SECOND;##'2004-12-31 23:59:59'

2.4條件判斷函數

 IF(expr1,expr2,expr3):若是expr1不爲0或者NULL,則返回expr2的值,不然返回expr3的值
 IFNULL(expr1,expr2):若是expr1不爲NULL,返回expr1,不然返回expr2
 NULLIF(expr1,expr2): 若是expr1=expr2則返回NULL, 不然返回expr2
 CASE value WHEN [compare_value] THEN result [WHEN [compare_value] THEN result ...] [ELSE result] END
 當compare_value=value時返回result
 CASE WHEN [condition] THEN result [WHEN [condition] THEN result ...] [ELSE result] END
 當condition爲TRUE時返回result
 SELECT CASE 1 WHEN 1 THEN 'one'
 WHEN 2 THEN 'two' ELSE 'more' END;##'one'
 SELECT CASE WHEN 1>0 THEN 'true' ELSE 'false' END;##'true'
 SELECT CASE BINARY 'B'
 WHEN 'a' THEN 1 WHEN 'b' THEN 2 END;##NULL

2.5系統信息函數

 VERSION():返回mysql服務器的版本,是utf8編碼的字符串
 CONNECTION_ID():顯示鏈接號(鏈接的線程號)
 DATABASE(),SCHEMA():顯示當前使用的數據庫
 SESSION_USER(), SYSTEM_USER(), USER(), CURRENT_USER, CURRENT_USER():返回當前的用戶名@主機,utf8編碼字符串
 CHARSET(str)
 COLLATION(str)
 LAST_INSERT_ID():自動返回最後一個insert或者update查詢, 爲auto_increment列設置的第一個發生的值

2.6加密和壓縮函數

PASSWORD(str):這個函數的輸出與變量old_password有關。old_password 在mysql5.6中默認爲0。 不一樣取值的效果以下表

 old_password=1時, password(str)的效果與old_password(str)相同,因爲其不夠安全已經棄用(5.6.5之後)。
old_password=2時,在生成哈希密碼時會隨機加鹽。
MD5(str):計算MD5 128位校驗和,返回32位16進制數構成的字符串,當str爲NULL時返回NULL。能夠用做哈希密碼
 SHA1(str), SHA(str):計算160位校驗和,返回40位16進制數構成的字符串,當str爲NULL時返回NULL。
 SHA2(str, hash_length):計算SHA-2系列的哈希方法(SHA-224, SHA-256, SHA-384, and SHA-512). 第一個參數爲待校驗字符串,第二個參數爲結果的位數(224, 256, 384, 512)
 ENCRYPT(str[,salt]): 用unix crypt()來加密str. salt至少要有兩位字符,不然會返回NULL。若未指定salt參數,則會隨機添加salt。
ECODE(crypt_str,pass_str):解密crypt_str, pass_str用做密碼
ENCODE(str,pass_str):用pass_str做爲密碼加密str
DES_ENCRYPT(str[,{key_num|key_str}]):用Triple-DES算法編碼str, 這個函數只有在mysql配置成支持ssl時纔可用。
DES_DECRYPT(crypt_str[,key_str])
AES_ENCRYPT(str,key_str[,init_vector])
AES_DECRYPT(crypt_str,key_str[,init_vector])
COMPRESS(string_to_compress):返回二進制碼
UNCOMPRESS(string_to_uncompress)

2.7 聚合函數

若在沒使用group by時使用聚合函數,至關於把全部的行都歸於一組來進行處理。除非特殊說明,通常聚合函數會忽略掉NULL. AVG([DISTINCT] expr): 返回expr的平均值,distinct選項用於忽略重複值 COUNT([DISTINCT] expr):返回select中expr的非0值個數,返回值爲bigint類型 group_concat:鏈接組內的非空值,若無非空值,則返回NULL

 GROUP_CONCAT([DISTINCT] expr [,expr ...]
 [ORDER BY {unsigned_integer | col_name | expr}
 [ASC | DESC] [,col_name ...]]
 [SEPARATOR str_val])
 MAX([DISTINCT] expr)
 MIN([DISTINCT] expr)
 SUM([DISTINCT] expr)
 VAR_POP(expr)
 VARIANCE(expr):同VAR_POP(expr),可是這是標準sql的一個擴展函數
 VAR_SAMP(expr)
 STD(expr): 這是標準sql的一個擴展函數
 STDDEV(expr):這個函數是爲了跟oracle兼容而設置的
 STDDEV_POP(expr):這個是sql標準函數
 STDDEV_SAMP(expr):樣本標準差

2.8格式或類型轉化函數

 FORMAT(X,D[,locale]):將數字X轉化成'#,###,###.##'格式,D爲保留的小數位數
 CONV(N,from_base,to_base):改變數字N的進制,返回值爲該進制下的數字構成的字符串
 INET_ATON(expr):ip字符串轉數字
 INET_NTOA(expr):數字轉ip字符串
 CAST(expr AS type):轉換數據類型
 CONVERT(expr,type), CONVERT(expr USING transcoding_name): type能夠爲BINARY[(N)],CHAR[(N)],DATE,DATETIME, DECIMAL[(M[,D])],DECIMAL[(M[,D])],TIME,UNSIGNED [INTEGER]等等。transcoding_name如utf8等等

3.存儲過程

存儲過程是存儲在數據庫目錄中的一段聲明性SQL語句。 觸發器,其餘存儲過程以及JavaPythonPHP等應用程序能夠調用存儲過程。

自身的存儲過程稱爲遞歸存儲過程。大多數數據庫管理系統支持遞歸存儲過程。 可是,MySQL不支持它。

 

 

MySQL中存儲過程

MySQL是最受歡迎的開源RDBMS,被社區和企業普遍使用。 然而,在它發佈的第一個十年期間,它不支持存儲過程,存儲函數觸發器事件。自從MySQL 5.0版本以來,這些功能被添加到MySQL數據庫引擎,使其更加靈活和強大。

MySQL存儲過程的優勢

一般存儲過程有助於提升應用程序的性能。當建立,存儲過程被編譯以後,就存儲在數據庫中。 可是,MySQL實現的存儲過程略有不一樣。 MySQL存儲過程按需編譯。 在編譯存儲過程以後,MySQL將其放入緩存中。 MySQL爲每一個鏈接維護本身的存儲過程高速緩存。 若是應用程序在單個鏈接中屢次使用存儲過程,則使用編譯版本,不然存儲過程的工做方式相似於查詢。

存儲過程有助於減小應用程序和數據庫服務器之間的流量,由於應用程序沒必要發送多個冗長的SQL語句,而只能發送存儲過程的名稱和參數。

存儲的程序對任何應用程序都是可重用的和透明的。 存儲過程將數據庫接口暴露給全部應用程序,以便開發人員沒必要開發存儲過程當中已支持的功能。

存儲的程序是安全的。 數據庫管理員能夠向訪問數據庫中存儲過程的應用程序授予適當的權限,而不向基礎數據庫表提供任何權限。

除了這些優勢以外,存儲過程有其自身的缺點,在數據庫中使用它們以前,您應該注意這些缺點。

MySQL存儲過程的缺點

若是使用大量存儲過程,那麼使用這些存儲過程的每一個鏈接的內存使用量將會大大增長。 此外,若是您在存儲過程當中過分使用大量邏輯操做,則CPU使用率也會增長,由於數據庫服務器的設計不當於邏輯運算。

存儲過程的構造使得開發具備複雜業務邏輯的存儲過程變得更加困難。

很難調試存儲過程。只有少數數據庫管理系統容許您調試存儲過程。不幸的是,MySQL不提供調試存儲過程的功能。

開發和維護存儲過程並不容易。開發和維護存儲過程一般須要一個不是全部應用程序開發人員擁有的專業技能。這可能會致使應用程序開發和維護階段的問題。

MySQL存儲過程有本身的優勢和缺點。開發應用程序時,您應該決定是否應該或不該該根據業務需求使用存儲過程。

函數

MySQL聚合函數

MySQL字符串函數

MySQL控制流函數

  • case()函數 - 若是知足WHEN分支中的條件,則返回THEN分支中的相應結果,不然返回ELSE分支中的結果。

  • if語句 - 根據給定的條件返回一個值。

  • ifnull()函數 - 若是第一個參數不爲NULL,則返回第一個參數,不然返回第二個參數。

  • nullif()函數 - 若是第一個參數等於第二個參數,則返回NULL,不然返回第一個參數。

MySQL日期和時間函數

MySQL比較函數

其餘MySQL函數

觸發器

SQL觸發器是存儲在數據庫目錄中的一組SQL語句。每當與表相關聯的事件發生時,即會執行或觸發SQL觸發器,例如插入,更新或刪除。

SQL觸發器是一種特殊類型的存儲過程。 這是特別的,由於它不像直接像存儲過程那樣調用。 觸發器和存儲過程之間的主要區別在於,當對錶執行數據修改事件時,會自動調用觸發器,而存儲過程必需要明確地調用。

瞭解SQL觸發器的優缺點很是重要,以便您能夠適當地使用它。在如下部分中,咱們將討論使用SQL觸發器的優缺點。

SQL觸發器的優勢

  • SQL觸發器提供了檢查數據完整性的替代方法。

  • SQL觸發器能夠捕獲數據庫層中業務邏輯中的錯誤。

  • SQL觸發器提供了運行計劃任務的另外一種方法。經過使用SQL觸發器,您沒必要等待運行計劃的任務,由於在對錶中的數據進行更改以前或以後自動調用觸發器。

  • SQL觸發器對於審覈表中數據的更改很是有用。

SQL觸發器的缺點

  • SQL觸發器只能提供擴展驗證,而且沒法替換全部驗證。一些簡單的驗證必須在應用層完成。 例如,您可使用JavaScript或服務器端使用服務器端腳本語言(如JSPPHP,ASP.NET,Perl等)來驗證客戶端的用戶輸入。

  • 從客戶端應用程序調用和執行SQL觸發器不可見,所以很難弄清數據庫層中發生的狀況。

  • SQL觸發器可能會增長數據庫服務器的開銷。

MySQL觸發器簡介

在MySQL中,觸發器是一組SQL語句,當對相關聯的表上的數據進行更改時,會自動調用該語句。 觸發器能夠被定義爲在INSERTUPDATEDELETE語句更改數據以前或以後調用。在MySQL5.7.2版本以前,每一個表最多能夠定義六個觸發器。

  • BEFORE INSERT - 在數據插入表以前被激活觸發器。

  • AFTER INSERT - 在將數據插入表以後激活觸發器。

  • BEFORE UPDATE - 在表中的數據更新以前激活觸發器。

  • AFTER UPDATE - 在表中的數據更新以後激活觸發器。

  • BEFORE DELETE - 在從表中刪除數據以前激活觸發器。

  • AFTER DELETE - 從表中刪除數據以後激活觸發器。

可是,從MySQL 5.7.2+版本開始,能夠爲相同的觸發事件和動做時間定義多個觸發器

當使用不使用INSERTDELETEUPDATE語句更改表中數據的語句時,不會調用與表關聯的觸發器。 例如,TRUNCATE語句刪除表的全部數據,但不調用與該表相關聯的觸發器。

有些語句使用了後臺的INSERT語句,如REPLACE語句LOAD DATA語句。若是使用這些語句,則調用與表關聯的相應觸發器。

必需要爲與表相關聯的每一個觸發器使用惟一的名稱。能夠爲不一樣的表定義相同的觸發器名稱,這是一個很好的作法。

應該使用如下命名約定命名觸發器:

 (BEFORE | AFTER)_tableName_(INSERT| UPDATE | DELETE)
 SQL

例如,before_order_update是更新orders表中的行數據以前調用的觸發器。

如下命名約定與上述同樣。

 tablename_(BEFORE | AFTER)_(INSERT| UPDATE | DELETE)
 SQL

例如,order_before_update與上述before_order_update觸發器相同。

MySQL觸發存儲

MySQL在數據目錄中存儲觸發器,例如:/data/yiibaidb/,並使用名爲tablename.TRGtriggername.TRN的文件:

  • tablename.TRG文件將觸發器映射到相應的表。

  • triggername.TRN文件包含觸發器定義。

能夠經過將觸發器文件複製到備份文件夾來備份MySQL觸發器。也能夠使用mysqldump工具有份觸發器

MySQL觸發限制

MySQL觸發器覆蓋標準SQL中定義的全部功能。 可是,在應用程序中使用它們以前,您應該知道一些限制。

MySQL觸發器不能:

  • 使用在SHOWLOAD DATALOAD TABLEBACKUP DATABASERESTOREFLUSHRETURN語句之上。

  • 使用隱式或明確提交或回滾的語句,如COMMITROLLBACKSTART TRANSACTIONLOCK/UNLOCK TABLESALTERCREATEDROPRENAME等。

  • 使用準備語句,如PREPAREEXECUTE

  • 使用動態SQL語句。

MySQL觸發語法

爲了建立一個新的觸發器,可使用CREATE TRIGGER語句。 下面說明了CREATE TRIGGER語句的語法:

 CREATE TRIGGER trigger_name trigger_time trigger_event
  ON table_name
  FOR EACH ROW
  BEGIN
  ...
  END;
 SQL
  • 將觸發器名稱放在CREATE TRIGGER語句以後。觸發器名稱應遵循命名約定[trigger time]_[table name]_[trigger event],例如before_employees_update。

  • 觸發激活時間能夠在以前或以後。必須指定定義觸發器的激活時間。若是要在更改以前處理操做,則使用BEFORE關鍵字,若是在更改後須要處理操做,則使用AFTER關鍵字。

  • 觸發事件能夠是INSERTUPDATEDELETE。此事件致使觸發器被調用。 觸發器只能由一個事件調用。要定義由多個事件調用的觸發器,必須定義多個觸發器,每一個事件一個觸發器。

  • 觸發器必須與特定表關聯。沒有表觸發器將不存在,因此必須在ON關鍵字以後指定表名。

  • 將SQL語句放在BEGINEND塊之間。這是定義觸發器邏輯的位置。

MySQL觸發器示例

下面咱們將在MySQL中建立觸發器來記錄employees表中行數據的更改狀況。

 mysql> DESC employees;
 +----------------+--------------+------+-----+---------+-------+
 | Field         | Type         | Null | Key | Default | Extra |
 +----------------+--------------+------+-----+---------+-------+
 | employeeNumber | int(11)     | NO   | PRI | NULL   |       |
 | lastName       | varchar(50) | NO   |     | NULL   |       |
 | firstName     | varchar(50) | NO   |     | NULL   |       |
 | extension     | varchar(10) | NO   |     | NULL   |       |
 | email         | varchar(100) | NO   |     | NULL   |       |
 | officeCode     | varchar(10) | NO   | MUL | NULL   |       |
 | reportsTo     | int(11)     | YES | MUL | NULL   |       |
 | jobTitle       | varchar(50) | NO   |     | NULL   |       |
 +----------------+--------------+------+-----+---------+-------+
 8 rows in set
 SQL

首先,建立一個名爲employees audit的新表,用來保存employees表中數據的更改。 如下語句建立employee_audit表。

 USE yiibaidb;
 CREATE TABLE employees_audit (
    id INT AUTO_INCREMENT PRIMARY KEY,
    employeeNumber INT NOT NULL,
    lastname VARCHAR(50) NOT NULL,
    changedat DATETIME DEFAULT NULL,
    action VARCHAR(50) DEFAULT NULL
 );
 SQL

接下來,建立一個BEFORE UPDATE觸發器,該觸發器在對employees表中的行記錄更改以前被調用。

 DELIMITER $$
 CREATE TRIGGER before_employee_update
    BEFORE UPDATE ON employees
    FOR EACH ROW
 BEGIN
     INSERT INTO employees_audit
     SET action = 'update',
      employeeNumber = OLD.employeeNumber,
        lastname = OLD.lastname,
        changedat = NOW();
 END$$
 DELIMITER ;
 SQL

在觸發器的主體中,使用OLD關鍵字來訪問受觸發器影響的行的employeeNumberlastname列。

請注意,在爲INSERT定義的觸發器中,能夠僅使用NEW關鍵字。不能使用OLD關鍵字。可是,在爲DELETE定義的觸發器中,沒有新行,所以您只能使用OLD關鍵字。在UPDATE觸發器中,OLD是指更新前的行,而NEW是更新後的行。

而後,要查看當前數據庫中的全部觸發器,請使用SHOW TRIGGERS語句,以下所示:

 SHOW TRIGGERS;
 SQL

執行上面查詢語句,獲得如下結果 -

 mysql> SHOW TRIGGERS;
 +------------------------+--------+-----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------+------------------------+-----------------------------------------------------------------------------------+----------------+----------------------+----------------------+--------------------+
 | Trigger               | Event | Table     | Statement                                                                                                                                                             | Timing | Created               | sql_mode                                                                         | Definer       | character_set_client | collation_connection | Database Collation |
 +------------------------+--------+-----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------+------------------------+-----------------------------------------------------------------------------------+----------------+----------------------+----------------------+--------------------+
 | before_employee_update | UPDATE | employees | BEGIN
     INSERT INTO employees_audit
     SET action = 'update',
      employeeNumber = OLD.employeeNumber,
        lastname = OLD.lastname,
        changedat = NOW();
 END | BEFORE | 2017-08-02 22:06:36.40 | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION | root@localhost | utf8                 | utf8_general_ci     | utf8_general_ci   |
 +------------------------+--------+-----------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------+------------------------+-----------------------------------------------------------------------------------+----------------+----------------------+----------------------+--------------------+
 1 row in set
 SQL

以後,更新employees表以檢查觸發器是否被調用。

 UPDATE employees 
 SET
    lastName = 'Maxsu'
 WHERE
    employeeNumber = 1056;
 SQL

最後,要檢查觸發器是否被UPDATE語句調用,可使用如下查詢來查詢employees_audit表:

 SELECT * FROM employees_audit;
 SQL

如下是查詢的輸出:

 mysql> SELECT * FROM employees_audit;
 +----+----------------+----------+---------------------+--------+
 | id | employeeNumber | lastname | changedat           | action |
 +----+----------------+----------+---------------------+--------+
 |  1 |           1056 | Hill     | 2017-08-02 22:15:51 | update |
 +----+----------------+----------+---------------------+--------+
 1 row in set

MySQL將按照建立的順序調用觸發器。要更改觸發器的順序,須要在FOR EACH ROW子句以後指定FOLLOWSPRECEDES。以下說明 -

  • FOLLOWS選項容許新觸發器在現有觸發器以後激活。

  • PRECEDES選項容許新觸發器在現有觸發器以前激活。

如下是使用顯式順序建立新的附加觸發器的語法:

 DELIMITER $$
 CREATE TRIGGER trigger_name
 [BEFORE|AFTER] [INSERT|UPDATE|DELETE] ON table_name
 FOR EACH ROW [FOLLOWS|PRECEDES] existing_trigger_name
 BEGIN
 
 END$$
 DELIMITER ;
 SQL

MySQL多重觸發器示例

咱們來看如何一個在表中的同一個事件和動做上,建立多個觸發器的例子。

下面將使用示例數據庫(yiibaidb)中的products表進行演示。假設,每當更改產品的價格(MSRP列)時,要將舊的價格記錄在一個名爲price_logs的表中。

首先,使用CREATE TABLE語句建立一個新的price_logs表,以下所示:

 USE yiibaidb;
 CREATE TABLE price_logs (
   id INT(11) NOT NULL AUTO_INCREMENT,
   product_code VARCHAR(15) NOT NULL,
   price DOUBLE NOT NULL,
   updated_at TIMESTAMP NOT NULL DEFAULT 
              CURRENT_TIMESTAMP 
              ON UPDATE CURRENT_TIMESTAMP,
 ​
   PRIMARY KEY (id),
 ​
   KEY product_code (product_code),
 ​
   CONSTRAINT price_logs_ibfk_1 FOREIGN KEY (product_code) 
   REFERENCES products (productCode) 
   ON DELETE CASCADE 
   ON UPDATE CASCADE
 );
 SQL

其次,當表的BEFORE UPDATE事件發生時,建立一個新的觸發器。觸發器名稱爲before_products_update,具體實現以下所示:

 DELIMITER $$
 CREATE TRIGGER before_products_update 
    BEFORE UPDATE ON products 
    FOR EACH ROW 
 BEGIN
      INSERT INTO price_logs(product_code,price)
      VALUES(old.productCode,old.msrp);
 END$$
 DELIMITER ;
 SQL

第三,咱們更改產品的價格,並使用如下UPDATE語句,最後查詢price_logs表:

 UPDATE products
 SET msrp = 95.1
 WHERE productCode = 'S10_1678';
 -- 查詢結果價格記錄
 SELECT * FROM price_logs;
 SQL

上面查詢語句執行後,獲得如下結果 -

 +----+--------------+-------+---------------------+
 | id | product_code | price | updated_at         |
 +----+--------------+-------+---------------------+
 |  1 | S10_1678     |  95.7 | 2017-08-03 02:46:42 |
 +----+--------------+-------+---------------------+
 1 row in set
 SQL

能夠看到結果中,它按咱們預期那樣工做了。

假設不只要看到舊的價格,改變的時候,還要記錄是誰修改了它。 咱們能夠向price_logs表添加其餘列。 可是,爲了實現多個觸發器的演示,咱們將建立一個新表來存儲進行更改的用戶的數據。這個新表的名稱爲user_change_logs,結構以下:

 USE yiibaidb;
 CREATE TABLE user_change_logs (
   id int(11) NOT NULL AUTO_INCREMENT,
   product_code varchar(15) DEFAULT NULL,
   updated_at timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP 
   ON UPDATE CURRENT_TIMESTAMP,
   updated_by varchar(30) NOT NULL,
   PRIMARY KEY (id),
   KEY product_code (product_code),
   CONSTRAINT user_change_logs_ibfk_1 FOREIGN KEY (product_code) 
   REFERENCES products (productCode) 
   ON DELETE CASCADE ON UPDATE CASCADE
 );
 SQL

如今,咱們建立一個在products表上的BEFORE UPDATE事件上激活的第二個觸發器。 此觸發器將更改的用戶信息更新到user_change_logs表。 它在before_products_update觸發後被激活。

 DELIMITER $$
 CREATE TRIGGER before_products_update_2 
    BEFORE UPDATE ON products 
    FOR EACH ROW FOLLOWS before_products_update
 BEGIN
    INSERT INTO user_change_logs(product_code,updated_by)
    VALUES(old.productCode,user());
 END$$
 DELIMITER ;
 SQL

下面咱們來作一個快速測試。

首先,使用UPDATE語句更新指定產品的價格,以下:

 UPDATE products
 SET msrp = 95.3
 WHERE productCode = 'S10_1678';
 SQL

其次,分別從price_logsuser_change_logs表查詢數據:

 SELECT * FROM price_logs;
 SQL

上面查詢語句執行後,獲得如下結果 -

 mysql> SELECT * FROM price_logs;
 +----+--------------+-------+---------------------+
 | id | product_code | price | updated_at         |
 +----+--------------+-------+---------------------+
 |  1 | S10_1678     |  95.7 | 2017-08-03 02:46:42 |
 |  2 | S10_1678     |  95.1 | 2017-08-03 02:47:21 |
 +----+--------------+-------+---------------------+
 2 rows in set
 SQL
 SELECT * FROM user_change_logs;
 SQL

上面查詢語句執行後,獲得如下結果 -

 mysql> SELECT * FROM user_change_logs;
 +----+--------------+---------------------+----------------+
 | id | product_code | updated_at         | updated_by     |
 +----+--------------+---------------------+----------------+
 |  1 | S10_1678     | 2017-08-03 02:47:21 | root@localhost |
 +----+--------------+---------------------+----------------+
 1 row in set
 SQL

如上所見,兩個觸發器按照預期的順序激活執行相關操做了。

觸發器順序

若是使用SHOW TRIGGERS語句,則不會在表中看到觸發激活同一事件和操做的順序。

 SHOW TRIGGERS FROM yiibaidb;
 SQL

要查找此信息,須要以下查詢information_schema數據庫的triggers表中的action_order列,以下查詢語句 -

 SELECT 
     trigger_name, action_order
 FROM
     information_schema.triggers
 WHERE
     trigger_schema = 'yiibaidb'
 ORDER BY event_object_table , 
          action_timing , 
          event_manipulation;
 SQL

上面查詢語句執行後,獲得如下結果 -

 mysql> SELECT 
     trigger_name, action_order
 FROM
     information_schema.triggers
 WHERE
     trigger_schema = 'yiibaidb'
 ORDER BY event_object_table , 
          action_timing , 
          event_manipulation;
 +--------------------------+--------------+
 | trigger_name             | action_order |
 +--------------------------+--------------+
 | before_employee_update   |            1 |
 | before_products_update   |            1 |
 | before_products_update_2 |            2 |
 +--------------------------+--------------+
 3 rows in set

觸發器做爲純文本文件存儲在如下數據庫文件夾中:

 /data_folder/database_name/table_name.trg
 SQL

也可經過查詢information_schema數據庫中的triggers表來顯示觸發器,以下所示:

 SELECT 
    *
 FROM
    information_schema.triggers
 WHERE
    trigger_schema = 'database_name'
         AND trigger_name = 'trigger_name';
 SQL

該語句容許您查看觸發器的內容及其元數據,例如:關聯表名和定義器,這是建立觸發器的MySQL用戶的名稱。

若是要檢索指定數據庫中的全部觸發器,則須要使用如下SELECT語句information_schema數據庫中的triggers表查詢數據:

 SELECT 
     *
 FROM
     information_schema.triggers
 WHERE
     trigger_schema = 'database_name';
 SQL
 

要查找與特定表相關聯的全部觸發器,請使用如下查詢:

 SELECT 
    *
 FROM
    information_schema.triggers
 WHERE
    trigger_schema = 'database_name'
         AND event_object_table = 'table_name';
 SQL

例如,如下查詢語句與yiibaidb數據庫中的employees表相關聯的全部觸發器。

 SELECT * FROM information_schema.triggers
 WHERE trigger_schema = 'yiibaidb'
         AND event_object_table = 'employees';
 SQL

執行上面查詢,獲得如下結果 -

 mysql> SELECT * FROM information_schema.triggers
 WHERE trigger_schema = 'yiibaidb'
         AND event_object_table = 'employees';
 +-----------------+----------------+------------------------+--------------------+----------------------+---------------------+--------------------+--------------+------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+---------------+----------------------------+----------------------------+--------------------------+--------------------------+------------------------+-----------------------------------------------------------------------------------+----------------+----------------------+----------------------+--------------------+
 | TRIGGER_CATALOG | TRIGGER_SCHEMA | TRIGGER_NAME           | EVENT_MANIPULATION | EVENT_OBJECT_CATALOG | EVENT_OBJECT_SCHEMA | EVENT_OBJECT_TABLE | ACTION_ORDER | ACTION_CONDITION | ACTION_STATEMENT                                                                                                                                                      | ACTION_ORIENTATION | ACTION_TIMING | ACTION_REFERENCE_OLD_TABLE | ACTION_REFERENCE_NEW_TABLE | ACTION_REFERENCE_OLD_ROW | ACTION_REFERENCE_NEW_ROW | CREATED                | SQL_MODE                                                                          | DEFINER        | CHARACTER_SET_CLIENT | COLLATION_CONNECTION | DATABASE_COLLATION |
 +-----------------+----------------+------------------------+--------------------+----------------------+---------------------+--------------------+--------------+------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+---------------+----------------------------+----------------------------+--------------------------+--------------------------+------------------------+-----------------------------------------------------------------------------------+----------------+----------------------+----------------------+--------------------+
 | def             | yiibaidb       | before_employee_update | UPDATE             | def                  | yiibaidb            | employees          |            1 | NULL             | BEGIN
     INSERT INTO employees_audit
     SET action = 'update',
      employeeNumber = OLD.employeeNumber,
         lastname = OLD.lastname,
         changedat = NOW();
 END | ROW                | BEFORE        | NULL                       | NULL                       | OLD                      | NEW                      | 2017-08-02 22:06:36.40 | ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION | root@localhost | utf8                 | utf8_general_ci      | utf8_general_ci    |
 +-----------------+----------------+------------------------+--------------------+----------------------+---------------------+--------------------+--------------+------------------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------+---------------+----------------------------+----------------------------+--------------------------+--------------------------+------------------------+-----------------------------------------------------------------------------------+----------------+----------------------+----------------------+--------------------+
 1 row in set
 SQL

 

 

MySQL SHOW TRIGGERS語句

在特定數據庫中顯示觸發器的另外一種方法是使用SHOW TRIGGERS語句,以下所示:

 SHOW TRIGGERS [FROM|IN] database_name
 [LIKE expr | WHERE expr];
 SQL

例如,若是要查看當前數據庫中的全部觸發器,可使用SHOW TRIGGERS語句,以下所示:

 SHOW TRIGGERS;
 SQL

要獲取特定數據庫中的全部觸發器,請在SHOW TRIGGERS語句中指定數據庫名稱,好比要查詢數據庫:yiibaidb下的全部觸發器,以下所示:

 SHOW TRIGGERS FROM yiibaidb;
 SQL

上面語句返回yiibaidb數據庫中的全部觸發器。

要獲取與特定表相關聯的全部觸發器,可使用SHOW TRIGGERS語句中的WHERE子句。 如下語句返回與employees表相關聯的全部觸發器:

 SHOW TRIGGERS FROM yiibaidb
 WHERE `table` = 'employees';
 SQL

請注意,咱們使用反引號包裝table列,由於table是MySQL中的保留關鍵字。

當執行SHOW TRIGGERS語句時,MySQL返回如下列 -

  • Trigger:存儲觸發器的名稱,例如before_employee_update觸發器。

  • Event:指定事件,例如,調用觸發器的INSERTUPDATEDELETE

  • Table:指定觸發器與例如相關聯的表,如employees表。

  • Statement:存儲調用觸發器時要執行的語句或複合語句。

  • Timing:接受兩個值:BEFOREAFTER,它指定觸發器的激活時間。

  • Created:在建立觸發器時記錄建立的時間。

  • sql_mode:指定觸發器執行時的SQL模式。

  • Definer:記錄建立觸發器的賬戶。

請注意,要執行SHOW TRIGGERS語句,您必須具備SUPER權限。

刪除觸發器

要刪除現有的觸發器,請使用DROP TRIGGER語句,以下所示:

 DROP TRIGGER table_name.trigger_name;
 SQL

例如,若是要刪除與employees表相關聯的before_employees_update觸發器,則能夠執行如下語句:

 DROP TRIGGER employees.before_employees_update;
 SQL

要修改觸發器,必須首先刪除它並使用新的代碼從新建立。在MySQL中沒有相似:ALTER TRIGGER語句,所以,您不能像修改其餘數據庫對象,如視圖存儲過程那樣修改觸發器。

事件

MySQL事件調度器配置

MySQL使用一個名爲事件調度線程的特殊線程來執行全部調度的事件。能夠經過執行如下命令來查看事件調度程序線程的狀態:

 SHOW PROCESSLIST;
 SQL

執行上面查詢語句,獲得如下結果 -

 mysql> SHOW PROCESSLIST;
 +----+------+-----------------+----------+---------+------+----------+------------------+
 | Id | User | Host            | db       | Command | Time | State    | Info             |
 +----+------+-----------------+----------+---------+------+----------+------------------+
 |  2 | root | localhost:50405 | NULL     | Sleep   | 1966 |          | NULL             |
 |  3 | root | localhost:50406 | yiibaidb | Sleep   | 1964 |          | NULL             |
 |  4 | root | localhost:50407 | yiibaidb | Query   |    0 | starting | SHOW PROCESSLIST |
 +----+------+-----------------+----------+---------+------+----------+------------------+
 3 rows in set
 Shell

默認狀況下,事件調度程序線程未啓用。 要啓用和啓動事件調度程序線程,須要執行如下命令:

 SET GLOBAL event_scheduler = ON;
 SQL

如今看到事件調度器線程的狀態,再次執行SHOW PROCESSLIST命令,結果以下所示 -

 mysql> SHOW PROCESSLIST;
 +----+-----------------+-----------------+----------+---------+------+------------------------+------------------+
 | Id | User            | Host            | db       | Command | Time | State                  | Info             |
 +----+-----------------+-----------------+----------+---------+------+------------------------+------------------+
 |  2 | root            | localhost:50405 | NULL     | Sleep   | 1986 |                        | NULL             |
 |  3 | root            | localhost:50406 | yiibaidb | Sleep   | 1984 |                        | NULL             |
 |  4 | root            | localhost:50407 | yiibaidb | Query   |    0 | starting               | SHOW PROCESSLIST |
 |  5 | event_scheduler | localhost       | NULL     | Daemon  |    6 | Waiting on empty queue | NULL             |
 +----+-----------------+-----------------+----------+---------+------+------------------------+------------------+
 4 rows in set
 SQL

要禁用並中止事件調度程序線程,可經過執行SET GLOBAL命令將event_scheduler其值設置爲OFF

 SET GLOBAL event_scheduler = OFF;
 SQL

建立新的MySQL事件

建立事件與建立其餘數據庫對象(如存儲過程或觸發器)相似。事件是一個包含SQL語句的命名對象。

存儲過程僅在直接調用時執行; 觸發器則與一個表相關聯的事件(例如插入更新刪除)事件發生時,能夠在一次或更多的規則間隔執行事件時執行觸發。

要建立和計劃新事件,請使用CREATE EVENT語句,以下所示:

 CREATE EVENT [IF NOT EXIST] event_name
 ON SCHEDULE schedule
 DO
 event_body
 SQL

下面讓咱們更詳細地解釋語法中的一些參數 -

  • 首先,在CREATE EVENT子句以後指定事件名稱。事件名稱在數據庫模式中必須是惟一的。

  • 其次,在ON SCHEDULE子句後面加上一個表。若是事件是一次性事件,則使用語法:AT timestamp [+ INTERVAL],若是事件是循環事件,則使用EVERY子句:EVERY interval STARTS timestamp [+INTERVAL] ENDS timestamp [+INTERVAL]

  • 第三,將DO語句放在DO關鍵字以後。請注意,能夠在事件主體內調用存儲過程。 若是您有複合SQL語句,能夠將它們放在BEGIN END塊中。

咱們來看幾個建立事件的例子來了解上面的語法。

首先,建立並計劃將一個消息插入到messages表中的一次性事件,請執行如下步驟:

 USE testdb;
 CREATE TABLE IF NOT EXISTS messages (
     id INT PRIMARY KEY AUTO_INCREMENT,
     message VARCHAR(255) NOT NULL,
     created_at DATETIME NOT NULL
 );
 SQL

其次,使用CREATE EVENT語句建立一個事件:

 CREATE EVENT IF NOT EXISTS test_event_01
 ON SCHEDULE AT CURRENT_TIMESTAMP
 DO
   INSERT INTO messages(message,created_at)
   VALUES('Test MySQL Event 1',NOW());
 SQL

第三,檢查messages表; 會看到有1條記錄。這意味着事件在建立時被執行。

 SELECT * FROM messages;
 SQL

執行上面查詢語句,獲得如下結果 -

 mysql> SELECT * FROM messages;
 +----+--------------------+---------------------+
 | id | message            | created_at          |
 +----+--------------------+---------------------+
 |  1 | Test MySQL Event 1 | 2017-08-03 04:23:11 |
 +----+--------------------+---------------------+
 1 row in set
 Shell

要顯示數據庫(testdb)的全部事件,請使用如下語句:

 SHOW EVENTS FROM testdb;
 SQL

執行上面查詢看不到任何行返回,由於事件在到期時自動刪除。 在咱們的示例中,它是一次性的事件,在執行完成時就過時了。

要更改此行爲,可使用ON COMPLETION PRESERVE子句。如下語句建立另外一個一次性事件,在其建立時間1分鐘後執行,執行後不會被刪除。

 CREATE EVENT test_event_02
 ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 MINUTE
 ON COMPLETION PRESERVE
 DO
    INSERT INTO messages(message,created_at)
    VALUES('Test MySQL Event 2',NOW());
 SQL

等待1分鐘後,查看messages表,添加了另外一條記錄:

 SELECT * FROM messages;
 SQL

執行上面查詢語句,獲得如下結果 -

 mysql> SELECT * FROM messages;
 +----+--------------------+---------------------+
 | id | message            | created_at          |
 +----+--------------------+---------------------+
 |  1 | Test MySQL Event 1 | 2017-08-03 04:23:11 |
 |  2 | Test MySQL Event 2 | 2017-08-03 04:24:48 |
 +----+--------------------+---------------------+
 2 rows in set
 Shell

若是再次執行SHOW EVENTS語句,看到事件是因爲ON COMPLETION PRESERVE子句的影響:

 SHOW EVENTS FROM testdb;
 SQL

執行上面查詢語句,獲得如下結果 -

 mysql> SHOW EVENTS FROM testdb;
 +--------+---------------+----------------+-----------+----------+---------------------+----------------+----------------+--------+------+----------+------------+----------------------+----------------------+--------------------+
 | Db     | Name          | Definer        | Time zone | Type     | Execute at          | Interval value | Interval field | Starts | Ends | Status   | Originator | character_set_client | collation_connection | Database Collation |
 +--------+---------------+----------------+-----------+----------+---------------------+----------------+----------------+--------+------+----------+------------+----------------------+----------------------+--------------------+
 | testdb | test_event_02 | root@localhost | SYSTEM    | ONE TIME | 2017-08-03 04:24:48 | NULL           | NULL           | NULL   | NULL | DISABLED |          0 | utf8                 | utf8_general_ci      | utf8_general_ci    |
 +--------+---------------+----------------+-----------+----------+---------------------+----------------+----------------+--------+------+----------+------------+----------------------+----------------------+--------------------+
 1 row in set
 Shell

如下語句建立一個循環的事件,每分鐘執行一次,並在其建立時間的1小時內過時:

 CREATE EVENT test_event_03
 ON SCHEDULE EVERY 1 MINUTE
 STARTS CURRENT_TIMESTAMP
 ENDS CURRENT_TIMESTAMP + INTERVAL 1 HOUR
 DO
    INSERT INTO messages(message,created_at)
    VALUES('Test MySQL recurring Event',NOW());
 SQL

請注意,使用STARTSENDS子句定義事件的有效期。等待個3,5分鐘後再查看messages表數據,以測試驗證此循環事件的執行。

 SELECT * FROM messages;
 SQL

執行上面查詢語句,獲得如下結果 -

 mysql> SELECT * FROM messages;
 +----+----------------------------+---------------------+
 | id | message                    | created_at          |
 +----+----------------------------+---------------------+
 |  1 | Test MySQL Event 1         | 2017-08-03 04:23:11 |
 |  2 | Test MySQL Event 2         | 2017-08-03 04:24:48 |
 |  3 | Test MySQL recurring Event | 2017-08-03 04:25:20 |
 |  4 | Test MySQL recurring Event | 2017-08-03 04:26:20 |
 |  5 | Test MySQL recurring Event | 2017-08-03 04:27:20 |
 +----+----------------------------+---------------------+
 5 rows in set
 Shell

刪除MySQL事件

要刪除現有事件,請使用DROP EVENT語句,以下所示:

 DROP EVENT [IF EXISTS] event_name;
 SQL

例如,要刪除test_event_03的事件,請使用如下語句:

 DROP EVENT IF EXISTS test_event_03;

MySQL容許您更改現有事件的各類屬性。要更改現有事件,請使用ALTER EVENT語句,以下所示:

 ALTER EVENT event_name
 ON SCHEDULE schedule
 ON COMPLETION [NOT] PRESERVE
 RENAME TO new_event_name
 ENABLE | DISABLE
 DO
  event_body
 SQL

請注意,ALTER EVENT語句僅適用於存在的事件。若是您嘗試修改不存在的事件,MySQL將會發出一條錯誤消息,所以在更改事件以前,應先使用SHOW EVENTS語句檢查事件的存在。

 SHOW EVENTS FROM testdb;
 SQL

執行上面查詢,獲得如下結果 -

 mysql> SHOW EVENTS FROM testdb;
 +--------+---------------+----------------+-----------+----------+---------------------+----------------+----------------+--------+------+----------+------------+----------------------+----------------------+--------------------+
 | Db     | Name         | Definer       | Time zone | Type     | Execute at         | Interval value | Interval field | Starts | Ends | Status   | Originator | character_set_client | collation_connection | Database Collation |
 +--------+---------------+----------------+-----------+----------+---------------------+----------------+----------------+--------+------+----------+------------+----------------------+----------------------+--------------------+
 | testdb | test_event_02 | root@localhost | SYSTEM   | ONE TIME | 2017-08-03 04:24:48 | NULL           | NULL           | NULL   | NULL | DISABLED |          0 | utf8                 | utf8_general_ci     | utf8_general_ci   |
 +--------+---------------+----------------+-----------+----------+---------------------+----------------+----------------+--------+------+----------+------------+----------------------+----------------------+--------------------+
 1 row in set
 Shell

ALTER EVENT示例

咱們建立一個示例事件來演示如何使用ALTER EVENT語句的各類功能。

如下語句建立一個事件,每分鐘將一條新記錄插入到messages表中。

 USE testdb;
 CREATE EVENT test_event_04
 ON SCHEDULE EVERY 1 MINUTE
 DO
    INSERT INTO messages(message,created_at)
    VALUES('Test ALTER EVENT statement',NOW());
 SQL

改變調度時間

要修改事件爲每2分鐘運行一次,請使用如下語句:

 ALTER EVENT test_event_04
 ON SCHEDULE EVERY 2 MINUTE;
 SQL

改變事件的主體代碼邏輯

您還能夠經過指定新的邏輯來更改事件的主體代碼,以下所示:

 ALTER EVENT test_event_04
 DO
    INSERT INTO messages(message,created_at)
    VALUES('Message from event',NOW());
 -- 清空表中的數據
 truncate messages;
 SQL

上面修改完成後,能夠等待2分鐘,再次查看messages表:

 SELECT * FROM messages;
 SQL

執行上面查詢,獲得如下結果 -

 mysql> SELECT * FROM messages;
 +----+--------------------+---------------------+
 | id | message           | created_at         |
 +----+--------------------+---------------------+
 |  1 | Message from event | 2017-08-03 04:46:47 |
 |  2 | Message from event | 2017-08-03 04:48:47 |
 +----+--------------------+---------------------+
 2 rows in set
 Shell

禁用事件

要禁用某個事件,請在ALTER EVENT語句以後使用DISABLE關鍵字,請使用如下語句:

 ALTER EVENT test_event_04
 DISABLE;
 SQL

也能夠經過使用SHOW EVENTS語句來查看事件的狀態,以下所示:

 SHOW EVENTS FROM testdb;
 SQL

執行上面查詢,獲得如下結果 -

 mysql> SHOW EVENTS FROM testdb;
 +--------+---------------+----------------+-----------+-----------+---------------------+----------------+----------------+---------------------+------+----------+------------+----------------------+----------------------+--------------------+
 | Db     | Name         | Definer       | Time zone | Type     | Execute at         | Interval value | Interval field | Starts             | Ends | Status   | Originator | character_set_client | collation_connection | Database Collation |
 +--------+---------------+----------------+-----------+-----------+---------------------+----------------+----------------+---------------------+------+----------+------------+----------------------+----------------------+--------------------+
 | testdb | test_event_02 | root@localhost | SYSTEM   | ONE TIME | 2017-08-03 04:24:48 | NULL           | NULL           | NULL               | NULL | DISABLED |          0 | utf8                 | utf8_general_ci     | utf8_general_ci   |
 | testdb | test_event_04 | root@localhost | SYSTEM   | RECURRING | NULL               | 2             | MINUTE         | 2017-08-03 04:44:47 | NULL | DISABLED |          0 | utf8                 | utf8_general_ci     | utf8_general_ci   |
 +--------+---------------+----------------+-----------+-----------+---------------------+----------------+----------------+---------------------+------+----------+------------+----------------------+----------------------+--------------------+
 2 rows in set
 Shell

啓用事件

要啓用已禁用的事件,請在ALTER EVENT語句以後使用ENABLE關鍵字,以下所示:

 ALTER EVENT test_event_04
 ENABLE;
 SQL

查詢上面語句執行結果,獲得如下結果 -

 mysql> SHOW EVENTS FROM testdb;
 +--------+---------------+----------------+-----------+-----------+---------------------+----------------+----------------+---------------------+------+----------+------------+----------------------+----------------------+--------------------+
 | Db     | Name         | Definer       | Time zone | Type     | Execute at         | Interval value | Interval field | Starts             | Ends | Status   | Originator | character_set_client | collation_connection | Database Collation |
 +--------+---------------+----------------+-----------+-----------+---------------------+----------------+----------------+---------------------+------+----------+------------+----------------------+----------------------+--------------------+
 | testdb | test_event_02 | root@localhost | SYSTEM   | ONE TIME | 2017-08-03 04:24:48 | NULL           | NULL           | NULL               | NULL | DISABLED |          0 | utf8                 | utf8_general_ci     | utf8_general_ci   |
 | testdb | test_event_04 | root@localhost | SYSTEM   | RECURRING | NULL               | 2             | MINUTE         | 2017-08-03 04:44:47 | NULL | ENABLED |          0 | utf8                 | utf8_general_ci     | utf8_general_ci   |
 +--------+---------------+----------------+-----------+-----------+---------------------+----------------+----------------+---------------------+------+----------+------------+----------------------+----------------------+--------------------+
 2 rows in set
 Shell

重命名事件

MySQL不提供相似RENAME EVENT語句。幸運的是,咱們可使用ALTER EVENT重命名現有事件,以下所示:

 ALTER EVENT test_event_04
 RENAME TO test_event_05;
 SQL

查詢上面語句執行結果,獲得如下結果 -

 mysql> SHOW EVENTS FROM testdb;
 +--------+---------------+----------------+-----------+-----------+---------------------+----------------+----------------+---------------------+------+----------+------------+----------------------+----------------------+--------------------+
 | Db     | Name         | Definer       | Time zone | Type     | Execute at         | Interval value | Interval field | Starts             | Ends | Status   | Originator | character_set_client | collation_connection | Database Collation |
 +--------+---------------+----------------+-----------+-----------+---------------------+----------------+----------------+---------------------+------+----------+------------+----------------------+----------------------+--------------------+
 | testdb | test_event_02 | root@localhost | SYSTEM   | ONE TIME | 2017-08-03 04:24:48 | NULL           | NULL           | NULL               | NULL | DISABLED |          0 | utf8                 | utf8_general_ci     | utf8_general_ci   |
 | testdb | test_event_05 | root@localhost | SYSTEM   | RECURRING | NULL               | 2             | MINUTE         | 2017-08-03 04:44:47 | NULL | ENABLED |          0 | utf8                 | utf8_general_ci     | utf8_general_ci   |
 +--------+---------------+----------------+-----------+-----------+---------------------+----------------+----------------+---------------------+------+----------+------------+----------------------+----------------------+--------------------+
 2 rows in set
 Shell

將事件移動到其餘數據庫

能夠經過使用RENAME TO子句將事件從一個數據庫移動到另外一個數據庫中,以下所示:

 ALTER EVENT testdb.test_event_05
 RENAME TO newdb.test_event_05;
 SQL

查詢上面語句執行結果,獲得如下結果 -

 mysql> SHOW EVENTS FROM newdb;
 +-------+---------------+----------------+-----------+-----------+------------+----------------+----------------+---------------------+------+---------+------------+----------------------+----------------------+--------------------+
 | Db   | Name         | Definer       | Time zone | Type     | Execute at | Interval value | Interval field | Starts             | Ends | Status | Originator | character_set_client | collation_connection | Database Collation |
 +-------+---------------+----------------+-----------+-----------+------------+----------------+----------------+---------------------+------+---------+------------+----------------------+----------------------+--------------------+
 | newdb | test_event_05 | root@localhost | SYSTEM   | RECURRING | NULL       | 2             | MINUTE         | 2017-08-03 04:44:47 | NULL | ENABLED |          0 | utf8                 | utf8_general_ci     | utf8_general_ci   |
 +-------+---------------+----------------+-----------+-----------+------------+----------------+----------------+---------------------+------+---------+------------+----------------------+----------------------+--------------------+
 1 row in set
 SQL

假設newdb數據庫在MySQL數據庫服務器中可用。

在本教程中,咱們向您展現瞭如何使用ALTER EVENT語句更改MySQL事件的各類屬性。

視圖

數據庫視圖是虛擬表或邏輯表,它被定義爲具備鏈接的SQL SELECT查詢語句。 由於數據庫視圖與數據庫表相似,它由行和列組成,所以能夠根據數據庫表查詢數據。 大多數數據庫管理系統(包括MySQL)容許您經過具備一些先決條件的數據庫視圖來更新基礎表中的數據。

 

 

 

 

數據庫視圖是動態的,由於它與物理模式無關。數據庫系統將數據庫視圖存儲爲具備鏈接的SQL SELECT語句。當表的數據發生變化時,視圖也反映了這些數據的變化。

數據庫視圖的優勢

如下是使用數據庫視圖的優勢 -

  • 數據庫視圖容許簡化複雜查詢:數據庫視圖由與許多基礎表相關聯的SQL語句定義。 您可使用數據庫視圖來隱藏最終用戶和外部應用程序的基礎表的複雜性。 經過數據庫視圖,您只需使用簡單的SQL語句,而不是使用具備多個鏈接的複雜的SQL語句。

  • 數據庫視圖有助於限制對特定用戶的數據訪問。 您可能不但願全部用戶均可以查詢敏感數據的子集。可使用數據庫視圖將非敏感數據僅顯示給特定用戶組。

  • 數據庫視圖提供額外的安全層。 安全是任何關係數據庫管理系統的重要組成部分。 數據庫視圖爲數據庫管理系統提供了額外的安全性。 數據庫視圖容許您建立只讀視圖,以將只讀數據公開給特定用戶。 用戶只能以只讀視圖檢索數據,但沒法更新。

  • 數據庫視圖啓用計算列。 數據庫表不該該具備計算列,但數據庫視圖能夠這樣。 假設在orderDetails表中有quantityOrder(產品的數量)和priceEach(產品的價格)列。 可是,orderDetails表沒有一個列用來存儲訂單的每一個訂單項的總銷售額。若是有,數據庫模式不是一個好的設計。 在這種狀況下,您能夠建立一個名爲total的計算列,該列是quantityOrderpriceEach的乘積,以表示計算結果。當您從數據庫視圖中查詢數據時,計算列的數據將隨機計算產生。

  • 數據庫視圖實現向後兼容。 假設你有一箇中央數據庫,許多應用程序正在使用它。 有一天,您決定從新設計數據庫以適應新的業務需求。刪除一些表並建立新的表,而且不但願更改影響其餘應用程序。在這種狀況下,能夠建立與將要刪除的舊錶相同的模式的數據庫視圖。

數據庫視圖的缺點

除了上面的優勢,使用數據庫視圖有幾個缺點:

  • 性能:從數據庫視圖查詢數據可能會很慢,特別是若是視圖是基於其餘視圖建立的。

  • 表依賴關係:將根據數據庫的基礎表建立一個視圖。每當更改與其相關聯的表的結構時,都必須更改視圖。

在MySQL中,視圖的幾乎特徵符合SQL:2003標準。 MySQL以兩種方式處理對視圖的查詢:

  • 第一種方式,MySQL會根據視圖定義語句建立一個臨時表,並在此臨時表上執行傳入查詢。

  • 第二種方式,MySQL將傳入查詢與查詢定義爲一個查詢並執行組合查詢。

MySQL支持版本系統的視圖。每次視圖被更改或替換時,視圖的副本將在駐留在特定數據庫文件夾的arc(archive)文件夾中備份。備份文件的名稱爲view_name.frm-00001。若是再次更改視圖,MySQL將建立一個名爲view_name.frm-00002的新備份文件。

MySQL容許基於其餘視圖建立視圖。在視圖定義的SELECT語句中,能夠引用另外一個視圖。

MySQL視圖的限制

不能在視圖上建立索引。當使用合併算法的視圖查詢數據時,MySQL會使用底層表的索引。對於使用誘惑算法的視圖,當您針對視圖查詢數據時,不會使用索引。

MySQL 5.7.7以前版本,是不能在SELECT語句的FROM子句中使用子查詢來定義視圖的。

若是刪除或重命名視圖所基於的表,則MySQL不會發出任何錯誤。可是,MySQL會使視圖無效。 可使用CHECK TABLE語句來檢查視圖是否有效。

一個簡單的視圖能夠更新表中數據。基於具備鏈接,子查詢等的複雜SELECT語句建立的視圖沒法更新。

MySQL不像:OraclePostgreSQL等其餘數據庫系統那樣支持物理視圖,MySQL是不支持物理視圖的。

CREATE VIEW語句簡介

要在MySQL中建立一個新視圖,可使用CREATE VIEW語句。 在MySQL中建立視圖的語法以下:

 CREATE 
    [ALGORITHM = {MERGE | TEMPTABLE | UNDEFINED}]
 VIEW [database_name].[view_name]
 AS
 [SELECT statement]
 SQL

下面咱們來詳細的查看上面的語法。

查看處理算法

算法屬性容許您控制MySQL在建立視圖時使用的機制,MySQL提供了三種算法:MERGETEMPTABLEUNDEFINED

  • 使用MERGE算法,MySQL首先將輸入查詢與定義視圖的SELECT語句組合成單個查詢。 而後MySQL執行組合查詢返回結果集。 若是SELECT語句包含集合函數(如MINMAXSUMCOUNTAVG等)或DISTINCTGROUP BYHAVINGLIMITUNIONUNION ALL子查詢,則不容許使用MERGE算法。 若是SELECT語句無引用表,則也不容許使用MERGE算法。 若是不容許MERGE算法,MySQL將算法更改成UNDEFINED。請注意,將視圖定義中的輸入查詢和查詢組合成一個查詢稱爲視圖分辨率

  • 使用TEMPTABLE算法,MySQL首先根據定義視圖的SELECT語句建立一個臨時表,而後針對該臨時表執行輸入查詢。由於MySQL必須建立臨時表來存儲結果集並將數據從基表移動到臨時表,因此TEMPTABLE算法的效率比MERGE算法效率低。 另外,使用TEMPTABLE算法的視圖是不可更新的。

  • 當您建立視圖而不指定顯式算法時,UNDEFINED是默認算法。 UNDEFINED算法使MySQL能夠選擇使用MERGETEMPTABLE算法。MySQL優先使用MERGE算法進行TEMPTABLE算法,由於MERGE算法效率更高。

查看名稱

在數據庫中,視圖和表共享相同的命名空間,所以視圖和表不能具備相同的名稱。 另外,視圖的名稱必須遵循表的命名規則。

SELECT語句

SELECT語句中,能夠從數據庫中存在的任何表或視圖查詢數據。SELECT語句必須遵循如下幾個規則:

  • SELECT語句能夠在WHERE子句中包含子查詢,但FROM子句中的不能包含子查詢。

  • SELECT語句不能引用任何變量,包括局部變量,用戶變量和會話變量。

  • SELECT語句不能引用準備語句的參數。

請注意,SELECT語句不須要引用任何表。

建立MySQL視圖示例

建立簡單的視圖

咱們來看看orderDetails表。基於orderDetails表來建立一個表示每一個訂單的總銷售額的視圖。

 CREATE VIEW SalePerOrder AS
     SELECT
        orderNumber, SUM(quantityOrdered * priceEach) total
     FROM
        orderDetails
     GROUP by orderNumber
     ORDER BY total DESC;
 SQL

若是使用SHOW TABLES命令來查看示例數據庫(yiibaidb)中的全部表,咱們還會看到SalesPerOrder視圖也顯示在表的列表中。以下所示 -

 mysql> SHOW TABLES;
 +--------------------+
 | Tables_in_yiibaidb |
 +--------------------+
 | article_tags       |
 | contacts           |
 | customers         |
 | departments       |
 | employees         |
 | offices           |
 | offices_bk         |
 | offices_usa       |
 | orderdetails       |
 | orders             |
 | payments           |
 | productlines       |
 | products           |
 | saleperorder       |
 +--------------------+
 14 rows in set
 SQL

這是由於視圖和表共享相同的命名空間。要知道哪一個對象是視圖或表,請使用SHOW FULL TABLES命令,以下所示:

 mysql> SHOW FULL TABLES;
 +--------------------+------------+
 | Tables_in_yiibaidb | Table_type |
 +--------------------+------------+
 | article_tags       | BASE TABLE |
 | contacts           | BASE TABLE |
 | customers         | BASE TABLE |
 | departments       | BASE TABLE |
 | employees         | BASE TABLE |
 | offices           | BASE TABLE |
 | offices_bk         | BASE TABLE |
 | offices_usa       | BASE TABLE |
 | orderdetails       | BASE TABLE |
 | orders             | BASE TABLE |
 | payments           | BASE TABLE |
 | productlines       | BASE TABLE |
 | products           | BASE TABLE |
 | saleperorder       | VIEW       |
 +--------------------+------------+
 14 rows in set
 SQL

結果集中的table_type列指定哪一個對象是視圖,哪一個對象是一個表(基表)。如上所示,saleperorder對應table_type列的值爲:VIEW

若是要查詢每一個銷售訂單的總銷售額,只須要對SalePerOrder視圖執行一個簡單的SELECT語句,以下所示:

 SELECT 
    *
 FROM
    salePerOrder;
 SQL

執行上面查詢語句,獲得如下結果 -

 +-------------+----------+
 | orderNumber | total   |
 +-------------+----------+
 |       10165 | 67392.85 |
 |       10287 | 61402.00 |
 |       10310 | 61234.67 |
 |       10212 | 59830.55 |
 |-- 此處省略了一大波數據-- |
 |       10116 | 1627.56 |
 |       10158 | 1491.38 |
 |       10144 | 1128.20 |
 |       10408 | 615.45   |
 +-------------+----------+
 327 rows in set
 SQL

基於另外一個視圖建立視圖

MySQL容許您基於另外一個視圖建立一個視圖。例如,能夠根據SalesPerOrder視圖建立名爲大銷售訂單(BigSalesOrder)的視圖,以顯示總計大於60,000的每一個銷售訂單,以下所示:

 CREATE VIEW BigSalesOrder AS
     SELECT
        orderNumber, ROUND(total,2) as total
     FROM
        saleperorder
     WHERE
        total > 60000;
 SQL

如今,咱們能夠從BigSalesOrder視圖查詢數據,以下所示:

 SELECT 
    orderNumber, total
 FROM
    BigSalesOrder;
 SQL

執行上面查詢語句,獲得如下結果 -

 +-------------+----------+
 | orderNumber | total   |
 +-------------+----------+
 |       10165 | 67392.85 |
 |       10287 | 61402.00 |
 |       10310 | 61234.67 |
 +-------------+----------+
 3 rows in set
 SQL

使用鏈接表建立視圖

如下是使用INNER JOIN建立視圖的示例。 該視圖包含客戶編號和客戶支付的總金額。

 CREATE VIEW customerOrders AS
     SELECT
        c.customerNumber,
        p.amount
     FROM
        customers c
            INNER JOIN
        payments p ON p.customerNumber = c.customerNumber
     GROUP BY c.customerNumber
     ORDER BY p.amount DESC;
 SQL

要查詢customerOrders視圖中的數據,請使用如下查詢:

 SELECT * FROM customerOrders;
 SQL

執行上面查詢語句,獲得如下結果 -

 +----------------+-----------+
 | customerNumber | amount   |
 +----------------+-----------+
 |            124 | 101244.59 |
 |            321 | 85559.12 |
 |            239 | 80375.24 |
 | **** 此處省略了一大波數據 ***|
 |            219 | 3452.75   |
 |            216 | 3101.4   |
 |            161 | 2434.25   |
 |            172 | 1960.8   |
 +----------------+-----------+
 98 rows in set
 Shell

使用子查詢建立視圖

如下說明如何使用子查詢建立視圖,該視圖包含價格高於全部產品的平均價格的產品。

 CREATE VIEW aboveAvgProducts AS
     SELECT
        productCode, productName, buyPrice
     FROM
        products
     WHERE
        buyPrice >
  (SELECT
                AVG(buyPrice)
             FROM
                products)
     ORDER BY buyPrice DESC;
 SQL

查詢上述視圖:aboveAvgProducts的數據簡單以下:

 SELECT 
    *
 FROM
    aboveAvgProducts;
 SQL

執行上面查詢語句,獲得如下結果 -

 +-------------+-----------------------------------------+----------+
 | productCode | productName                             | buyPrice |
 +-------------+-----------------------------------------+----------+
 | S10_4962   | 1962 LanciaA Delta 16V                 | 103.42   |
 | S18_2238   | 1998 Chrysler Plymouth Prowler         | 101.51   |
 | S10_1949   | 1952 Alpine Renault 1300               | 98.58   |
 |************* 此處省略了一大波數據 *********************************|
 | S18_3320   | 1917 Maxwell Touring Car               | 57.54   |
 | S24_4258   | 1936 Chrysler Airflow                   | 57.46   |
 | S18_3233   | 1985 Toyota Supra                       | 57.01   |
 | S18_2870   | 1999 Indy 500 Monte Carlo SS           | 56.76   |
 | S32_4485   | 1974 Ducati 350 Mk3 Desmo               | 56.13   |
 | S12_4473   | 1957 Chevy Pickup                       | 55.7     |
 | S700_3167   | F/A 18 Hornet 1/72                     | 54.4     |
 +-------------+-----------------------------------------+----------+
 54 rows in set

MySQL可更新視圖簡介

在MySQL中,視圖不只是可查詢的,並且是可更新的。這意味着您可使用INSERTUPDATE語句經過可更新視圖插入或更新基表的行。 另外,您可使用DELETE語句經過視圖刪除底層表的行。

可是,要建立可更新視圖,定義視圖的SELECT語句不能包含如下任何元素:

若是使用TEMPTABLE算法建立視圖,則沒法更新視圖。

請注意,有時可使用內部鏈接建立基於多個表的可更新視圖。

MySQL可更新視圖示例

讓咱們先來看看如何建立一個可更新的視圖。

首先,基於示例數據庫(yiibaidb)中的offices表建立一個名爲officeInfo的視圖。該視圖指的是offices表中的三列:officeCodephonecity

 CREATE VIEW officeInfo
  AS
    SELECT officeCode, phone, city
    FROM offices;
 SQL

接下來,使用如下語句從officeInfo視圖中查詢數據:

 SELECT 
    *
 FROM
    officeInfo;
 SQL

執行上面查詢語句,獲得如下結果 -

 mysql> SELECT * FROM officeInfo;
 +------------+------------------+---------------+
 | officeCode | phone           | city         |
 +------------+------------------+---------------+
 | 1         | +1 650 219 4782 | San Francisco |
 | 2         | +1 215 837 0825 | Boston       |
 | 3         | +1 212 555 3000 | NYC           |
 | 4         | +33 14 723 4404 | Paris         |
 | 5         | +86 33 224 5000 | Beijing       |
 | 6         | +61 2 9264 2451 | Sydney       |
 | 7         | +44 20 7877 2041 | London       |
 +------------+------------------+---------------+
 7 rows in set
 SQL

而後,使用如下UPDATE語句經過officeInfo視圖更改officeCode的值爲:4的辦公室電話號碼。

 UPDATE officeInfo 
 SET
    phone = '+86 089866668888'
 WHERE
    officeCode = 4;
 SQL

最後,驗證更改結果,經過執行如下查詢來查詢officeInfo視圖中的數據:

 mysql> SELECT 
    *
 FROM
    officeInfo
 WHERE
    officeCode = 4;
 
 +------------+------------------+-------+
 | officeCode | phone           | city |
 +------------+------------------+-------+
 | 4         | +86 089866668888 | Paris |
 +------------+------------------+-------+
 1 row in set
 Shell

檢查可更新視圖信息

經過從information_schema數據庫中的views表查詢is_updatable列來檢查數據庫中的視圖是否可更新。

如下查詢語句將查詢yiibaidb數據庫獲取全部視圖,並顯示哪些視圖是可更新的。

 SELECT 
    table_name, is_updatable
 FROM
    information_schema.views
 WHERE
    table_schema = 'yiibaidb';
 SQL

執行上面查詢語句,獲得如下結果 -

 +------------------+--------------+
 | table_name       | is_updatable |
 +------------------+--------------+
 | aboveavgproducts | YES         |
 | bigsalesorder   | YES         |
 | customerorders   | NO           |
 | officeinfo       | YES         |
 | saleperorder     | NO           |
 +------------------+--------------+
 5 rows in set
 SQL

經過視圖刪除行

首先,建立一個名爲items的表,在items表中插入一些行,並建立一個查詢包含價格大於700的項的視圖。

 USE testdb;
 -- create a new table named items
 CREATE TABLE items (
     id INT AUTO_INCREMENT PRIMARY KEY,
     name VARCHAR(100) NOT NULL,
     price DECIMAL(11 , 2 ) NOT NULL
 );
 ​
 -- insert data into the items table
 INSERT INTO items(name,price) 
 VALUES('Laptop',700.56),('Desktop',699.99),('iPad',700.50) ;
 ​
 -- create a view based on items table
 CREATE VIEW LuxuryItems AS
     SELECT 
         *
     FROM
         items
     WHERE
         price > 700;
 -- query data from the LuxuryItems view
 SELECT 
     *
 FROM
     LuxuryItems;
 SQL

執行上面查詢語句後,獲得如下結果 -

 +----+--------+--------+
 | id | name   | price |
 +----+--------+--------+
 |  1 | Laptop | 700.56 |
 |  3 | iPad   | 700.5 |
 +----+--------+--------+
 2 rows in set
 Shell

其次,使用DELETE語句來刪除id3的行。

 DELETE FROM LuxuryItems 
 WHERE
    id = 3;
 SQL

MySQL返回一條消息,表示有1行受到影響。

 Query OK, 1 row affected
 SQL

第三步,再次經過視圖檢查數據。

 mysql> SELECT * FROM LuxuryItems;
 +----+--------+--------+
 | id | name   | price  |
 +----+--------+--------+
 |  1 | Laptop | 700.56 |
 +----+--------+--------+
 1 row in set
 SQL

第四步,還能夠從基表items查詢數據,以驗證DELETE語句是否實際刪除了該行。

 mysql> SELECT  * FROM items;
 +----+---------+--------+
 | id | name    | price  |
 +----+---------+--------+
 |  1 | Laptop  | 700.56 |
 |  2 | Desktop | 699.99 |
 +----+---------+--------+
 2 rows in set
 SQL

如上面所示,ID3的行在基表中被刪除。

WITH CHECK OPTION子句簡介

有時候,建立一個視圖來顯示錶的部分數據。然而,簡單視圖是可更新的,所以能夠更新經過視圖不可見的數據。此更新使視圖不一致。爲了確保視圖的一致性,在建立或修改視圖時使用WITH CHECK OPTION子句。

下面說明了WITH CHECK OPTION子句的語法 -

 CREATE OR REPLACE VIEW view_name 
 AS
  select_statement
  WITH CHECK OPTION;
 SQL

請注意,將分號(;)放在WITH CHECK OPTION子句的末尾,而不是在SELECT語句的末尾來定義視圖。

咱們來看一下使用WITH CHECK OPTION子句的例子。

MySQL WITH CHECK OPTION子句示例

首先,咱們根據employees建立一個名爲vps的視圖,以顯示其職位爲VP的員工,例如VP MarketingVP Sales

 CREATE OR REPLACE VIEW vps AS
     SELECT
        employeeNumber,
        lastname,
        firstname,
        jobtitle,
        extension,
        email,
        officeCode,
        reportsTo
     FROM
        employees
     WHERE
        jobTitle LIKE '%VP%';
 SQL

接下來,使用如下語句從vps視圖中查詢數據:

 SELECT * FROM vps;
 SQL

執行上面查詢語句,獲得如下結果 -

 mysql> SELECT * FROM vps;
 +----------------+----------+-----------+--------------+-----------+----------------------+------------+-----------+
 | employeeNumber | lastname | firstname | jobtitle     | extension | email               | officeCode | reportsTo |
 +----------------+----------+-----------+--------------+-----------+----------------------+------------+-----------+
 |           1056 | Hill     | Mary     | VP Sales     | x4611     | mary.hill@yiibai.com | 1         |      1002 |
 |           1076 | Firrelli | Jeff     | VP Marketing | x9273     | jfirrelli@yiibai.com | 1         |      1002 |
 +----------------+----------+-----------+--------------+-----------+----------------------+------------+-----------+
 2 rows in set
 SQL

由於vps是一個簡單的視圖,所以它是可更新的。

而後,咱們經過vps視圖將一行員工數據信息插入。

 INSERT INTO vps(employeeNumber,firstname,lastname,jobtitle,extension,email,officeCode,reportsTo)
 values(1703,'Lily','Bush','IT Manager','x9111','lilybush@yiiibai.com',1,1002);
 SQL

請注意,新建立的員工經過vps視圖不可見,由於她的職位是IT經理,而不是VP。使用如下SELECT語句來驗證它。

 SELECT * FROM employees WHERE employeeNumber=1703;
 SQL

執行上面語句,獲得如下結果 -

 +----------------+-----------+-----------+-----------+-----------------------+------------+-----------+----------------------+
 | employeeNumber | lastName | firstName | extension | email                 | officeCode | reportsTo | jobTitle             |
 +----------------+-----------+-----------+-----------+-----------------------+------------+-----------+----------------------+
 |           1703 | Bush     | Lily     | x9111     | lilybush@yiiibai.com | 1         |      1002 | IT Manager           |
 |           1702 | Gerard   | Martin   | x2312     | mgerard@gmail.com     | 4         |      1102 | Sales Rep           |
 |           1625 | Kato     | Yoshimi   | x102     | ykato@gmail.com       | 5         |      1621 | Sales Rep           |
 |           1621 | Nishi     | Mami     | x101     | mnishi@gmail.com     | 5         |      1056 | Sales Rep           |
 SQL

但這可能不是咱們想要的,由於經過vps視圖暴露VP員工,而不是其餘員工。

爲了確保視圖的一致性,用戶只能顯示或更新經過視圖可見的數據,則在建立或修改視圖時使用WITH CHECK OPTION

讓咱們修改視圖以包括WITH CHECK OPTION選項。

 CREATE OR REPLACE VIEW vps AS
     SELECT
        employeeNumber,
        lastname,
        firstname,
        jobtitle,
        extension,
        email,
        officeCode,
        reportsTo
     FROM
        employees
     WHERE
        jobTitle LIKE '%VP%'
 WITH CHECK OPTION;
 SQL

請注意在CREATE OR REPLACE語句的結尾處加上WITH CHECK OPTION子句。

以後,再次經過vps視圖將一行插入employees表中,以下所示:

 INSERT INTO vps(employeeNumber,firstname,lastname,jobtitle,extension,email,officeCode,reportsTo)
 VALUES(1704,'John','Minsu','IT Staff','x9112','johnminsu@yiibai.com',1,1703);
 SQL

此次MySQL拒絕插入併發出如下錯誤消息:

 Error Code: 1369 - CHECK OPTION failed 'yiibaidb.vps'
 SQL

最後,咱們經過vps視圖將一個職位爲SVP Marketing的員工插入employees表,看看MySQL是否容許這樣作。

 INSERT INTO vps(employeeNumber,firstname,lastname,jobtitle,extension,email,officeCode,reportsTo)
 VALUES(1704,'John','Minsu','SVP Marketing','x9112','johnminsu@classicmodelcars.com',1,1076);
 SQL

MySQL發出1行受影響(Query OK, 1 row affected)。

能夠經過根據vps視圖查詢數據來再次驗證插入操做。

 SELECT * FROM vps;
 SQL

如上查詢結果所示,它的確按預期工做了。

 mysql> SELECT * FROM vps;
 +----------------+----------+-----------+---------------+-----------+--------------------------------+------------+-----------+
 | employeeNumber | lastname | firstname | jobtitle     | extension | email                         | officeCode | reportsTo |
 +----------------+----------+-----------+---------------+-----------+--------------------------------+------------+-----------+
 |           1056 | Hill     | Mary     | VP Sales     | x4611     | mary.hill@yiibai.com           | 1         |      1002 |
 |           1076 | Firrelli | Jeff     | VP Marketing | x9273     | jfirrelli@yiibai.com           | 1         |      1002 |
 |           1704 | Minsu   | John     | SVP Marketing | x9112     | johnminsu@classicmodelcars.com | 1         |      1076 |
 +----------------+----------+-----------+---------------+-----------+--------------------------------+------------+-----------+
 3 rows in set

LOCAL&CASCADED檢查範圍介紹

當使用WITH CHECK OPTION子句建立視圖時,MySQL會經過視圖檢查正在更改的每一個行,例如插入更新刪除,以使其符合視圖的定義。由於MySQL容許基於另外一個視圖建立視圖,它還會檢查依賴視圖中的規則以保持一致性。

爲了肯定檢查的範圍,MySQL提供了兩個選項:LOCALCASCADED。若是您沒有在WITH CHECK OPTION子句中顯式指定關鍵字,則MySQL默認使用CASCADED

MySQL與CASCADC檢查選項

要了解使用CASCADED CHECK OPTION的效果,請參閱下面的例子。

首先,建立一個名爲t1的表,其中只有一個名稱爲:c的列,它的數據類型爲int

 USE testdb;
 CREATE TABLE t1 (
    c INT
 );
 SQL

接下來,基於t1表建立一個名爲v1的視圖,以選擇值大於10的行記錄。

 CREATE OR REPLACE VIEW v1 
 AS
     SELECT
        c
     FROM
        t1
     WHERE
        c > 10;
 SQL

由於沒有指定WITH CHECK OPTION,因此如下語句即便不符合v1視圖的定義也能夠工做。

 INSERT INTO v1(c) VALUES (5);
 SQL

而後,基於v1視圖建立v2視圖。在v2視圖中添加一個WITH CASCADED CHECK OPTION子句。

 CREATE OR REPLACE VIEW v2 
 AS
     SELECT
        c
     FROM
        v1
 WITH CASCADED CHECK OPTION;
 SQL

如今,經過v2視圖在t1表中插入一個值爲5的行。

 INSERT INTO v2(c) VALUES (5);
 SQL

MySQL發出如下錯誤消息:

 Error Code: 1369. CHECK OPTION failed 'testdb.v2'
 SQL

它失敗了,由於它建立一個不符合v2視圖定義的新行。

以後,咱們再建立一個基於v2的名爲v3的新視圖。

 CREATE OR REPLACE VIEW v3 
 AS
     SELECT
        c
     FROM
        v2
     WHERE
        c < 20;
 SQL

咱們經過v3視圖插入一個新行到t1表中,值爲8

 INSERT INTO v3(c) VALUES (8);
 SQL

MySQL發出如下錯誤信息:

 Error Code: 1369. CHECK OPTION failed 'testdb.v3'
 SQL

上面插入語句看起來符合v3視圖的定義,insert語句仍然執行失敗。

這是爲何呢?

由於v3視圖取決於v2視圖,v2視圖具備WITH CASCADED CHECK OPTION

可是,如下插入語句能正常工做。

 INSERT INTO v3(c) VALUES (30);
 SQL

由於v3視圖沒有使用WITH CHECK OPTION定義,而且該語句符合v2視圖的定義。

因此,總而言之:

當視圖使用WITH CASCADED CHECK OPTION時,MySQL會循環檢查視圖的規則以及底層視圖的規則。

MySQL WITH LOCAL CHECK OPTION

下面將演示使用 WITH LOCAL CHECK OPTION 選項,使用上面相同的示例來查看差別。

首先,將v2視圖更改成使用WITH LOCAL CHECK OPTIONS替代。

 ALTER VIEW v2 AS
     SELECT
        c
     FROM
        v1
 WITH LOCAL CHECK OPTION;
 SQL

其次,插入與上述示例相同的行。

 INSERT INTO v2(c) VALUES (5);
 SQL

它是能夠成功執行的。

由於v2視圖沒有任何規則。 v2視圖取決於v1視圖。 可是,v1視圖沒有指定檢查選項,所以MySQL跳過檢查v1視圖中的規則。

請注意,在使用WITH CASCADED CHECK OPTION建立的v2視圖中,此語句失敗。

第三,經過v3視圖將相同的行插入t1表。

 INSERT INTO v3(c) VALUES (8);
 SQL

在這種狀況下能夠執行成功,由於MySQL視圖中的WITH LOCAL CHECK OPTIONS選項沒有檢查v1視圖的規則。 另外,請注意,在使用WITH CASCADED CHECK OPTION建立的v2視圖示例中,此語句執行失敗。

 

所以,若是視圖使用WITH LOCAL CHECK OPTION,MySQL會檢查WITH LOCAL CHECK OPTIONWITH CASCADED CHECK OPTION選項的視圖規則。

與使用WITH CASCADED CHECK OPTION的視圖不一樣,MySQL檢查全部依賴視圖的規則。

注意,在MySQL 5.7.6以前,若是您使用帶有WITH LOCAL CHECK OPTION的視圖,MySQL只會檢查當前視圖的規則,而且不會檢查底層視圖的規則。

查看視圖定義

MySQL提供了用於顯示視圖定義的SHOW CREATE VIEW語句。

如下是SHOW CREATE VIEW語句的語法:

 SHOW CREATE VIEW [database_name].[view_ name];
 SQL

要顯示視圖的定義,須要在SHOW CREATE VIEW子句以後指定視圖的名稱。

爲了更好的演示,咱們先來建立一個視圖

假設根據employees表建立一個簡單的視圖用來顯示公司組織結構:

 USE yiibaidb;
 CREATE VIEW organization AS
     SELECT
        CONCAT(E.lastname, E.firstname) AS Employee,
        CONCAT(M.lastname, M.firstname) AS Manager
     FROM
        employees AS E
            INNER JOIN
        employees AS M ON M.employeeNumber = E.ReportsTo
     ORDER BY Manager;
 SQL

從以上視圖中查詢數據,獲得如下結果 -

 mysql> SELECT * FROM organization;
 +------------------+------------------+
 | Employee         | Manager         |
 +------------------+------------------+
 | BondurLoui       | BondurGerard     |
 | CastilloPamela   | BondurGerard     |
 | JonesBarry       | BondurGerard     |
 | HernandezGerard | BondurGerard     |
 .......此處省略了一大波數據.......
 | KatoYoshimi     | NishiMami       |
 | KingTom         | PattersonWilliam |
 | MarshPeter       | PattersonWilliam |
 | FixterAndy       | PattersonWilliam |
 +------------------+------------------+
 24 rows in set
 SQL

要顯示視圖的定義,請使用SHOW CREATE VIEW語句以下:

 SHOW CREATE VIEW organization;
 SQL

還可使用任何純文本編輯器(如記事本)顯示視圖的定義,以打開數據庫文件夾中的視圖定義文件。

例如,要打開organization視圖定義,可使用如下路徑找到視圖定義文件:\data\yiibaidb\organization.frm

可是,不該該直接在.frm文件中修改視圖的定義。

修改視圖

MySQL提供兩個語句,容許您修改現有視圖:ALTER VIEWCREATE OR REPLACE VIEW

使用ALTER VIEW語句修改視圖

建立視圖後,可使用ALTER VIEW語句修改視圖。

ALTER VIEW語句的語法相似於CREATE VIEW語句,除了CREATE關鍵字被ALTER關鍵字替換外,其它都同樣。

 ALTER
  [ALGORITHM = {MERGE | TEMPTABLE | UNDEFINED}]
  VIEW [database_name]. [view_name]
    AS
  [SELECT statement]
 SQL

如下語句經過添加email列來演示如何修改organization視圖。

 ALTER VIEW organization
   AS
   SELECT CONCAT(E.lastname,E.firstname) AS Employee,
          E.email AS employeeEmail,
          CONCAT(M.lastname,M.firstname) AS Manager
   FROM employees AS E
  INNER JOIN employees AS M
     ON M.employeeNumber = E.ReportsTo
   ORDER BY Manager;
 SQL

要驗證更改,能夠從organization視圖中查詢數據:

 SELECT 
    *
 FROM
    Organization;
 SQL

執行上面查詢語句,獲得如下結果 -

 Shell

使用CREATE OR REPLACE VIEW語句修改視圖

ALTER VIEW語句外,還可使用CREATE OR REPLACE VIEW語句來建立或替換現有視圖。若是一個視圖已經存在,MySQL只會修改視圖。若是視圖不存在,MySQL將建立一個新的視圖。

如下語句使用CREATE OR REPLACE VIEW語法根據employees表建立一個名稱爲v_contacts的視圖:

 CREATE OR REPLACE VIEW v_contacts AS
     SELECT
        firstName, lastName, extension, email
     FROM
        employees;
 -- 查詢視圖數據
 SELECT * FROM v_contacts;
 SQL

執行上面查詢語句,獲得如下結果 -

 +-----------+-----------+-----------+--------------------------------+
 | firstName | lastName | extension | email                         |
 +-----------+-----------+-----------+--------------------------------+
 | Diane     | Murphy   | x5800     | dmurphy@yiibai.com             |
 | Mary     | Hill     | x4611     | mary.hill@yiibai.com           |
 | Jeff     | Firrelli | x9273     | jfirrelli@yiibai.com           |
 | William   | Patterson | x4871     | wpatterson@yiibai.com         |
 | Gerard   | Bondur   | x5408     | gbondur@gmail.com             |
 | Anthony   | Bow       | x5428     | abow@gmail.com                 |
 | Leslie   | Jennings | x3291     | ljennings@yiibai.com           |
 .............. 此處省略了一大波數據 ..................................
 | Martin   | Gerard   | x2312     | mgerard@gmail.com             |
 | Lily     | Bush     | x9111     | lilybush@yiiibai.com           |
 | John     | Minsu     | x9112     | johnminsu@classicmodelcars.com |
 +-----------+-----------+-----------+--------------------------------+
 25 rows in set
 Shell

假設您要將職位(jobtitle)列添加到v_contacts視圖中,只需使用如下語句 -

 CREATE OR REPLACE VIEW v_contacts AS
     SELECT
        firstName, lastName, extension, email, jobtitle
     FROM
        employees;
 -- 查詢視圖數據
 SELECT * FROM v_contacts;
 SQL

執行上面查詢語句後,能夠看到添加一列數據 -

 +-----------+-----------+-----------+--------------------------------+----------------------+
 | firstName | lastName | extension | email                         | jobtitle             |
 +-----------+-----------+-----------+--------------------------------+----------------------+
 | Diane     | Murphy   | x5800     | dmurphy@yiibai.com             | President           |
 | Mary     | Hill     | x4611     | mary.hill@yiibai.com           | VP Sales             |
 | Jeff     | Firrelli | x9273     | jfirrelli@yiibai.com           | VP Marketing         |
 ................... 此處省略了一大波數據 ....................................................
 | Yoshimi   | Kato     | x102     | ykato@gmail.com               | Sales Rep           |
 | Martin   | Gerard   | x2312     | mgerard@gmail.com             | Sales Rep           |
 | Lily     | Bush     | x9111     | lilybush@yiiibai.com           | IT Manager           |
 | John     | Minsu     | x9112     | johnminsu@classicmodelcars.com | SVP Marketing       |
 +-----------+-----------+-----------+--------------------------------+----------------------+
 25 rows in set
 SQL

刪除視圖

建立視圖後,可使用DROP VIEW語句將其刪除。下面說明了DROP VIEW語句的語法:

 DROP VIEW [IF EXISTS] [database_name].[view_name]
 SQL

IF EXISTS是語句的可選子句,它容許您檢查視圖是否存在。它能夠避免刪除不存在的視圖的錯誤。

例如,若是要刪除organization視圖,能夠按以下所示使用DROP VIEW語句:

 DROP VIEW IF EXISTS organization;
 SQL

每次修改或刪除視圖時,MySQL會將視圖定義文件備份到/database_name/arc/目錄中。 若是您意外修改或刪除視圖,能夠從/database_name/arc/文件夾獲取其備份。

三.元數據獲取

  • 3.1 show

MySQL5.7show結構

 show  databases;                          #查看全部數據庫
 show tables;                              #查看當前庫的全部表
 SHOW TABLES FROM                          #查看某個指定庫下的表
 show create database world                #查看建庫語句
 show create table world.city              #查看建表語句
 show grants for root@'localhost'        #查看用戶的權限信息
 show charset;                           #查看字符集
 show collation                            #查看校對規則
 show processlist;                         #查看數據庫鏈接狀況
 show index from                           #表的索引狀況
 show status                               #數據庫狀態查看
 SHOW STATUS LIKE '%lock%';                #模糊查詢數據庫某些狀態
 SHOW VARIABLES                            #查看全部配置信息
 SHOW variables LIKE '%lock%';             #查看部分配置信息
 show engines                              #查看支持的全部的存儲引擎
 show engine innodb status\G             #查看InnoDB引擎相關的狀態信息
 show binary logs                       #列舉全部的二進制日誌
 show master status                     #查看數據庫的日誌位置信息
 show binlog evnets in                  #查看二進制日誌事件
 show slave status \G                   #查看從庫狀態
 SHOW RELAYLOG EVENTS               #查看從庫relaylog事件信息
 desc (show colums from city)      #查看錶的列定義信息

3.2 information_schema

  • a.介紹

視圖,查詢元數據的方法

  • b.tables視圖應用

 TABLE_SCHEMA    ---->表所在庫
 TABLE_NAME      ---->表名
 ENGINE          ---->存儲引擎
 TABLE_ROWS      ---->表的行數(粗略統計)
 AVG_ROW_LENGTH  ---->表中行的平均行(字節)(粗略統計)
 INDEX_LENGTH    ---->索引的佔用空間大小(字節)(粗略統計)
 DATA_FREE       ---->碎片數
 TABLE_COMMENT   ---->表註釋

例子:

  • 1.查詢整個數據庫中全部庫和所對應的表信息

 庫名                     表個數                表名
 world                   3                   city,a,b
 SELECT table_schema,count(*) ,GROUP_CONCAT(table_name)
 FROM information_schema.tables
 GROUP BY table_schema;
  • 2.統計每一個庫的數據量大小

    -- 表的數據量=平均行長度*行數+索引長度

 SELECT table_schema,sum(TABLE_ROWS*AVG_ROW_LENGTH+INDEX_LENGTH)/1024 
 FROM information_schema.tables
 GROUP BY table_schema;

3.3concat()拼接語句

  • a. 查詢一下業務數據庫中,非InnoDB的表

 mysql> create table t2(id int)engine=myisam;
 SELECT table_schema , table_name ,engine FROM information_schema.tables 
 WHERE 
 table_schema NOT IN ('mysql','sys','information_schema','performance_schema') 
 AND ENGINE <>'innodb';  
 mysql> alter table t2 engine=innodb;
 mysql> alter table world.t3 engine=innodb;
 mysql> select concat(user,"@",host) from mysql.user;
 SELECT CONCAT("table_schema" , "table_name" ,"engine") FROM information_schema.tables 
 WHERE 
 table_schema NOT IN ('mysql','sys','information_schema','performance_schema') 
 AND ENGINE <>'innodb';
  • b. 將非InnoDB表批量替換爲InnoDB

 SELECT concat("alter table ",table_schema,".",table_name," engine=innodb;") FROM information_schema.tables 
 WHERE 
 table_schema NOT IN ('mysql','sys','information_schema','performance_schema','world') 
 AND ENGINE <>'innodb' into outfile '/tmp/alter.sql' ; 
 ​
 [root@db01 ~]#mysqldump -uroot -p123 world city>/tmp/world_city.sql#備份/tmp/world_city.sql
 [root@db01 ~]#mysqldump -uroot -p123 world city >/databak/world_city.sql
 SELECT  concat("mysqldump -uroot -p123 ",table_schema," ",table_name," >/databak/",table_schema,"_",table_name,".sql") FROM information_schema.tables 
 WHERE 
 table_schema NOT IN ('mysql','sys','information_schema','performance_schema') into outfile '/tmp/bak.sh' ;  #導出到/tmp/bak.sh
 [root@db01 ~]# sh /tmp/bak.sh

3.4 COLUMNS

 TABLE_SCHEMA    ---->庫名
 TABLE_NAME     ---->表名
 COLUMN_NAME     ---->列名
 DATA_TYPE       ---->數據類型
 COLUMN_KEY     ---->列鍵
 COLUMN_COMMENT ---->列註釋
 mysql> select id,command from processlist where command='sleep'
 mysql>kill 10:\
 mysql>kill 11:\

四.索引及執行計劃

1.介紹

相似於一本書中的目錄,起到優化查詢的做用(select,update,delete)

2.種類

 Btree(平衡多叉樹):b-tree   b+tree(b*tree),優勢:範圍查找
 HASH :優勢,比較適合隨機的等值。
 Rtree
 FullText
 GIS 索引

二叉樹

 

 

 

 原理:
1> 將數據存放在一個一個節點上;
2> 節點又分爲三種節點:最上面的叫根節點,中間的叫分支節點,最下面的到底叫葉子節點。
3> 每一個分支節點有一個分支,或者兩個分支。
缺點
二叉樹存在不平衡的問題。

紅黑樹

 

 

 

原理: 就是平衡的二叉樹。

 紅黑樹(Red-Black Tree)是二叉搜索樹(Binary Search Tree)的一種改進。咱們知道二叉搜索樹在最壞的狀況下可能會變成一個鏈表(當全部節點按從小到大的順序依次插入後)。
 而紅黑樹在每一次插入或刪除節點以後都會花O(log N)的時間來對樹的結構做修改,以保持樹的平衡。也就是說,紅黑樹的查找方法與二叉搜索樹徹底同樣;插入和刪除節點的的方法前半部分節與二叉搜索樹徹底同樣,然後半部分添加了一些修改樹的結構的操做。

缺點: 查詢數字的速度慢。

3.Btree的細分

Btree樹

 

原理:

 B樹(B-Tree,並非B「減」樹,橫槓爲鏈接符,容易被誤導)
 <br> 是一種多路搜索樹(並非二叉的):
 1.定義任意非葉子結點最多隻有M個兒子;且M>2;
 2.根結點的兒子數爲[2, M];
 3.除根結點之外的非葉子結點的兒子數爲[M/2, M];
 4.每一個結點存放至少M/2-1(取上整)和至多M-1個關鍵字;(至少2個關鍵字)
 5.非葉子結點的關鍵字個數=指向兒子的指針個數-1;
 6.非葉子結點的關鍵字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
 7.非葉子結點的指針:P[1], P[2], …, P[M];其中P[1]指向關鍵字小於K[1]的子樹,P[M]指向關鍵字大於K[M-1]的子樹,其它P[i]指向關鍵字屬於(K[i-1], K[i])的子樹;
 8.全部葉子結點位於同一層;

B-樹的特性:

 1.關鍵字集合分佈在整顆樹中;
 2.任何一個關鍵字出現且只出如今一個結點中;
 3.搜索有可能在非葉子結點結束;
 4.其搜索性能等價於在關鍵字全集內作一次二分查找;
 5.自動層次控制;

注意:B-樹的搜索,從根結點開始,對結點內的關鍵字(有序)序列進行二分查找,若是命中則結束,不然進入查詢關鍵字所屬範圍的兒子結點;重複,直到所對應的兒子指針爲空,或已是葉子結點;

每次查找數據時,都去根節點查找數據,查找效率低下。

B+TREE

 

 

 

 

 

 一般所說的索引是指 B-Tree索引,它是目前關係型數據庫中查找數據最爲經常使用和有效的索引,大多數存儲引擎都支持這種索引,且 MySQL 默認採用這種索引。使用 B-Tree 這個術語,是由於 MySQL 在 CREATE TABLE 或其它語句中使用這個關鍵字,
但實際上不一樣的存儲引擎可能使用不一樣的數據結構,好比 InnoDB 就是使用的 B+Tree。B+Tree中的B是指balance,意爲平衡。相對 Hash索引,B+Tree在查找單條記錄的速度比不上 Hash索引,可是由於更適合排序等操做,因此它更受歡迎。畢竟不可能只對數據庫進行單條記錄的操做

注 : B+樹索引 並不能找到一個給定鍵值的具體行,它找到的只是被查找數據行所在的頁,接着數據庫會把頁讀入到內存,再在內存中進行查找,最後獲得要查找的數據

葉子節點只放數據,根節點和分支節點只放索引。

B+樹特性:

 1.全部關鍵字都出如今葉子結點的鏈表中(稠密索引),且鏈表中的關鍵字剛好是有序的;
 2.不可能在非葉子結點命中;
 3.非葉子結點至關因而葉子結點的索引(稀疏索引),葉子結點至關因而存儲(關鍵字)數據的數據層;
 4.更適合文件索引系統;
 5.葉子節點最下面的數據塊指針直接指向下一個數據塊的位置,適合按順序範圍查詢,搜索必定的範圍,速度很是快。
 6.全部的記錄優先級一致,查詢次數一致,效率一致。

B+Tree索引:順序存儲,每個葉子節點到根結點的距離是相同的;左前綴索引,適合查詢範圍類的數據

可使用B+Tree索引的查詢類型:

 全值匹配:精確全部索引列,如:姓wang,名xiaochun,年齡30
 匹配最左前綴:即只使用索引的第一列,如:姓wang
 匹配列前綴:只匹配一列值開頭部分,如:姓以w開頭的
 匹配範圍值:如:姓ma和姓wang之間
 精確匹配某一列並範圍匹配另外一列:如:姓wang,名以x開頭的只訪問索引的查詢

B+Tree索引的限制

 1> 如不從最左列開始,則沒法使用索引,如:查找名爲xiaochun,或姓爲g結尾
 2> 不能跳過索引中的列:如:查找姓wang,年齡30的,只能使用索引第一列
 3> 若是查詢中某個列是爲範圍查詢,那麼其右側的列都沒法再使用索引:如:姓wang,名x%,年齡30,只能利用姓和名上面的索引

特別提示:

 1> 索引列的順序和查詢語句的寫法應相匹配,才能更好的利用索引
 2> 爲優化性能,可能須要針對相同的列但順序不一樣建立不一樣的索引來知足不一樣類型的查詢需求

冗餘和重複索引:

 冗餘索引:(A),(A,B)
 重複索引:已經有索引,再次創建索引

索引優化策略:

 1> 獨立地使用列:儘可能避免其參與運算,獨立的列指索引列不能是表達式的一部分,也不能是函數的參數,在where條件中,始終將索引列單獨放在比較符號的一側
 2> 左前綴索引:構建指定索引字段的左側的字符數,要經過索引選擇性來評估
 3> 索引選擇性:不重複的索引值和數據表的記錄總數的比值
 4> 多列索引:AND操做時更適合使用多列索引,而非爲每一個列建立單獨的索引
 5> 選擇合適的索引列順序:無排序和分組時,將選擇性最高放左側

Hash 索引

 mysql 中,只有 Memory(Memory表只存在內存中,斷電會消失,適用於臨時表) 存儲引擎顯示支持 Hash索引,是 Memory表的默認索引類型,儘管 Memory表也可使用 B+Tree索引。Hash索引 把數據以 hash 形式組織起來,所以當查找某一條記錄的時候,速度很是快。<br>可是由於 hash 結構,每一個鍵只對應一個值,並且是散列的方式分佈,因此它並不支持範圍查找和排序等功能

 

 

 

 聚簇和非聚簇索引,主鍵和二級索引

MyISAM:frm後綴的文件(存放表結構)、文件後綴是YMD(存放數據文件)和YMI後綴的文件(存放索引文件),索引和數據分開存放,爲非聚簇索引

innodb:文件中frm後綴(存放表結構)和idb後綴的文件存放(索引和數據文件),索引和數據文件在一塊兒存放,爲聚簇索引

稠密索引:一個索引對應了全部的數據。

稀疏索引:一個索引對應了一個數據,且對應的數據不全。

簡單索引:對單個字段建立索引

複合索引:對多個字段建立索引 

索引的設計原則

 1> 選擇惟一性索引,可更快經過索引肯定某條記錄
 2> 爲常常須要 排序ORDER BY、分組GROUP BY 和 聯合操做UNION 的字段創建索引
 3> 爲常做爲查詢條件的字段創建索引
 4> 限制索引的數目,索引太多須要的磁盤空間就越大,修改表示對索引的重構和更新會很麻煩
 5> 儘可能使用數據量少的索引,對 CHAR(100) 全文索引確定會比 CHAR(10) 耗時多
 6> 儘可能使用前綴來索引
 7> 刪除再也不使用或者不多使用的索引
 8> 避免多個範圍條件 : MySQL 支持單列的範圍索引,但不支持多列範圍索引
 9> 儘可能避免NULL,含有 NULL 的索引將很難進行優化

索引優化建議

 1> 只要列中含有NULL值,就最好不要在此例設置索引,複合索引若是有NULL值,此列在使用時也不會使用索引
 2> 儘可能使用短索引,若是能夠,應該制定一個前綴長度
 3> 對於常常在where子句使用的列,最好設置索引
 4> 對於有多個列where或者order by子句,應該創建複合索引
 5> 對於like語句,以%或者‘-’開頭的不會使用索引,以%結尾會使用索引
 6> 儘可能不要在列上進行運算(函數操做和表達式操做)
 7> 儘可能不要使用not in和<>操做

SQL語句性能優化

 1> 查詢時,能不要*就不用*,儘可能寫全字段名
 2> 大部分狀況鏈接效率遠大於子查詢
 3> 多表鏈接時,儘可能小表驅動大表,即小表 join 大表
 4> 在有大量記錄的表分頁時使用limit
 5> 對於常用的查詢,能夠開啓緩存
 6> 多使用explain和profile分析查詢語句
 7> 查看慢查詢日誌,找出執行時間長的sql語句優化

聚簇索引:主鍵索引

 (1)MySQL 會自動選擇主鍵做爲彙集索引列,沒有主鍵會選擇惟一鍵,若是都沒有會生成隱藏的.
 (2)MySQL進行存儲數據時,會按照彙集索引列值得順序,有序存儲數據行    
 (3)彙集索引直接將原表數據頁,做爲葉子節點,而後提取彙集索引列向上生成枝和根

輔助索引:

 (1) 提取索引列的全部值,進行排序      
 (2) 將排好序的值,均勻的存放在葉子節點,進一步生成枝節點和根節點
 (3) 在葉子節點中的值,都會對應存儲主鍵ID
 單列
 聯合
 惟一
 前綴

彙集索引和輔助索引的區別

 (1) 表中任何一個列均可以建立輔助索引,在你有須要的時候,只要名字不一樣便可
 (2) 在一張表中,彙集索引只能有一個,通常是主鍵.
 (3) 輔助索引,葉子節點只存儲索引列的有序值+彙集索引列值(id列值).
 (4) 彙集索引,葉子節點存儲的是有序的整行數據.

4.索引的管理

#索引創建以前壓測:

 mysqlslap --defaults-file=/etc/my.cnf \
 --concurrency=100 --iterations=1 --create-schema='test' \
 --query="select * from test.t100w where k2='VMlm'" engine=innodb \
 --number-of-queries=2000 -uroot -p123 -verbose
 mysql> alter table t100w add index idx_k2(k2);#優化索引
 Benchmark
     Running for engine rbose
     Average number of seconds to run all queries: 3.443 seconds
     Minimum number of seconds to run all queries: 3.443 seconds
     Maximum number of seconds to run all queries: 3.443 seconds
     Number of clients running queries: 100
     Average number of queries per client: 20
4.1 查詢索引
 mysql> use information_schema;
 mysql> show tables;
 use world
 a.desc  city;
 PRI:  主鍵索引
 MUL:  普通索引
 UNI:  惟一索引
 b.mysql> show index from city;
 Table          表名
 Key_name       索引名
 Column_name    列名
 Cardinality    基數(選擇度),位置值的多少

 

面試題:

Cardinality 創建索引以前,基數如何計算的?(重複值高於80%的不建議創建索引) select count(distinct countrycode)from city; +-----------------------------+ | count(distinct countrycode) | +-----------------------------+ | 232 | +-----------------------------+ mysql> select count(*)from city; mysql> select count(id)from city; +-----------+ | count(id) | +-----------+ | 4079 | +-----------+

4.2創建索引

a.單列索引

 mysql> alter table city add index i_name(name);

b.聯合索引

 mysql> alter table city add index i_d_p(district,population);

c.前綴索引

 mysql> alter table city add index i_n(name(10));
 mysql> alter table city_demo add key(city(5));
 Query OK, 0 rows affected (0.34 sec)
 Records: 0  Duplicates: 0  Warnings: 0

 

d.惟一索引

 mysql> alter table tt add unique index ui(telnum);

去重複

 mysql> select count(distinct district) from city;
4.3刪除索引
 mysql> show index from city;
 mysql> alter table city drop index i_name;
 mysql> alter table city drop index i_c_p;
 mysql> alter table city drop index i_n;

4.4 MySQL8.0新特性---> invisible index 不可見索引

 語法:
 ALTER TABLE t1 ALTER INDEX i_idx INVISIBLE;
 ALTER TABLE t1 ALTER INDEX i_idx VISIBLE;
 mysql> alter table city alter index idx_name invisible;
 SELECT INDEX_NAME, IS_VISIBLE
        FROM INFORMATION_SCHEMA.STATISTICS
        WHERE TABLE_SCHEMA = 'world' AND table_name='city';

5.B+tree查找算法介紹

 

 5.1 平衡

無論查找哪一個數,須要查找次數理論上是相同的,對於一個三層b樹來說,理論上查找每一個值都是三次IO。

5.2擅長範圍查找

講究快速鎖定範圍

B+tree,加入了雙向指針(頭尾相接),進一步加強範圍查找,減小對於ROOT和NO-LEAF的訪問次數。

5.3構建過程

葉子:先將數據排序,生成葉子節點。

枝:保存葉子節點的範圍(>=1 <5)+指針(→)

根:保存枝節點範圍+指針

葉子節點和枝節點都有雙向指針。

6.MySQL中如何應用B+TREE

6.1名詞解釋

 區(簇)extent:連續的64pages ,默認是1M存儲空間
 page頁:16KB大小,MySQL中最小的IO單元

6.2IOT組織表

數據應該按照索引結構有序(順序)組織和存儲數據

MySQL使用聚簇索引組織存儲數據。

6.3 聚簇(區)索引

6.3.1 構建條件
 a. 若是表中有主鍵,主鍵就被做爲聚簇索引.
 b. 沒有主鍵,第一個不爲空的惟一鍵.
 c. 什麼都沒有,自動生成一個6字節的隱藏列,做爲聚簇索引.

https://dev.mysql.com/doc/refman/8.0/en/innodb-index-types.html

 When you define a PRIMARY KEY on your table, InnoDB uses it as the clustered index. Define a primary key for each table that you create. If there is no logical unique and non-null column or set of columns, add a new auto-increment column, whose values are filled in automatically.
 If you do not define a PRIMARY KEY for your table, MySQL locates the first UNIQUE index where all the key columns are NOT NULL and InnoDB uses it as the clustered index.
 If the table has no PRIMARY KEY or suitable UNIQUE index, InnoDB internally generates a hidden clustered index named GEN_CLUST_INDEX on a synthetic column containing row ID values. The rows are ordered by the ID that InnoDB assigns to the rows in such a table. The row ID is a 6-byte field that increases monotonically as new rows are inserted. Thus, the rows ordered by the row ID are physically in insertion order.
6.3.2 如何造成B樹結構.
 葉子節點: 聚簇索引組織表,存數據時,已是按照ID列有序存儲數據到各個連續的數據頁中.原表數據存儲結構就是葉子節點.
 枝節點:葉子節點中ID範圍+指針
 根節點:枝節點的ID範圍+指針
 葉子節點和枝節點都有雙向指針.
6.3.3 優化了哪些查詢?
 只能優化基於ID做爲條件.索引單純使用ID列查詢,很侷限.
 > < = group by
 where

 

 

 6.4 輔助索引

 

6.4.1 構建條件

須要人爲按照需求建立輔助索引.

6.4.2 如何造成B樹結構(面試題)

alter table t1 add index idx(name);

 葉子節點 : 將輔助索引列值(name)+ID提取出來,按照輔助索引列值從小到大排序,存儲到各個page中,生成葉子節點.
 枝節點 : 存儲了葉子節點中,name列範圍+指針.
 根節點 : 枝節點的name的範圍+指針.

 

 

 

 

 

 

6.4.3 優化了哪些查詢?

若是查詢條件使用了name列,都會先掃描輔助索引,得到ID,再回到聚簇索引(回表),按照ID進行聚簇索引掃描,最終獲取到數據行.

 All indexes other than the clustered index are known as secondary indexes. In InnoDB, each record in a secondary index contains the primary key columns for the row, as well as the columns specified for the secondary index. InnoDB uses this primary key value to search for the row in the clustered index.
 If the primary key is long, the secondary indexes use more space, so it is advantageous to have a short primary key.

https://dev.mysql.com/doc/refman/8.0/en/innodb-index-types.htm

 

 

 

 

 

 

6.5 聯合輔助索引結構

6.5.1 構建過程
 alter table t1 add index idx_n_g(a,b)
 葉子節點 : 提取a+b+id列值,按照a,b聯合排序(從小到大),生成葉子節點.
 枝節點 : 葉子節點最左列範圍+指針
 根節點 : 枝節點的範圍+指針.

 

 

 

 回表作聚簇索引

 

 

 

 

 

 

6.5.2 優化了哪些查詢?

查詢條件中必須包含最左列條件(a),先經過 a條件 掃描聯合索引的根節點和枝節點,從而獲得葉子節點範圍.再拿b做爲條件過濾一次. 最終目的,獲得更精確的ID .理論上減小回表的次數.

6.5.3 最左原則

創建聯合索引時,選擇基數大(重複值少)做爲最左列. 查詢條件中必需要包含最左列條件.

7.索引樹高度

通常建議3-4層爲準,3層b樹,2000w+

a.數據行多

 分區表
 按期歸檔:通常按照時間字段,按期歸檔到歷史庫中。pt-archiver
 分庫分表:分佈式

b.索引列長度過長

 前綴索引

c.數據類型

 足夠
 簡短的
 合適的

影響索引樹高度的因素

  1. 數據量級,解決方法:

    分區表:

    按期歸檔:通常按照時間字段,按期歸檔到歷史庫中, pt-archiver

    分庫,分表,分佈式

  2. 索引列值過長,解決方法:

    業務容許,儘可能選擇字符長度短的列做爲索引列

    業務不容許,採用前綴索引.

  3. 數據類型:

    變長長度字符串,使用了char,解決方案:變長長度字符串使用 varchar

    enum使用短字符代替長字符

     enum ('山東','河北','黑龍江','吉林','遼寧','陝西'......)
     enum (  1      2      3      4      5     6       )

8.回表問題

8.1回表是什麼?

輔助索引掃描以後,獲得ID,再回到聚簇索引查找的過程

8.2回表會帶來什麼問題?

 回表是隨機IO,會致使IO的次數(IOPS)和量(吞吐量)增長
 IOPS(Input/Output Operations Per Second):每秒的讀寫次數,用於衡量隨機訪問的性能
 吞吐量:單位時間內成功地傳送數據的數量(例如 200M/s)

8.3減小回表的方法

 a.建索引使用聯合索引(覆蓋),儘量多將查詢條件的數據包含聯合索引中
 b.精細查詢條件(業務方面>and < ,limit)
 c.查詢條件要符合聯合索引規則。覆蓋的列越多越好。
 a b   c
 where a=   and b=     and   c=

9.擴展項: 索引自優化AHI(自適應hash索引)\change buffer

AHI : 索引的索引. 爲內存中的熱點索引頁,作了一個HASH索引表,可以快速找到須要的索引頁地址. https://dev.mysql.com/doc/refman/8.0/en/innodb-adaptive-hash.html https://dev.mysql.com/doc/refman/8.0/en/innodb-architecture.html change buffer : 對於輔助索引的變化,不會當即更新到索引中.暫存至change buffer . https://dev.mysql.com/doc/refman/8.0/en/innodb-change-buffer.html

10.分析執行計劃

10.1 分析執行計劃定義:

 優化器(算法)最終得出的,代價最低的,SQL語句的執行方案.

10.2 爲何要分析執行計劃?

 場景一: 分析比較慢的語句.
 場景二: 上線新業務,可能會包含不少select  update  delete...,提早發現問題.

10.3 如何抓取執行計劃

a. 抓取目標

 select  update  delete

b. 方法

 mysql> desc   select * from world.city where countrycode='CHN';
 mysql> explain   select * from world.city where countrycode='CHN';
 +----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
 | id   | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra |
 | ---- | ----------- | ----- | ---------- | ---- | ------------- | ---- | ------- | ---- | ---- | -------- | ----- |
 |      |             |       |            |      |               |      |         |      |      |          |       |
 +----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
 | 1    | SIMPLE | city | NULL | ref  | CountryCode | CountryCode | 3    | const | 363  | 100.00 | NULL |
 | ---- | ------ | ---- | ---- | ---- | ----------- | ----------- | ---- | ----- | ---- | ------ | ---- |
 |      |        |      |      |      |             |             |      |       |      |        |      |
 +----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+

 

type

從上往下效率依次升高

  1. ALL:全表掃描,不走索引

 ① 查詢全表
 DESC SELECT * FROM world.city;
 ② 查詢條件出現如下語句
 DESC SELECT * FROM world.city WHERE countrycode LIKE '%HN';
 ③ 輔助索引列的查詢條件出現如下語句
 DESC SELECT * FROM world.city WHERE countrycode != 'CHN';
 DESC SELECT * FROM world.city WHERE countrycode <> 'CHN';
 DESC SELECT * FROM world.city WHERE countrycode NOT IN ('CHN','USA' );

注意:對於彙集索引,使用例③語句,依然會走RANGE索引

 DESC SELECT * FROM world.city WHERE id <> 10;
 DESC SELECT * FROM world.city WHERE id != 10;
 DESC SELECT * FROM world.city WHERE id NOT IN ('1','2' );
  1. INDEX:全索引掃描

 1. 查詢須要獲取整顆索引樹,才能獲得想要的結果時
 DESC  SELECT countrycode FROM world.city;
 2. 聯合索引中,任何一個非最左列做爲查詢條件時
 idx_a_b_c(a,b,c) ---> a ab abc
 SELECT * FROM t1 WHERE b
 SELECT * FROM t1 WHERE c

RANGE:索引範圍掃描

輔助索引列 > < >= <= LIKE IN OR

 DESC SELECT * FROM world.city WHERE Population > 1000000;
 DESC SELECT * FROM world.city WHERE Population < 100;
 DESC SELECT * FROM world.city WHERE Population >= 1000000;
 DESC SELECT * FROM world.city WHERE Population <= 100;
 DESC SELECT * FROM world.city WHERE countrycode LIKE 'CH%';
 DESC SELECT * FROM world.city WHERE countrycode IN ('CHN','USA');
 DESC SELECT * FROM world.city WHERE Population < 100 OR Population > 1000000;
  • 主鍵列 > < >= <= IN OR <> != NOT IN

     DESC SELECT * FROM world.city WHERE id<5;
     DESC SELECT * FROM world.city WHERE id>5;
     DESC SELECT * FROM world.city WHERE id<=5;
     DESC SELECT * FROM world.city WHERE id>=5;
     DESC SELECT * FROM world.city WHERE id IN ('1','2' );
     DESC SELECT * FROM world.city WHERE id < 5 OR id > 10;
     DESC SELECT * FROM world.city WHERE id <> 10;
     DESC SELECT * FROM world.city WHERE id != 10;
    

     

     DESC SELECT * FROM world.city WHERE id NOT IN ('1','2' );

注意:

IN不能享受到B+樹的雙向指針

 DESC SELECT * FROM world.city WHERE countrycode IN ('CHN','USA');

能夠改寫爲

 DESC SELECT * FROM world.city WHERE countrycode='CHN'
 UNION ALL
 SELECT * FROM world.city WHERE countrycode='USA';

具體效果看壓測結果

若是索引列的重複值太多, 改寫爲UNION沒有意義

  1. ref:非惟一性索引,等值查詢

 DESC SELECT * FROM world.city WHERE countrycode='CHN';
  1. eq_ref:在多表鏈接查詢時,鏈接條件使用了主鍵或惟一鍵索引(uk pK)的非驅動表

 DESC SELECT b.name,a.name 
 FROM city a
 JOIN country b
 ON a.countrycode=b.code
 WHERE a.population <100;
  1. const(system):惟一索引的等值查詢

 DESC SELECT * FROM world.city WHERE id=10;

10.4 如何分析執行計劃

 table : 操做的表
 type   : 操做類型(全表\索引) ,ALL index range ref eq_ref const(system)
 possible_keys : 有可能用的索引
 key   : 真正要用是哪索引
 key_len: 索引覆蓋長度(聯合索引)
 rows   : 預估須要掃描的行數
 Extra : using where using index using index condition using filesort sort   using temp

EXPLAIN

1.經過EXPLAIN來分析索引的有效性

2.EXPLAIN SELECT clause

獲取查詢執行計劃信息,用來查看查詢優化器如何執行查詢

輸出信息說明: 參考 https://dev.mysql.com/doc/refman/5.7/en/explain-output.html

id: 當前查詢語句中,每一個SELECT語句的編號

 複雜類型的查詢有三種:
 簡單子查詢
 用於FROM中的子查詢
 聯合查詢:UNION

注意:UNION查詢的分析結果會出現一個額外匿名臨時表

3.select_type:簡單查詢爲SIMPLE 複雜查詢:

 SUBQUERY 簡單子查詢
 PRIMARY 最外面的SELECT
 DERIVED 用於FROM中的子查詢
 UNION UNION語句的第一個以後的SELECT語句
 UNION RESULT 匿名臨時表

table:SELECT語句關聯到的表

4.type:關聯類型或訪問類型,即MySQL決定的如何去查詢表中的行的方式,如下順序,性能從低到高

 ALL: 全表掃描
 index:根據索引的次序進行全表掃描;若是在Extra列出現「Using index」表示了使用覆蓋索引,而非全表掃描
 range:有範圍限制的根據索引實現範圍掃描;掃描位置始於索引中的某一點,結束於另外一點
 ref: 根據索引返回表中匹配某單個值的全部行
 eq_ref:僅返回一個行,但與須要額外與某個參考值作比較
 const, system: 直接返回單個行

5.possible_keys:查詢可能會用到的索引 6.key: 查詢中使用到的索引 7.key_len: 在索引使用的字節數

explain(desc)使用場景(面試題)

 題目意思:  咱們公司業務慢,請你從數據庫的角度分析緣由
 1.mysql出現性能問題,我總結有兩種狀況:
 (1)應急性的慢:忽然夯住
 應急狀況:數據庫hang(卡了,資源耗盡)
 處理過程:
 1.show processlist; 獲取到致使數據庫hang的語句
 2. explain 分析SQL的執行計劃,有沒有走索引,索引的類型狀況
 3. 建索引,改語句
 (2)一段時間慢(持續性的):
 (1)記錄慢日誌slowlog,分析slowlog
 (2)explain 分析SQL的執行計劃,有沒有走索引,索引的類型狀況
 (3)建索引,改語句

10.5 type 詳解

  • a. ALL 全表掃描

 mysql> explain  select * from world.city ;
 mysql> explain  select * from world.city where countrycode!='chn';
 mysql> explain   select * from world.city where countrycode like '%hn%';
 mysql> explain   select * from world.city where countrycode not in ('chn','usa');
  • b. index 全索引掃描

 須要掃描整顆索引樹,才能獲得想要的結果. 
 desc select id ,countrycode from world.city;
  • c. range 索引範圍 是咱們應用索引優化的底線,也是應用最多的.

 mysql> desc select * from city where id<10;
 mysql> desc select * from city where countrycode like 'ch%';
 mysql> desc select * from city where countrycode in ('CHN','USA');

SQL 改寫爲:

 desc 
 select * from city where countrycode='CHN' union  all  select * from city where countrycode='USA'
 mysqlslap --defaults-file=/etc/my.cnf \
 --concurrency=100 --iterations=1 --create-schema='world' \
 --query=" select * from city where countrycode in ('CHN','USA')" engine=innodb \
 --number-of-queries=2000 -uroot -p123 -verbose
 mysqlslap --defaults-file=/etc/my.cnf \
 --concurrency=100 --iterations=1 --create-schema='world' \
 --query="select * from city where countrycode='CHN' union  all  select * from city where countrycode='USA'" engine=innodb \
 --number-of-queries=2000 -uroot -p123 -verbose

小經驗:

 索引列基數多少 +  壓測結果,最終評估是否須要使用union .

d. ref : 輔助索引等值查詢

 mysql> desc  select * from city where countrycode='CHN';

e. eq_ref : 非驅動表,鏈接條件是主鍵或惟一鍵.

補充:多表鏈接時,小結果集的表驅動大表

優化會自動判斷查詢語句中的誰做爲驅動表更合適,有可能會出現選擇錯誤

咱們能夠經過left join強制驅動表干預執行計劃。

如何在任務判斷一條鏈接語句中,誰是驅動表

 

a. 優化器的判斷

 mysql>select count(*) from city  where city.population<100000;  
 mysql> desc select * from city join country on city.countrycode=country.code where city.population<100000  and country.SurfaceArea(國土面積 )>10000000;

b. 如何人爲判斷一條鏈接語句中,誰是驅動表 (工做中經常使用)

 mysql> select count(*) from country where country.SurfaceArea>10000000;
 +----------+
 | count(*) |
 +----------+
 |    2     |
 +----------+
 1 row in set (0.00 sec)
 ​
 mysql> select count(*) from city where city.population<100000 ;
 +----------+
 | count(*) |
 +----------+
 |   517    |
 +----------+

補充: 若是 where後的列中都有索引,會選擇結果集小的做爲驅動表.

c. 壓測.

 mysql> desc select a.name, b.name,a.countrycode,a.population  
 from city  as a 
 join country as b 
 on a.countrycode=b.code where a.population<100;

f. const(system)

 mysql> desc select * from city where id=1;

10.6 key_len詳解

a.介紹

(聯合)索引覆蓋長度

idx(a,b,c) ------->a(10) b(20) c(30)

b.如何計算索引列的key_len

key_len和每一個列的最大預留長度(字節)有關。

數據類型 utf8mb4 沒有 not null
tinyint 1 1
int 4 1
char(n) 4*n 1
varchar(n) 4*n+2 1

mysql> desc select * from t100w where num=10 and k1 ='aa' and k2='bb';

+----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+ | 1 | SIMPLE | t100w | NULL | ref | idx_k2,idx | idx_k2 | 17 | const | 1 | 5.00 | Using where | +----+-------------+-------+------------+------+---------------+--------+---------+-------+------+----------+-------------+ 1 row in set, 1 warning (1.65 sec)

c.聯合索引應用細節

idx(a,b,c)

徹底覆蓋

a= and b= and c=
a= and c= and b= 等值打亂順序的
a= and b= and c範圍
a= anb b字符範圍 and c=

部分覆蓋

a= and b=  
a=    
a= and b= and c=
a= and b數字範圍 and c=

徹底不覆蓋 bc ----> bc b

b

c

bc

cb

優化案例: idx(k1,num,k2)

 mysql> desc select * from t100w where k1='Vs' and num<27779  and k2='mnij' 

優化方案: 修改索引爲idx(k1,k2,num)

mysql> desc select * from t100w where k1='Vs'  and k2='mnij' 

10.7 extra

 using index  使用了索引覆蓋掃描 
 using where 使用where回表掃描數據行,說明目標表的索引沒有設計好.
 a. table ----> 獲取到出問題的表
 b. 看原始查詢語句中的where條件列
 c. 查詢列的索引狀況-----> show index from t1;
 d. 按需優化索引.
 using filesort  使用了額外排序. 
 a. table ---->獲取到出問題的表
 b. 查看原始語句中的: order by   group by   distinct
 c. 查看列的索引狀況
 d. 按需優化索引.

優化案例:

 mysql> desc select *from city where countrycode='CHN' order by population;
 +----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+----------------+
 | id | select_type | table | partitions | type | possible_keys | key         | key_len | ref   | rows | filtered | Extra         |
 +----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+----------------+
 |  1 | SIMPLE     | city | NULL       | ref | CountryCode   | CountryCode | 3       | const |  363 |   100.00 | Using filesort |
 +----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+----------------+
 mysql> show index from city;
 +-------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
 | Table | Non_unique | Key_name   | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression |
 +-------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
 | city |          0 | PRIMARY     |            1 | ID         | A         |        4188 |     NULL |   NULL |     | BTREE     |         |               | YES     | NULL       |
 | city |          1 | CountryCode |            1 | CountryCode | A         |         232 |     NULL |   NULL |     | BTREE     |         |               | YES     | NULL       |
 +-------+------------+-------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+------------+
 mysql> alter table city add index idx(population);
 mysql> desc select *from city where countrycode='CHN' order by population;
 +----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+----------------+
 | id | select_type | table | partitions | type | possible_keys | key         | key_len | ref   | rows | filtered | Extra         |
 +----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+----------------+
 |  1 | SIMPLE     | city | NULL       | ref | CountryCode   | CountryCode | 3       | const |  363 |   100.00 | Using filesort |
 +----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+----------------+
 mysql> alter table city add index idx_c_p(countrycode,population);
 Query OK, 0 rows affected (0.02 sec)
 Records: 0 Duplicates: 0 Warnings: 0
 mysql> desc select *from city where countrycode='CHN' order by population;
 +----+-------------+-------+------------+------+---------------------+---------+---------+-------+------+----------+-------+
 | id | select_type | table | partitions | type | possible_keys       | key     | key_len | ref   | rows | filtered | Extra |
 +----+-------------+-------+------------+------+---------------------+---------+---------+-------+------+----------+-------+
 |  1 | SIMPLE     | city | NULL       | ref | CountryCode,idx_c_p | idx_c_p | 3       | const |  363 |   100.00 | NULL |
 +----+-------------+-------+------------+------+---------------------+---------+---------+-------+------+----------+-------+

查看冗餘索引

 mysql> select table_schema,table_name , redundant_index_name , redundant_index_columns  from sys.schema_redundant_indexes;
 using temp   --->  
 a. 條件範圍是否是過大. 
 b. having  order by 額外排序
 c. 子查詢

大概率開發須要改寫語句了.

亂序查詢

 

 

 

 

 

 

 輔助索引查找過程

 

 

 

 

11.擴展項: 關於索引的優化器算法:ICP \ MRR

11.1 ICP : Index Condition Pushdown

優化器算法: a. 查詢優化器算法:

 mysql> select @@optimizer_switch;

b.設置優化器算法:

 mysql> set global optimizer_switch='index_condition_pushdown=off';

hits方式: https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html 配置文件: my.cnf 例子 :

 mysql> set global optimizer_switch='index_condition_pushdown=off';
 mysql> desc select * from t100w where k1='Vs'  and num<27779 and k2='mnij';
 +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
 | id | select_type | table | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
 +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
 |  1 | SIMPLE      | t100w | NULL       | range | idx           | idx  | 14      | NULL |   29 |    10.00 | Using where |
 +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-------------+
 1 row in set, 1 warning (0.00 sec)
 mysql> set global optimizer_switch='index_condition_pushdown=on';
 ​
 mysql> desc select * from t100w where k1='Vs'  and num<27779 and k2='mnij';
 +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
 | id | select_type | table | partitions | type  | possible_keys | key  | key_len | ref  | rows | filtered | Extra                 |
 +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
 |  1 | SIMPLE      | t100w | NULL       | range | idx           | idx  | 14      | NULL |   29 |    10.00 | Using index condition |
 +----+-------------+-------+------------+-------+---------------+------+---------+------+------+----------+-----------------------+
 1 row in set, 1 warning (0.00 sec)

壓測: a. 開ICP 20000次語句壓測 索引順序不調整

 mysqlslap --defaults-file=/etc/my.cnf \
 --concurrency=100 --iterations=1 --create-schema='test' \
 --query=" select * from t100w where k1='Vs'  and num<27779 and k2='mnij'" engine=innodb \
 --number-of-queries=20000 -uroot -p123 -verbose
 4.580 seconds
 4.569 seconds
 4.431 seconds
 4.433 seconds
 4.391 seconds

b. 關 ICP 20000次語句壓測 索引順序不調整

 5.327 
 5.516
 5.267
 5.330
 5.293 seconds

c. 索引順序優化 壓測

 4.251
 4.143

11.2 MRR

 MRR(Mean reciprocal rank)是一個國際上通用的對搜索算法進行評價的機制,即第一個結果匹配,分數爲1,第二個匹配分數爲0.5,第n個匹配分數爲1/n,若是沒有匹配的句子分數爲0。最終的分數爲全部得分之和。

https://dev.mysql.com/doc/refman/8.0/en/mrr-optimization.html

12.索引應用規範

 業務
 1.產品的功能
 2.用戶的行爲
 "熱"查詢語句 --->較慢--->slowlog
 "熱"數據

12.1 創建索引的原則(DBA運維規範)

 (1) 必需要有主鍵,業務無關列。
 (2) 常常作爲where條件列  order by  group by  join on, distinct 的條件(業務:產品功能+用戶行爲)
 (3) 最好使用惟一值多的列做爲索引列,若是索引列重複值較多,能夠考慮使用聯合索引
 (4) 列值長度較長的索引列,咱們建議使用前綴索引.
 mysql> select count(distinct left(name,19)) from city;
 (5) 下降索引條目,一方面不要建立沒用索引,不常使用的索引清理,percona toolkit(xxxxx)
 (6) 索引維護要避開業務繁忙期,建議用pt-osc。 
 (7) 聯合索引最左原則

12.2 不走索引的狀況(開發規範)

12.2.1 沒有查詢條件,或者查詢條件沒有創建索引

 select * from t1  ;
 select * from t1 where id=1001 or 1=1;

做業: SQL審覈和審計. yearning.io github, inception

12.2.2 查詢結果集是原表中的大部分數據,應該是15-25%以上。

查詢的結果集,超過了總數行數25%,優化器以爲就沒有必要走索引了。 MySQL的預讀功能有關。

能夠經過精確查找範圍,達到優化的效果。

 1000000
 500000 and

12.2.3 索引自己失效,統計信息不真實(過舊)

 索引有自我維護的能力。
 對於表內容變化比較頻繁的狀況下,有可能會出現索引失效。
 通常是刪除重建

現象:

面試題

有一條select語句日常查詢時很快,忽然有一天很慢,會是什麼緣由 select? --->索引失效,統計數據不真實

 innodb_index_stats  索引統計信息
 innodb_table_stats  表的統計信息
 mysql> ANALYZE TABLE world.city;

12.2.4 查詢條件使用函數在索引列上,或者對索引列進行運算,運算包括(+,-,*,/,! 等)

例子:

 錯誤的例子:select * from test where id-1=9;
 正確的例子:select * from test where id=10;
 算術運算
 函數運算
 子查詢

12.2.5 隱式轉換致使索引失效.這一點應當引發重視.也是開發中常常會犯的錯誤.

 這樣會致使索引失效. 錯誤的例子
 mysql> alter table tab add index inx_tel(telnum);
 Query OK, 0 rows affected (0.03 sec)
 Records: 0  Duplicates: 0  Warnings: 0
 mysql> desc tab;
 +--------+-------------+------+-----+---------+-------+
 | Field  | Type        | Null | Key | Default | Extra |
 +--------+-------------+------+-----+---------+-------+
 | id    | int(11)    | YES  |    | NULL    |      |
 | name  | varchar(20) | YES  |    | NULL    |      |
 | telnum | varchar(20) | YES  | MUL | NULL    |      |
 +--------+-------------+------+-----+---------+-------+
 3 rows in set (0.01 sec)
 mysql> select * from tab where telnum='1333333';
 +------+------+---------+
 | id  | name | telnum  |
 +------+------+---------+
 |    1 | a    | 1333333 |
 +------+------+---------+
 1 row in set (0.00 sec)
 mysql> select * from tab where telnum=1333333;
 +------+------+---------+
 | id  | name | telnum  |
 +------+------+---------+
 |    1 | a    | 1333333 |
 +------+------+---------+
 1 row in set (0.00 sec)
 mysql> explain  select * from tab where telnum='1333333';
 +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
 | id | select_type | table | type | possible_keys | key    | key_len | ref  | rows | Extra                |
 +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
 ​
 |  1 | SIMPLE      | tab  | ref  | inx_tel      | inx_tel | 63      | const |    1 | Using index condition |
 +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
 1 row in set (0.00 sec)
 mysql> explain  select * from tab where telnum=1333333;
 +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
 | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra      |
 +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
 |  1 | SIMPLE      | tab  | ALL  | inx_tel      | NULL | NULL    | NULL |    2 | Using where |
 +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
 1 row in set (0.00 sec)
 mysql> explain  select * from tab where telnum=1555555;
 +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
 | id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra      |
 +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
 |  1 | SIMPLE      | tab  | ALL  | inx_tel      | NULL | NULL    | NULL |    2 | Using where |
 +----+-------------+-------+------+---------------+------+---------+------+------+-------------+
 1 row in set (0.00 sec)
 mysql> explain  select * from tab where telnum='1555555';
 +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
 | id | select_type | table | type | possible_keys | key    | key_len | ref  | rows | Extra                |
 +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
 |  1 | SIMPLE      | tab  | ref  | inx_tel      | inx_tel | 63      | const |    1 | Using index condition |
 +----+-------------+-------+------+---------------+---------+---------+-------+------+-----------------------+
 1 row in set (0.00 sec)
 mysql> desc select * from b where telnum=110;
 mysql> desc select * from b where telnum='110';

12.2.6 < > ,not in 不走索引(輔助索引)

 EXPLAIN  SELECT * FROM teltab WHERE telnum  <> '110';
 EXPLAIN  SELECT * FROM teltab WHERE telnum  NOT IN ('110','119');
 mysql> select * from tab where telnum <> '1555555';
 +------+------+---------+
 | id  | name | telnum  |
 +------+------+---------+
 |    1 | a    | 1333333 |
 +------+------+---------+
 1 row in set (0.00 sec)
 mysql> explain select * from tab where telnum <> '1555555';
 單獨的>,<,in 有可能走,也有可能不走,和結果集有關,儘可能結合業務添加limit
 or或in  儘可能改爲union
 EXPLAIN  SELECT * FROM teltab WHERE telnum  IN ('110','119');
 改寫成:
 EXPLAIN SELECT * FROM teltab WHERE telnum='110'
 UNION ALL
 SELECT * FROM teltab WHERE telnum='119

12.2.7 like "%_" 百分號在最前面不走

 EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '31%'  走range索引掃描
 EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '%110'  不走索引
 %linux%類的搜索需求,可使用elasticsearch+mongodb 專門作搜索服務的數據庫產品

Optimizer: 查詢優化器

SQL語句在查詢以前會使用查詢優化器對查詢進行優化。就是優化客戶端請求的 query(sql語句) ,根據客戶端請求的 query 語句,和數據庫中的一些統計信息,在一系列算法的基礎上進行分析,得出一個最優的策略,告訴後面的程序如何取得這個 query 語句的結果

他使用的是「選取-投影-聯接」策略進行查詢。 用一個例子就能夠理解: select uid,name from user where gender = 1; 這個select 查詢先根據where 語句進行選取,而不是先將表所有查詢出來之後再進行gender過濾 這個select查詢先根據uid和name進行屬性投影,而不是將屬性所有取出之後再進行過濾 將這兩個查詢條件聯接起來生成最終查詢結果

Cache和Buffer: 查詢緩存

他的主要功能是將客戶端提交 給MySQL 的 Select 類 query 請求的返回結果集 cache 到內存中,與該 query 的一個 hash 值 作 一個對應。該 Query 所取數據的基表發生任何數據的變化以後, MySQL 會自動使該 query 的Cache 失效。在讀寫比例很是高的應用系統中, Query Cache 對性能的提升是很是顯著的。固然它對內存的消耗也是很是大的。

若是查詢緩存有命中的查詢結果,查詢語句就能夠直接去查詢緩存中取數據。這個緩存機制是由一系列小緩存組成的。好比表緩存,記錄緩存,key緩存,權限緩存等

存儲引擎接口

存儲引擎接口模塊能夠說是 MySQL 數據庫中最有特點的一點了。目前各類數據庫產品中,基本上只有 MySQL 能夠實現其底層數據存儲引擎的插件式管理。這個模塊實際上只是 一個抽象類,但正是由於它成功地將各類數據處理高度抽象化,才成就了今天 MySQL 可插拔存儲引擎的特點。

能夠看出,MySQL區別於其餘數據庫的最重要的特色就是其插件式的表存儲引擎。MySQL插件式的存儲引擎架構提供了一系列標準的管理和服務支持,這些標準與存儲引擎自己無關,多是每一個數據庫系統自己都必需的,如SQL分析器和優化器等,而存儲引擎是底層物理結構的實現,每一個存儲引擎開發者均可以按照本身的意願來進行開發。 注意:存儲引擎是基於表的,而不是數據庫。

查詢的執行路徑

 

 

MySQL 整個查詢執行過程,總的來講分爲 5 個步驟 :

 1> 客戶端向 MySQL 服務器發送一條查詢請求
 2> 服務器首先檢查查詢緩存,若是命中緩存,則馬上返回存儲在緩存中的結果,不然進入下一階段
 3> 服務器進行 SQL解析、預處理、再由優化器生成對應的執行計劃
 4> MySQL 根據執行計劃,調用存儲引擎的 API來執行查詢
 5> 將結果返回給客戶端,同時緩存查詢結果
相關文章
相關標籤/搜索