一.擴展php
https://www.yiibai.com/mysql/stored-function.htmlhtml
定義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
定義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)
定義面試
指子查詢返回的結果集是 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)
定義:
指子查詢返回的結果集是一行 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)
指子查詢返回的結果集是 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 優化。
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):
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參數,選中的參數用','鏈接後返回
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'
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
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列設置的第一個發生的值
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)
若在沒使用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):樣本標準差
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語句。
自身的存儲過程稱爲遞歸存儲過程。大多數數據庫管理系統支持遞歸存儲過程。 可是,MySQL不支持它。
MySQL是最受歡迎的開源RDBMS,被社區和企業普遍使用。 然而,在它發佈的第一個十年期間,它不支持存儲過程,
一般存儲過程有助於提升應用程序的性能。當建立,存儲過程被編譯以後,就存儲在數據庫中。 可是,MySQL實現的存儲過程略有不一樣。 MySQL存儲過程按需編譯。 在編譯存儲過程以後,MySQL將其放入緩存中。 MySQL爲每一個鏈接維護本身的存儲過程高速緩存。 若是應用程序在單個鏈接中屢次使用存儲過程,則使用編譯版本,不然存儲過程的工做方式相似於查詢。
存儲過程有助於減小應用程序和數據庫服務器之間的流量,由於應用程序沒必要發送多個冗長的SQL語句,而只能發送存儲過程的名稱和參數。
存儲的程序對任何應用程序都是可重用的和透明的。 存儲過程將數據庫接口暴露給全部應用程序,以便開發人員沒必要開發存儲過程當中已支持的功能。
存儲的程序是安全的。 數據庫管理員能夠向訪問數據庫中存儲過程的應用程序授予適當的權限,而不向基礎數據庫表提供任何權限。
除了這些優勢以外,存儲過程有其自身的缺點,在數據庫中使用它們以前,您應該注意這些缺點。
若是使用大量存儲過程,那麼使用這些存儲過程的每一個鏈接的內存使用量將會大大增長。 此外,若是您在存儲過程當中過分使用大量邏輯操做,則CPU使用率也會增長,由於數據庫服務器的設計不當於邏輯運算。
存儲過程的構造使得開發具備複雜業務邏輯的存儲過程變得更加困難。
很難調試存儲過程。只有少數數據庫管理系統容許您調試存儲過程。不幸的是,MySQL不提供調試存儲過程的功能。
開發和維護存儲過程並不容易。開發和維護存儲過程一般須要一個不是全部應用程序開發人員擁有的專業技能。這可能會致使應用程序開發和維護階段的問題。
MySQL存儲過程有本身的優勢和缺點。開發應用程序時,您應該決定是否應該或不該該根據業務需求使用存儲過程。
SQL觸發器是存儲在數據庫目錄中的一組SQL語句。每當與表相關聯的事件發生時,即會執行或觸發SQL觸發器,例如插入,更新或刪除。
SQL觸發器是一種特殊類型的
瞭解SQL觸發器的優缺點很是重要,以便您能夠適當地使用它。在如下部分中,咱們將討論使用SQL觸發器的優缺點。
SQL觸發器提供了檢查數據完整性的替代方法。
SQL觸發器能夠捕獲數據庫層中業務邏輯中的錯誤。
SQL觸發器提供了
SQL觸發器對於審覈表中數據的更改很是有用。
SQL觸發器只能提供擴展驗證,而且沒法替換全部驗證。一些簡單的驗證必須在應用層完成。 例如,您可使用JavaScript或服務器端使用服務器端腳本語言(如
從客戶端應用程序調用和執行SQL觸發器不可見,所以很難弄清數據庫層中發生的狀況。
SQL觸發器可能會增長數據庫服務器的開銷。
在MySQL中,觸發器是一組SQL語句,當對相關聯的表上的數據進行更改時,會自動調用該語句。 觸發器能夠被定義爲在
BEFORE INSERT
- 在數據插入表以前被激活觸發器。
AFTER INSERT
- 在將數據插入表以後激活觸發器。
BEFORE UPDATE
- 在表中的數據更新以前激活觸發器。
AFTER UPDATE
- 在表中的數據更新以後激活觸發器。
BEFORE DELETE
- 在從表中刪除數據以前激活觸發器。
AFTER DELETE
- 從表中刪除數據以後激活觸發器。
可是,從MySQL 5.7.2+版本開始,能夠
當使用不使用INSERT
,DELETE
或UPDATE
語句更改表中數據的語句時,不會調用與表關聯的觸發器。 例如,
有些語句使用了後臺的INSERT
語句,如
必需要爲與表相關聯的每一個觸發器使用惟一的名稱。能夠爲不一樣的表定義相同的觸發器名稱,這是一個很好的作法。
應該使用如下命名約定命名觸發器:
(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在數據目錄中存儲觸發器,例如:/data/yiibaidb/
,並使用名爲tablename.TRG
和triggername.TRN
的文件:
tablename.TRG
文件將觸發器映射到相應的表。
triggername.TRN
文件包含觸發器定義。
能夠經過將觸發器文件複製到備份文件夾來備份MySQL觸發器。也能夠
MySQL觸發器覆蓋標準SQL中定義的全部功能。 可是,在應用程序中使用它們以前,您應該知道一些限制。
MySQL觸發器不能:
使用在SHOW
,LOAD DATA
,LOAD TABLE
,
使用隱式或明確提交或回滾的語句,如COMMIT
,ROLLBACK
,START TRANSACTION
,
使用
使用動態SQL語句。
爲了建立一個新的觸發器,可使用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
關鍵字。
觸發事件能夠是INSERT
,UPDATE
或DELETE
。此事件致使觸發器被調用。 觸發器只能由一個事件調用。要定義由多個事件調用的觸發器,必須定義多個觸發器,每一個事件一個觸發器。
觸發器必須與特定表關聯。沒有表觸發器將不存在,因此必須在ON
關鍵字以後指定表名。
將SQL語句放在BEGIN
和END
塊之間。這是定義觸發器邏輯的位置。
下面咱們將在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
關鍵字來訪問受觸發器影響的行的employeeNumber
和lastname
列。
請注意,在爲
而後,要查看當前數據庫中的全部觸發器,請使用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
子句以後指定FOLLOWS
或PRECEDES
。以下說明 -
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
咱們來看如何一個在表中的同一個事件和動做上,建立多個觸發器的例子。
下面將使用
首先,使用
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 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 products SET msrp = 95.3 WHERE productCode = 'S10_1678'; SQL
其次,分別從price_logs
和user_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
該語句容許您查看觸發器的內容及其元數據,例如:關聯表名和定義器,這是建立觸發器的
若是要檢索指定數據庫中的全部觸發器,則須要使用如下
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
在特定數據庫中顯示觸發器的另外一種方法是使用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
:指定事件,例如,調用觸發器的INSERT
,UPDATE
或DELETE
。
Table
:指定觸發器與例如相關聯的表,如employees
表。
Statement
:存儲調用觸發器時要執行的語句或複合語句。
Timing
:接受兩個值:BEFORE
和AFTER
,它指定觸發器的激活時間。
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使用一個名爲事件調度線程的特殊線程來執行全部調度的事件。能夠經過執行如下命令來查看事件調度程序線程的狀態:
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
建立事件與建立其餘數據庫對象(如存儲過程或觸發器)相似。事件是一個包含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
請注意,使用STARTS
和ENDS
子句定義事件的有效期。等待個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
要刪除現有事件,請使用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
語句的各類功能。
如下語句建立一個事件,每分鐘將一條新記錄插入到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語句定義。 您可使用數據庫視圖來隱藏最終用戶和外部應用程序的基礎表的複雜性。 經過數據庫視圖,您只需使用簡單的SQL語句,而不是使用具備多個鏈接的複雜的SQL語句。
數據庫視圖有助於限制對特定用戶的數據訪問。 您可能不但願全部用戶均可以查詢敏感數據的子集。可使用數據庫視圖將非敏感數據僅顯示給特定用戶組。
數據庫視圖提供額外的安全層。 安全是任何關係數據庫管理系統的重要組成部分。 數據庫視圖爲數據庫管理系統提供了額外的安全性。 數據庫視圖容許您建立只讀視圖,以將只讀數據公開給特定用戶。 用戶只能以只讀視圖檢索數據,但沒法更新。
數據庫視圖啓用計算列。 數據庫表不該該具備計算列,但數據庫視圖能夠這樣。 假設在orderDetails
表中有quantityOrder
(產品的數量)和priceEach
(產品的價格)列。 可是,orderDetails
表沒有一個列用來存儲訂單的每一個訂單項的總銷售額。若是有,數據庫模式不是一個好的設計。 在這種狀況下,您能夠建立一個名爲total
的計算列,該列是quantityOrder
和priceEach
的乘積,以表示計算結果。當您從數據庫視圖中查詢數據時,計算列的數據將隨機計算產生。
數據庫視圖實現向後兼容。 假設你有一箇中央數據庫,許多應用程序正在使用它。 有一天,您決定從新設計數據庫以適應新的業務需求。刪除一些表並建立新的表,而且不但願更改影響其餘應用程序。在這種狀況下,能夠建立與將要刪除的舊錶相同的模式的數據庫視圖。
除了上面的優勢,使用數據庫視圖有幾個缺點:
性能:從數據庫視圖查詢數據可能會很慢,特別是若是視圖是基於其餘視圖建立的。
表依賴關係:將根據數據庫的基礎表建立一個視圖。每當更改與其相關聯的表的結構時,都必須更改視圖。
在MySQL中,視圖的幾乎特徵符合SQL:2003標準。 MySQL以兩種方式處理對視圖的查詢:
第一種方式,MySQL會根據視圖定義語句建立一個
第二種方式,MySQL將傳入查詢與查詢定義爲一個查詢並執行組合查詢。
MySQL支持版本系統的視圖。每次
MySQL容許基於其餘視圖建立視圖。在視圖定義的
不能在視圖上建立
在MySQL 5.7.7以前版本,是不能在SELECT
語句的FROM
子句中使用
若是刪除或
一個簡單的視圖能夠
MySQL不像:
要在MySQL中建立一個新視圖,可使用CREATE VIEW
語句。 在MySQL中建立視圖的語法以下:
CREATE
[ALGORITHM = {MERGE | TEMPTABLE | UNDEFINED}]
VIEW [database_name].[view_name]
AS
[SELECT statement]
SQL
下面咱們來詳細的查看上面的語法。
算法屬性容許您控制MySQL在建立視圖時使用的機制,MySQL提供了三種算法:MERGE
,TEMPTABLE
和UNDEFINED
。
使用MERGE
算法,MySQL首先將輸入查詢與定義視圖的
使用TEMPTABLE
算法,MySQL首先根據定義視圖的SELECT
語句
當您建立視圖而不指定顯式算法時,UNDEFINED
是默認算法。 UNDEFINED
算法使MySQL能夠選擇使用MERGE
或TEMPTABLE
算法。MySQL優先使用MERGE
算法進行TEMPTABLE
算法,由於MERGE
算法效率更高。
在數據庫中,視圖和表共享相同的命名空間,所以視圖和表不能具備相同的名稱。 另外,視圖的名稱必須遵循表的命名規則。
在SELECT
語句中,能夠從數據庫中存在的任何表或視圖查詢數據。SELECT
語句必須遵循如下幾個規則:
SELECT
語句能夠在
SELECT
語句不能引用任何變量,包括局部變量,用戶變量和會話變量。
SELECT
語句不能引用準備語句的參數。
請注意,
SELECT
語句不須要引用任何表。
建立簡單的視圖
咱們來看看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
使用鏈接表建立視圖
如下是使用
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中,視圖不只是可查詢的,並且是可更新的。這意味着您可使用
可是,要建立可更新
SELECT子句中的
引用FROM
子句中的不可更新視圖
僅引用文字值
對基表的任何列的屢次引用
若是使用
請注意,有時可使用
讓咱們先來看看如何建立一個可更新的視圖。
首先,基於
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
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
語句來刪除id
爲3
的行。
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
如上面所示,ID
爲3
的行在基表中被刪除。
有時候,
下面說明了WITH CHECK OPTION
子句的語法 -
CREATE OR REPLACE VIEW view_name
AS
select_statement
WITH CHECK OPTION;
SQL
請注意,將分號(;
)放在WITH CHECK OPTION
子句的末尾,而不是在
咱們來看一下使用WITH CHECK OPTION
子句的例子。
首先,咱們根據employees
表
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
當使用WITH CHECK OPTION
子句
爲了肯定檢查的範圍,MySQL提供了兩個選項:LOCAL
和CASCADED
。若是您沒有在WITH CHECK OPTION
子句中顯式指定關鍵字,則MySQL默認使用CASCADED
。
要了解使用CASCADED CHECK OPTION
的效果,請參閱下面的例子。
首先,
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
表中
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
視圖的定義,
這是爲何呢?
由於v3
視圖取決於v2
視圖,v2
視圖具備WITH CASCADED CHECK OPTION
。
可是,如下插入語句能正常工做。
INSERT INTO v3(c) VALUES (30);
SQL
由於v3
視圖沒有使用WITH CHECK OPTION
定義,而且該語句符合v2
視圖的定義。
因此,總而言之:
當視圖使用WITH CASCADED CHECK OPTION
時,MySQL會循環檢查視圖的規則以及底層視圖的規則。
下面將演示使用 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 OPTION
和WITH 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 VIEW
和CREATE OR REPLACE 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
除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/
文件夾獲取其備份。
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) #查看錶的列定義信息
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;
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
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.全部葉子結點位於同一層;
1.關鍵字集合分佈在整顆樹中;
2.任何一個關鍵字出現且只出如今一個結點中;
3.搜索有可能在非葉子結點結束;
4.其搜索性能等價於在關鍵字全集內作一次二分查找;
5.自動層次控制;
注意:B-樹的搜索,從根結點開始,對結點內的關鍵字(有序)序列進行二分查找,若是命中則結束,不然進入查詢關鍵字所屬範圍的兒子結點;重複,直到所對應的兒子指針爲空,或已是葉子結點;
每次查找數據時,都去根節點查找數據,查找效率低下。
一般所說的索引是指 B-Tree索引,它是目前關係型數據庫中查找數據最爲經常使用和有效的索引,大多數存儲引擎都支持這種索引,且 MySQL 默認採用這種索引。使用 B-Tree 這個術語,是由於 MySQL 在 CREATE TABLE 或其它語句中使用這個關鍵字,
但實際上不一樣的存儲引擎可能使用不一樣的數據結構,好比 InnoDB 就是使用的 B+Tree。B+Tree中的B是指balance,意爲平衡。相對 Hash索引,B+Tree在查找單條記錄的速度比不上 Hash索引,可是由於更適合排序等操做,因此它更受歡迎。畢竟不可能只對數據庫進行單條記錄的操做
注 : B+樹索引 並不能找到一個給定鍵值的具體行,它找到的只是被查找數據行所在的頁,接着數據庫會把頁讀入到內存,再在內存中進行查找,最後獲得要查找的數據
葉子節點只放數據,根節點和分支節點只放索引。
1.全部關鍵字都出如今葉子結點的鏈表中(稠密索引),且鏈表中的關鍵字剛好是有序的;
2.不可能在非葉子結點命中;
3.非葉子結點至關因而葉子結點的索引(稀疏索引),葉子結點至關因而存儲(關鍵字)數據的數據層;
4.更適合文件索引系統;
5.葉子節點最下面的數據塊指針直接指向下一個數據塊的位置,適合按順序範圍查詢,搜索必定的範圍,速度很是快。
6.全部的記錄優先級一致,查詢次數一致,效率一致。
B+Tree索引:順序存儲,每個葉子節點到根結點的距離是相同的;左前綴索引,適合查詢範圍類的數據
可使用B+Tree索引的查詢類型:
全值匹配:精確全部索引列,如:姓wang,名xiaochun,年齡30
匹配最左前綴:即只使用索引的第一列,如:姓wang
匹配列前綴:只匹配一列值開頭部分,如:姓以w開頭的
匹配範圍值:如:姓ma和姓wang之間
精確匹配某一列並範圍匹配另外一列:如:姓wang,名以x開頭的只訪問索引的查詢
1> 如不從最左列開始,則沒法使用索引,如:查找名爲xiaochun,或姓爲g結尾
2> 不能跳過索引中的列:如:查找姓wang,年齡30的,只能使用索引第一列
3> 若是查詢中某個列是爲範圍查詢,那麼其右側的列都沒法再使用索引:如:姓wang,名x%,年齡30,只能利用姓和名上面的索引
特別提示:
1> 索引列的順序和查詢語句的寫法應相匹配,才能更好的利用索引
2> 爲優化性能,可能須要針對相同的列但順序不一樣建立不一樣的索引來知足不一樣類型的查詢需求
冗餘索引:(A),(A,B)
重複索引:已經有索引,再次創建索引
1> 獨立地使用列:儘可能避免其參與運算,獨立的列指索引列不能是表達式的一部分,也不能是函數的參數,在where條件中,始終將索引列單獨放在比較符號的一側
2> 左前綴索引:構建指定索引字段的左側的字符數,要經過索引選擇性來評估
3> 索引選擇性:不重複的索引值和數據表的記錄總數的比值
4> 多列索引:AND操做時更適合使用多列索引,而非爲每一個列建立單獨的索引
5> 選擇合適的索引列順序:無排序和分組時,將選擇性最高放左側
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和<>操做
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) 彙集索引,葉子節點存儲的是有序的整行數據.
#索引創建以前壓測:
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
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 | +-----------+
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;
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;
語法:
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.1 平衡
無論查找哪一個數,須要查找次數理論上是相同的,對於一個三層b樹來說,理論上查找每一個值都是三次IO。
講究快速鎖定範圍
B+tree,加入了雙向指針(頭尾相接),進一步加強範圍查找,減小對於ROOT和NO-LEAF的訪問次數。
葉子:先將數據排序,生成葉子節點。
枝:保存葉子節點的範圍(>=1 <5)+指針(→)
根:保存枝節點範圍+指針
葉子節點和枝節點都有雙向指針。
區(簇)extent:連續的64pages ,默認是1M存儲空間
page頁:16KB大小,MySQL中最小的IO單元
數據應該按照索引結構有序(順序)組織和存儲數據
MySQL使用聚簇索引組織存儲數據。
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.
葉子節點: 聚簇索引組織表,存數據時,已是按照ID列有序存儲數據到各個連續的數據頁中.原表數據存儲結構就是葉子節點.
枝節點:葉子節點中ID範圍+指針
根節點:枝節點的ID範圍+指針
葉子節點和枝節點都有雙向指針.
只能優化基於ID做爲條件.索引單純使用ID列查詢,很侷限.
> < = group by
where
6.4 輔助索引
須要人爲按照需求建立輔助索引.
alter table t1 add index idx(name);
葉子節點 : 將輔助索引列值(name)+ID提取出來,按照輔助索引列值從小到大排序,存儲到各個page中,生成葉子節點.
枝節點 : 存儲了葉子節點中,name列範圍+指針.
根節點 : 枝節點的name的範圍+指針.
若是查詢條件使用了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
alter table t1 add index idx_n_g(a,b)
葉子節點 : 提取a+b+id列值,按照a,b聯合排序(從小到大),生成葉子節點.
枝節點 : 葉子節點最左列範圍+指針
根節點 : 枝節點的範圍+指針.
回表作聚簇索引
查詢條件中必須包含最左列條件(a),先經過 a條件 掃描聯合索引的根節點和枝節點,從而獲得葉子節點範圍.再拿b做爲條件過濾一次. 最終目的,獲得更精確的ID .理論上減小回表的次數.
創建聯合索引時,選擇基數大(重複值少)做爲最左列. 查詢條件中必需要包含最左列條件.
通常建議3-4層爲準,3層b樹,2000w+
a.數據行多
分區表
按期歸檔:通常按照時間字段,按期歸檔到歷史庫中。pt-archiver
分庫分表:分佈式
b.索引列長度過長
前綴索引
c.數據類型
足夠
簡短的
合適的
影響索引樹高度的因素
數據量級,解決方法:
分區表:
按期歸檔:通常按照時間字段,按期歸檔到歷史庫中, pt-archiver
分庫,分表,分佈式
索引列值過長,解決方法:
業務容許,儘可能選擇字符長度短的列做爲索引列
業務不容許,採用前綴索引.
數據類型:
變長長度字符串,使用了char
,解決方案:變長長度字符串使用 varchar
enum
使用短字符代替長字符
enum ('山東','河北','黑龍江','吉林','遼寧','陝西'......)
enum ( 1 2 3 4 5 6 )
輔助索引掃描以後,獲得ID,再回到聚簇索引查找的過程
回表是隨機IO,會致使IO的次數(IOPS)和量(吞吐量)增長
IOPS(Input/Output Operations Per Second):每秒的讀寫次數,用於衡量隨機訪問的性能
吞吐量:單位時間內成功地傳送數據的數量(例如 200M/s)
a.建索引使用聯合索引(覆蓋),儘量多將查詢條件的數據包含聯合索引中
b.精細查詢條件(業務方面>and < ,limit)
c.查詢條件要符合聯合索引規則。覆蓋的列越多越好。
a b c
where a= and b= and c=
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
優化器(算法)最終得出的,代價最低的,SQL語句的執行方案.
場景一: 分析比較慢的語句.
場景二: 上線新業務,可能會包含不少select update delete...,提早發現問題.
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 | | ---- | ------ | ---- | ---- | ---- | ----------- | ----------- | ---- | ----- | ---- | ------ | ---- | | | | | | | | | | | | | | +----+-------------+-------+------------+------+---------------+-------------+---------+-------+------+----------+-------+
從上往下效率依次升高
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' );
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
沒有意義
ref
:非惟一性索引,等值查詢
DESC SELECT * FROM world.city WHERE countrycode='CHN';
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;
const
(system):惟一索引的等值查詢
DESC SELECT * FROM world.city WHERE id=10;
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
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: 在索引使用的字節數
題目意思: 咱們公司業務慢,請你從數據庫的角度分析緣由
1.mysql出現性能問題,我總結有兩種狀況:
(1)應急性的慢:忽然夯住
應急狀況:數據庫hang(卡了,資源耗盡)
處理過程:
1.show processlist; 獲取到致使數據庫hang的語句
2. explain 分析SQL的執行計劃,有沒有走索引,索引的類型狀況
3. 建索引,改語句
(2)一段時間慢(持續性的):
(1)記錄慢日誌slowlog,分析slowlog
(2)explain 分析SQL的執行計劃,有沒有走索引,索引的類型狀況
(3)建索引,改語句
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強制驅動表干預執行計劃。
如何在任務判斷一條鏈接語句中,誰是驅動表
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;
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'
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. 子查詢
大概率開發須要改寫語句了.
亂序查詢
輔助索引查找過程
優化器算法: 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
MRR(Mean reciprocal rank)是一個國際上通用的對搜索算法進行評價的機制,即第一個結果匹配,分數爲1,第二個匹配分數爲0.5,第n個匹配分數爲1/n,若是沒有匹配的句子分數爲0。最終的分數爲全部得分之和。
https://dev.mysql.com/doc/refman/8.0/en/mrr-optimization.html
業務
1.產品的功能
2.用戶的行爲
"熱"查詢語句 --->較慢--->slowlog
"熱"數據
(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) 聯合索引最左原則
select * from t1 ; select * from t1 where id=1001 or 1=1;
做業: SQL審覈和審計. yearning.io github, inception
查詢的結果集,超過了總數行數25%,優化器以爲就沒有必要走索引了。 MySQL的預讀功能有關。
能夠經過精確查找範圍,達到優化的效果。
1000000
500000 and
索引有自我維護的能力。
對於表內容變化比較頻繁的狀況下,有可能會出現索引失效。
通常是刪除重建
現象:
有一條select語句日常查詢時很快,忽然有一天很慢,會是什麼緣由 select? --->索引失效,統計數據不真實
innodb_index_stats 索引統計信息 innodb_table_stats 表的統計信息 mysql> ANALYZE TABLE world.city;
例子:
錯誤的例子:select * from test where id-1=9;
正確的例子:select * from test where id=10;
算術運算
函數運算
子查詢
這樣會致使索引失效. 錯誤的例子
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';
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
EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '31%' 走range索引掃描 EXPLAIN SELECT * FROM teltab WHERE telnum LIKE '%110' 不走索引 %linux%類的搜索需求,可使用elasticsearch+mongodb 專門作搜索服務的數據庫產品
SQL語句在查詢以前會使用查詢優化器對查詢進行優化。就是優化客戶端請求的 query(sql語句) ,根據客戶端請求的 query 語句,和數據庫中的一些統計信息,在一系列算法的基礎上進行分析,得出一個最優的策略,告訴後面的程序如何取得這個 query 語句的結果
他使用的是「選取-投影-聯接」策略進行查詢。 用一個例子就能夠理解: select uid,name from user where gender = 1; 這個select 查詢先根據where 語句進行選取,而不是先將表所有查詢出來之後再進行gender過濾 這個select查詢先根據uid和name進行屬性投影,而不是將屬性所有取出之後再進行過濾 將這兩個查詢條件聯接起來生成最終查詢結果
他的主要功能是將客戶端提交 給MySQL 的 Select 類 query 請求的返回結果集 cache 到內存中,與該 query 的一個 hash 值 作 一個對應。該 Query 所取數據的基表發生任何數據的變化以後, MySQL 會自動使該 query 的Cache 失效。在讀寫比例很是高的應用系統中, Query Cache 對性能的提升是很是顯著的。固然它對內存的消耗也是很是大的。
若是查詢緩存有命中的查詢結果,查詢語句就能夠直接去查詢緩存中取數據。這個緩存機制是由一系列小緩存組成的。好比表緩存,記錄緩存,key緩存,權限緩存等
存儲引擎接口模塊能夠說是 MySQL 數據庫中最有特點的一點了。目前各類數據庫產品中,基本上只有 MySQL 能夠實現其底層數據存儲引擎的插件式管理。這個模塊實際上只是 一個抽象類,但正是由於它成功地將各類數據處理高度抽象化,才成就了今天 MySQL 可插拔存儲引擎的特點。
能夠看出,MySQL區別於其餘數據庫的最重要的特色就是其插件式的表存儲引擎。MySQL插件式的存儲引擎架構提供了一系列標準的管理和服務支持,這些標準與存儲引擎自己無關,多是每一個數據庫系統自己都必需的,如SQL分析器和優化器等,而存儲引擎是底層物理結構的實現,每一個存儲引擎開發者均可以按照本身的意願來進行開發。 注意:存儲引擎是基於表的,而不是數據庫。
1> 客戶端向 MySQL 服務器發送一條查詢請求
2> 服務器首先檢查查詢緩存,若是命中緩存,則馬上返回存儲在緩存中的結果,不然進入下一階段
3> 服務器進行 SQL解析、預處理、再由優化器生成對應的執行計劃
4> MySQL 根據執行計劃,調用存儲引擎的 API來執行查詢
5> 將結果返回給客戶端,同時緩存查詢結果