原文:https://learnku.com/articles/25270mysql
一、前言
最近偶然翻翻一些博客,發現依然有一些介紹 mysql 常見優化場景的東西,甚是有趣。想起了以前在公司作的 SQL 規範相關工做。獨樂了不如衆樂樂,獨學習不如衆分享,跟你們分享下本身在這個環節的一些心得。程序員
以前無非是根據一些經驗和書籍,列出常見的場景。直到有一次看到了小米的開源工具,SOAR,簡直是被震驚的感受。這個工具經過是 SQL 語法樹的分析,結合小米 DBA 多年經驗的總結,進行了一系列啓發規則的校驗。最後給出 SQL 的優化建議,甚是好用。正則表達式
固然,本篇文章不會介紹 SOAR 的具體使用,咱們來聊聊那些 DBA 總結出來的啓發規則。根據啓發規則,你們也能解決平時遇到的相關 SQL 問題。sql
啓發式規則建議數據庫
二、建議使用 AS 關鍵字顯示聲明一個別名
- Item: ALI.001
- Severity: L0
- Content: 在列或表別名 (如 "tbl AS alias") 中,明確使用 AS 關鍵字比隱含別名 (如 "tbl alias") 更易懂。
- Case:
select name from tbl t1 where id < 1000
三、不建議給列通配符 '*' 設置別名
- Item: ALI.002
- Severity: L8
- Content: 例: "SELECT tbl.* col1, col2" 上面這條 SQL 給列通配符設置了別名,這樣的 SQL 可能存在邏輯錯誤。您可能意在查詢 col1, 可是代替它的是重命名的是 tbl 的最後一列。
- Case:
select tbl.* as c1,c2,c3 from tbl where id < 1000
四、別名不要與表或列的名字相同
- Item: ALI.003
- Severity: L1
- Content: 表或列的別名與其真實名稱相同,這樣的別名會使得查詢更難去分辨。
- Case:
select name from tbl as tbl where id < 1000
五、修改表的默認字符集不會改表各個字段的字符集
- Item: ALT.001
- Severity: L4
- Content: 不少初學者會將 ALTER TABLE tbl_name [DEFAULT] CHARACTER SET 'UTF8' 誤認爲會修改全部字段的字符集,但實際上它只會影響後續新增的字段不會改表已有字段的字符集。若是想修改整張表全部字段的字符集建議使用 ALTER TABLE tbl_name CONVERT TO CHARACTER SET charset_name;
- Case:
ALTER TABLE tbl_name CONVERT TO CHARACTER SET charset_name;
六、同一張表的多條 ALTER 請求建議合爲一條
- Item: ALT.002
- Severity: L2
- Content: 每次表結構變動對線上服務都會產生影響,即便是可以經過在線工具進行調整也請儘可能經過合併 ALTER 請求的試減小操做次數。
- Case:
ALTER TABLE tbl ADD COLUMN col int, ADD INDEX idx_col (col);
七、刪除列爲高危操做,操做前請注意檢查業務邏輯是否還有依賴
- Item: ALT.003
- Severity: L0
- Content: 如業務邏輯依賴未徹底消除,列被刪除後可能致使數據沒法寫入或沒法查詢到已刪除列數據致使程序異常的狀況。這種狀況下即便經過備份數據回滾也會丟失用戶請求寫入的數據。
- Case:
ALTER TABLE tbl DROP COLUMN col;
八、刪除主鍵和外鍵爲高危操做,操做前請與 DBA 確認影響
- Item: ALT.004
- Severity: L0
- Content: 主鍵和外鍵爲關係型數據庫中兩種重要約束,刪除已有約束會打破已有業務邏輯,操做前請業務開發與 DBA 確認影響,三思而行。
- Case:
ALTER TABLE tbl DROP PRIMARY KEY;
九、不建議使用前項通配符查找
- Item: ARG.001
- Severity: L4
- Content: 例如 "%foo",查詢參數有一個前項通配符的狀況沒法使用已有索引。
- Case:
select c1,c2,c3 from tbl where name like '%foo'
十、沒有通配符的 LIKE 查詢
- Item: ARG.002
- Severity: L1
- Content: 不包含通配符的 LIKE 查詢可能存在邏輯錯誤,由於邏輯上它與等值查詢相同。
- Case:
select c1,c2,c3 from tbl where name like 'foo'
十一、參數比較包含隱式轉換,沒法使用索引
- Item: ARG.003
- Severity: L4
- Content: 隱式類型轉換有沒法命中索引的風險,在高併發、大數據量的狀況下,命不中索引帶來的後果很是嚴重。
- Case:
SELECT * FROM sakila.film WHERE length >= '60';
十二、IN (NULL)/NOT IN (NULL) 永遠非真
- Item: ARG.004
- Severity: L4
- Content: 正確的做法是 col IN ('val1', 'val2', 'val3') OR col IS NULL
- Case:
SELECT * FROM tb WHERE col IN (NULL);
1三、IN 要慎用,元素過多會致使全表掃描
- Item: ARG.005
- Severity: L1
- Content: 如:select id from t where num in (1,2,3) 對於連續的數值,能用 BETWEEN 就不要用 IN 了:select id from t where num between 1 and 3。而當 IN 值過多時 MySQL 也可能會進入全表掃描致使性能急劇降低。
- Case:
select id from t where num in(1,2,3)
1四、應儘可能避免在 WHERE 子句中對字段進行 NULL 值判斷
- Item: ARG.006
- Severity: L1
- Content: 使用 IS NULL 或 IS NOT NULL 將可能致使引擎放棄使用索引而進行全表掃描,如:select id from t where num is null; 能夠在 num 上設置默認值 0,確保表中 num 列沒有 NULL 值,而後這樣查詢: select id from t where num=0;
- Case:
select id from t where num is null
1五、避免使用模式匹配
- Item: ARG.007
- Severity: L3
- Content: 性能問題是使用模式匹配操做符的最大缺點。使用 LIKE 或正則表達式進行模式匹配進行查詢的另外一個問題,是可能會返回意料以外的結果。最好的方案就是使用特殊的搜索引擎技術來替代 SQL,好比 Apache Lucene。另外一個可選方案是將結果保存起來從而減小重複的搜索開銷。若是必定要使用 SQL,請考慮在 MySQL 中使用像 FULLTEXT 索引這樣的第三方擴展。但更普遍地說,您不必定要使用 SQL 來解決全部問題。
- Case:
select c_id,c2,c3 from tbl where c2 like 'test%'
1六、OR 查詢索引列時請儘可能使用 IN 謂詞
- Item: ARG.008
- Severity: L1
- Content: IN-list 謂詞能夠用於索引檢索,而且優化器能夠對 IN-list 進行排序,以匹配索引的排序序列,從而得到更有效的檢索。請注意,IN-list 必須只包含常量,或在查詢塊執行期間保持常量的值,例如外引用。
- Case:
SELECT c1,c2,c3 FROM tbl WHERE c1 = 14 OR c1 = 17
1七、引號中的字符串開頭或結尾包含空格
- Item: ARG.009
- Severity: L1
- Content: 若是 VARCHAR 列的先後存在空格將可能引發邏輯問題,如在 MySQL 5.5 中 'a' 和 'a ' 可能會在查詢中被認爲是相同的值。
- Case:
SELECT 'abc '
1八、不要使用 hint,如:sql_no_cache, force index, ignore key, straight join 等
- Item: ARG.010
- Severity: L1
- Content: hint 是用來強制 SQL 按照某個執行計劃來執行,但隨着數據量變化咱們沒法保證本身當初的預判是正確的。
- Case:
SELECT * FROM t1 USE INDEX (i1) ORDER BY a;
1九、不要使用負向查詢,如:NOT IN/NOT LIKE
- Item: ARG.011
- Severity: L3
- Content: 請儘可能不要使用負向查詢,這將致使全表掃描,對查詢性能影響較大。
- Case:
select id from t where num not in(1,2,3);
20、一次性 INSERT/REPLACE 的數據過多
- Item: ARG.012
- Severity: L2
- Content: 單條 INSERT/REPLACE 語句批量插入大量數據性能較差,甚至可能致使從庫同步延遲。爲了提高性能,減小批量寫入數據對從庫同步延時的影響,建議採用分批次插入的方法。
- Case:
INSERT INTO tb (a) VALUES (1), (2)
2一、最外層 SELECT 未指定 WHERE 條件
- Item: CLA.001
- Severity: L4
- Content: SELECT 語句沒有 WHERE 子句,可能檢查比預期更多的行 (全表掃描)。對於 SELECT COUNT (*) 類型的請求若是不要求精度,建議使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
- Case:
select id from tbl
2二、不建議使用 ORDER BY RAND ()
- Item: CLA.002
- Severity: L3
- Content: ORDER BY RAND () 是從結果集中檢索隨機行的一種很是低效的方法,由於它會對整個結果進行排序並丟棄其大部分數據。
- Case:
select name from tbl where id < 1000 order by rand(number)
2三、不建議使用帶 OFFSET 的 LIMIT 查詢
- Item: CLA.003
- Severity: L2
- Content: 使用 LIMIT 和 OFFSET 對結果集分頁的複雜度是 O (n^2),而且會隨着數據增大而致使性能問題。採用 「書籤」 掃描的方法實現分頁效率更高。
- Case:
select c1,c2 from tbl where name=xx order by number limit 1 offset 20
2四、不建議對常量進行 GROUP BY
- Item: CLA.004
- Severity: L2
- Content: GROUP BY 1 表示按第一列進行 GROUP BY。若是在 GROUP BY 子句中使用數字,而不是表達式或列名稱,當查詢列順序改變時,可能會致使問題。
- Case:
select col1,col2 from tbl group by 1
2五、ORDER BY 常數列沒有任何意義
- Item: CLA.005
- Severity: L2
- Content: SQL 邏輯上可能存在錯誤;最多隻是一個無用的操做,不會更改查詢結果。
- Case:
select id from test where id=1 order by id
2六、在不一樣的表中 GROUP BY 或 ORDER BY
- Item: CLA.006
- Severity: L4
- Content: 這將強制使用臨時表和 filesort,可能產生巨大性能隱患,而且可能消耗大量內存和磁盤上的臨時空間。
- Case:
select tb1.col, tb2.col from tb1, tb2 where id=1 group by tb1.col, tb2.col
2七、ORDER BY 語句對多個不一樣條件使用不一樣方向的排序沒法使用索引
- Item: CLA.007
- Severity: L2
- Content: ORDER BY 子句中的全部表達式必須按統一的 ASC 或 DESC 方向排序,以便利用索引。
- Case:
select c1,c2,c3 from t1 where c1='foo' order by c2 desc, c3 asc
2八、請爲 GROUP BY 顯示添加 ORDER BY 條件
- Item: CLA.008
- Severity: L2
- Content: 默認 MySQL 會對 'GROUP BY col1, col2, ...' 請求按以下順序排序 'ORDER BY col1, col2, ...'。若是 GROUP BY 語句不指定 ORDER BY 條件會致使無謂的排序產生,若是不須要排序建議添加 'ORDER BY NULL'。
- Case:
select c1,c2,c3 from t1 where c1='foo' group by c2
2九、ORDER BY 的條件爲表達式
- Item: CLA.009
- Severity: L2
- Content: 當 ORDER BY 條件爲表達式或函數時會使用到臨時表,若是在未指定 WHERE 或 WHERE 條件返回的結果集較大時性能會不好。
- Case:
select description from film where title ='ACADEMY DINOSAUR' order by length-language_id;
30、GROUP BY 的條件爲表達式
- Item: CLA.010
- Severity: L2
- Content: 當 GROUP BY 條件爲表達式或函數時會使用到臨時表,若是在未指定 WHERE 或 WHERE 條件返回的結果集較大時性能會不好。
- Case:
select description from film where title ='ACADEMY DINOSAUR' GROUP BY length-language_id;
3一、建議爲表添加註釋
- Item: CLA.011
- Severity: L1
- Content: 爲表添加註釋可以使得表的意義更明確,從而爲往後的維護帶來極大的便利。
- Case:
CREATE TABLE test1 (ID bigint(20) NOT NULL AUTO_INCREMENT,c1 varchar(128) DEFAULT NULL,PRIMARY KEY (ID)) ENGINE=InnoDB DEFAULT CHARSET=utf8
3二、將複雜的裹腳布式查詢分解成幾個簡單的查詢
- Item: CLA.012
- Severity: L2
- Content: SQL 是一門極具表現力的語言,您能夠在單個 SQL 查詢或者單條語句中完成不少事情。但這並不意味着必須強制只使用一行代碼,或者認爲使用一行代碼就搞定每一個任務是個好主意。經過一個查詢來得到全部結果的常見後果是獲得了一個笛卡兒積。當查詢中的兩張表之間沒有條件限制它們的關係時,就會發生這種狀況。沒有對應的限制而直接使用兩張表進行聯結查詢,就會獲得第一張表中的每一行和第二張表中的每一行的一個組合。每個這樣的組合就會成爲結果集中的一行,最終您就會獲得一個行數不少的結果集。重要的是要考慮這些查詢很難編寫、難以修改和難以調試。數據庫查詢請求的日益增長應該是預料之中的事。經理們想要更復雜的報告以及在用戶界面上添加更多的字段。若是您的設計很複雜,而且是一個單一查詢,要擴展它們就會很費時費力。不論對您仍是項目來講,時間花在這些事情上面不值得。將複雜的意大利麪條式查詢分解成幾個簡單的查詢。當您拆分一個複雜的 SQL 查詢時,獲得的結果多是不少相似的查詢,可能僅僅在數據類型上有所不一樣。編寫全部的這些查詢是很乏味的,所以,最好可以有個程序自動生成這些代碼。SQL 代碼生成是一個很好的應用。儘管 SQL 支持用一行代碼解決複雜的問題,但也別作不切實際的事情。
- Case: 這是一條很長很長的 SQL,案例略。
3三、不建議使用 HAVING 子句
- Item: CLA.013
- Severity: L3
- Content: 將查詢的 HAVING 子句改寫爲 WHERE 中的查詢條件,能夠在查詢處理期間使用索引。
- Case:
SELECT s.c_id,count(s.c_id) FROM s where c = test GROUP BY s.c_id HAVING s.c_id <> '1660' AND s.c_id <> '2' order by s.c_id
3四、刪除全表時建議使用 TRUNCATE 替代 DELETE
- Item: CLA.014
- Severity: L2
- Content: 刪除全表時建議使用 TRUNCATE 替代 DELETE
- Case:
delete from tbl
3五、UPDATE 未指定 WHERE 條件
- Item: CLA.015
- Severity: L4
- Content: UPDATE 不指定 WHERE 條件通常是致命的,請您三思後行
- Case:
update tbl set col=1
3六、不要 UPDATE 主鍵
- Item: CLA.016
- Severity: L2
- Content: 主鍵是數據表中記錄的惟一標識符,不建議頻繁更新主鍵列,這將影響元數據統計信息進而影響正常的查詢。
- Case:
update tbl set col=1
3七、不建議使用 SELECT * 類型查詢
- Item: COL.001
- Severity: L1
- Content: 當表結構變動時,使用 * 通配符選擇全部列將致使查詢的含義和行爲會發生更改,可能致使查詢返回更多的數據。
- Case:
select * from tbl where id=1
3八、INSERT/REPLACE 未指定列名
- Item: COL.002
- Severity: L2
- Content: 當表結構發生變動,若是 INSERT 或 REPLACE 請求不明確指定列名,請求的結果將會與預想的不一樣;建議使用 「INSERT INTO tbl (col1,col2) VALUES ...」 代替。
- Case:
insert into tbl values(1,'name')
3九、建議修改自增 ID 爲無符號類型
- Item: COL.003
- Severity: L2
- Content: 建議修改自增 ID 爲無符號類型
- Case:
create table test(id int(11) NOT NULL AUTO_INCREMENT)
40、請爲列添加默認值
- Item: COL.004
- Severity: L1
- Content: 請爲列添加默認值,若是是 ALTER 操做,請不要忘記將原字段的默認值寫上。字段無默認值,當表較大時沒法在線變動表結構。
- Case:
CREATE TABLE tbl (col int) ENGINE=InnoDB;
4一、列未添加註釋
- Item: COL.005
- Severity: L1
- Content: 建議對錶中每一個列添加註釋,來明確每一個列在表中的含義及做用。
- Case:
CREATE TABLE tbl (col int) ENGINE=InnoDB;
4二、表中包含有太多的列
- Item: COL.006
- Severity: L3
- Content: 表中包含有太多的列
- Case:
CREATE TABLE tbl ( cols ....);
4三、可以使用 VARCHAR 代替 CHAR, VARBINARY 代替 BINARY
- Item: COL.008
- Severity: L1
- Content: 爲首先變長字段存儲空間小,能夠節省存儲空間。其次對於查詢來講,在一個相對較小的字段內搜索效率顯然要高些。
- Case:
create table t1(id int,name char(20),last_time date)
4四、建議使用精確的數據類型
- Item: COL.009
- Severity: L2
- Content: 實際上,任何使用 FLOAT, REAL 或 DOUBLE PRECISION 數據類型的設計都有多是反模式。大多數應用程序使用的浮點數的取值範圍並不須要達到 IEEE 754 標準所定義的最大 / 最小區間。在計算總量時,非精確浮點數所積累的影響是嚴重的。使用 SQL 中的 NUMERIC 或 DECIMAL 類型來代替 FLOAT 及其相似的數據類型進行固定精度的小數存儲。這些數據類型精確地根據您定義這一列時指定的精度來存儲數據。儘量不要使用浮點數。
- Case:
CREATE TABLE tab2 (p_id BIGINT UNSIGNED NOT NULL,a_id BIGINT UNSIGNED NOT NULL,hours float not null,PRIMARY KEY (p_id, a_id))
4五、不建議使用 ENUM 數據類型
- Item: COL.010
- Severity: L2
- Content: ENUM 定義了列中值的類型,使用字符串表示 ENUM 裏的值時,實際存儲在列中的數據是這些值在定義時的序數。所以,這列的數據是字節對齊的,當您進行一次排序查詢時,結果是按照實際存儲的序數值排序的,而不是按字符串值的字母順序排序的。這可能不是您所但願的。沒有什麼語法支持從 ENUM 或者 check 約束中添加或刪除一個值;您只能使用一個新的集合從新定義這一列。若是您打算廢棄一個選項,您可能會爲歷史數據而煩惱。做爲一種策略,改變元數據 —— 也就是說,改變表和列的定義 —— 應該是不常見的,而且要注意測試和質量保證。有一個更好的解決方案來約束一列中的可選值:建立一張檢查表,每一行包含一個容許在列中出現的候選值;而後在引用新表的舊錶上聲明一個外鍵約束。
- Case:
create table tab1(status ENUM('new','in progress','fixed'))
4六、當須要惟一約束時才使用 NULL,僅當列不能有缺失值時才使用 NOT NULL
- Item: COL.011
- Severity: L0
- Content: NULL 和 0 是不一樣的,10 乘以 NULL 仍是 NULL。NULL 和空字符串是不同的。將一個字符串和標準 SQL 中的 NULL 聯合起來的結果仍是 NULL。NULL 和 FALSE 也是不一樣的。AND、OR 和 NOT 這三個布爾操做若是涉及 NULL,其結果也讓不少人感到困惑。當您將一列聲明爲 NOT NULL 時,也就是說這列中的每個值都必須存在且是有意義的。使用 NULL 來表示任意類型不存在的空值。 當您將一列聲明爲 NOT NULL 時,也就是說這列中的每個值都必須存在且是有意義的。
- Case:
select c1,c2,c3 from tbl where c4 is null or c4 <> 1
4七、BLOB 和 TEXT 類型的字段不可設置爲 NULL
- Item: COL.012
- Severity: L5
- Content: BLOB 和 TEXT 類型的字段不可設置爲 NULL
- Case:
CREATE TABLE tbl ( id int(10) unsigned NOT NULL AUTO_INCREMENT, c longblob, PRIMARY KEY (id));
4八、TIMESTAMP 類型未設置默認值
- Item: COL.013
- Severity: L4
- Content: TIMESTAMP 類型未設置默認值
- Case:
CREATE TABLE tbl( id bigint not null, create_time timestamp);
4九、爲列指定了字符集
- Item: COL.014
- Severity: L5
- Content: 建議列與表使用同一個字符集,不要單獨指定列的字符集。
- Case:
CREATE TABLE tb2 ( id int(11) DEFAULT NULL, col char(10) CHARACTER SET utf8 DEFAULT NULL)
50、BLOB 類型的字段不可指定默認值
- Item: COL.015
- Severity: L4
- Content: BLOB 類型的字段不可指定默認值
- Case:
CREATE TABLE tbl ( id int(10) unsigned NOT NULL AUTO_INCREMENT, c blob NOT NULL DEFAULT '', PRIMARY KEY (id));
5一、整型定義建議採用 INT (10) 或 BIGINT (20)
- Item: COL.016
- Severity: L1
- Content: INT (M) 在 integer 數據類型中,M 表示最大顯示寬度。 在 INT (M) 中,M 的值跟 INT (M) 所佔多少存儲空間並沒有任何關係。 INT (3)、INT (4)、INT (8) 在磁盤上都是佔用 4 bytes 的存儲空間。
- Case:
CREATE TABLE tab (a INT(1));
5二、VARCHAR 定義長度過長
- Item: COL.017
- Severity: L2
- Content: varchar 是可變長字符串,不預先分配存儲空間,長度不要超過 255,若是存儲長度過長 MySQL 將定義字段類型爲 text,獨立出來一張表,用主鍵來對應,避免影響其它字段索引效率。
- Case:
CREATE TABLE tab (a varchar(3500));
5三、消除沒必要要的 DISTINCT 條件
- Item: DIS.001
- Severity: L1
- Content: 太多 DISTINCT 條件是複雜的裹腳布式查詢的症狀。考慮將複雜查詢分解成許多簡單的查詢,並減小 DISTINCT 條件的數量。若是主鍵列是列的結果集的一部分,則 DISTINCT 條件可能沒有影響。
- Case:
SELECT DISTINCT c.c_id,count(DISTINCT c.c_name),count(DISTINCT c.c_e),count(DISTINCT c.c_n),count(DISTINCT c.c_me),c.c_d FROM (select distinct id, name from B) as e WHERE e.country_id = c.country_id
5四、COUNT (DISTINCT) 多列時結果可能和你預想的不一樣
- Item: DIS.002
- Severity: L3
- Content: COUNT (DISTINCT col) 計算該列除 NULL 以外的不重複行數,注意 COUNT (DISTINCT col, col2) 若是其中一列全爲 NULL 那麼即便另外一列有不一樣的值,也返回 0。
- Case:
SELECT COUNT(DISTINCT col, col2) FROM tbl;
5五、DISTINCT * 對有主鍵的表沒有意義
- Item: DIS.003
- Severity: L3
- Content: 當表已經有主鍵時,對全部列進行 DISTINCT 的輸出結果與不進行 DISTINCT 操做的結果相同,請不要多此一舉。
- Case:
SELECT DISTINCT * FROM film;
5六、避免在 WHERE 條件中使用函數或其餘運算符
- Item: FUN.001
- Severity: L2
- Content: 雖然在 SQL 中使用函數能夠簡化不少複雜的查詢,但使用了函數的查詢沒法利用表中已經創建的索引,該查詢將會是全表掃描,性能較差。一般建議將列名寫在比較運算符左側,將查詢過濾條件放在比較運算符右側。也不建議在查詢比較條件兩側書寫多餘的括號,這會對閱讀產生比較大的困擾。
- Case:
select id from t where substring(name,1,3)='abc'
5七、指定了 WHERE 條件或非 MyISAM 引擎時使用 COUNT (*) 操做性能不佳
- Item: FUN.002
- Severity: L1
- Content: COUNT () 的做用是統計表行數,COUNT (COL) 的做用是統計指定列非 NULL 的行數。MyISAM 表對於 COUNT () 統計全錶行數進行了特殊的優化,一般狀況下很是快。但對於非 MyISAM 表或指定了某些 WHERE 條件,COUNT (*) 操做須要掃描大量的行才能獲取精確的結果,性能也所以不佳。有時候某些業務場景並不須要徹底精確的 COUNT 值,此時能夠用近似值來代替。EXPLAIN 出來的優化器估算的行數就是一個不錯的近似值,執行 EXPLAIN 並不須要真正去執行查詢,因此成本很低。
- Case:
SELECT c3, COUNT(*) AS accounts FROM tab where c2 < 10000 GROUP BY c3 ORDER BY num
5八、使用了合併爲可空列的字符串鏈接
- Item: FUN.003
- Severity: L3
- Content: 在一些查詢請求中,您須要強制讓某一列或者某個表達式返回非 NULL 的值,從而讓查詢邏輯變得更簡單,擔心不想將這個值存下來。使用 COALESCE () 函數來構造鏈接的表達式,這樣即便是空值列也不會使整表達式變爲 NULL。
- Case:
select c1 || coalesce(' ' || c2 || ' ', ' ') || c3 as c from tbl
5九、不建議使用 SYSDATE () 函數
- Item: FUN.004
- Severity: L4
- Content: SYSDATE () 函數可能致使主從數據不一致,請使用 NOW () 函數替代 SYSDATE ()。
- Case:
SELECT SYSDATE();
60、不建議使用 COUNT (col) 或 COUNT (常量)
- Item: FUN.005
- Severity: L1
- Content: 不要使用 COUNT (col) 或 COUNT (常量) 來替代 COUNT (), COUNT () 是 SQL92 定義的標準統計行數的方法,跟數據無關,跟 NULL 和非 NULL 也無關。
- Case:
SELECT COUNT(1) FROM tbl;
6一、使用 SUM (COL) 時需注意 NPE 問題
- Item: FUN.006
- Severity: L1
- Content: 當某一列的值全是 NULL 時,COUNT (COL) 的返回結果爲 0, 但 SUM (COL) 的返回結果爲 NULL,所以使用 SUM () 時需注意 NPE 問題。可使用以下方式來避免 SUM 的 NPE 問題: SELECT IF (ISNULL (SUM (COL)), 0, SUM (COL)) FROM tbl
- Case:
SELECT SUM(COL) FROM tbl;
6二、不建議使用觸發器
- Item: FUN.007
- Severity: L1
- Content: 觸發器的執行沒有反饋和日誌,隱藏了實際的執行步驟,當數據庫出現問題是,不能經過慢日誌分析觸發器的具體執行狀況,不易發現問題。在 MySQL 中,觸發器不能臨時關閉或打開,在數據遷移或數據恢復等場景下,須要臨時 drop 觸發器,可能影響到生產環境。
- Case:
CREATE TRIGGER t1 AFTER INSERT ON work FOR EACH ROW INSERT INTO time VALUES(NOW());
6三、不建議使用存儲過程
- Item: FUN.008
- Severity: L1
- Content: 存儲過程無版本控制,配合業務的存儲過程升級很難作到業務無感知。存儲過程在拓展和移植上也存在問題。
- Case:
CREATE PROCEDURE simpleproc (OUT param1 INT);
6四、不建議使用自定義函數
- Item: FUN.009
- Severity: L1
- Content: 不建議使用自定義函數
- Case:
CREATE FUNCTION hello (s CHAR(20));
6五、不建議對等值查詢列使用 GROUP BY
- Item: GRP.001
- Severity: L2
- Content: GROUP BY 中的列在前面的 WHERE 條件中使用了等值查詢,對這樣的列進行 GROUP BY 意義不大。
- Case:
select film_id, title from film where release_year='2006' group by release_year
6六、JOIN 語句混用逗號和 ANSI 模式
- Item: JOI.001
- Severity: L2
- Content: 錶鏈接的時候混用逗號和 ANSI JOIN 不便於人類理解,而且 MySQL 不一樣版本的錶鏈接行爲和優先級均有所不一樣,當 MySQL 版本變化後可能會引入錯誤。
- Case:
select c1,c2,c3 from t1,t2 join t3 on t1.c1=t2.c1,t1.c3=t3,c1 where id>1000
6七、同一張表被鏈接兩次
- Item: JOI.002
- Severity: L4
- Content: 相同的表在 FROM 子句中至少出現兩次,能夠簡化爲對該表的單次訪問。
- Case:
select tb1.col from (tb1, tb2) join tb2 on tb1.id=tb.id where tb1.id=1
6八、OUTER JOIN 失效
- Item: JOI.003
- Severity: L4
- Content: 因爲 WHERE 條件錯誤使得 OUTER JOIN 的外部表無數據返回,這會將查詢隱式轉換爲 INNER JOIN 。如:select c from L left join R using (c) where L.a=5 and R.b=10。這種 SQL 邏輯上可能存在錯誤或程序員對 OUTER JOIN 如何工做存在誤解,由於 LEFT/RIGHT JOIN 是 LEFT/RIGHT OUTER JOIN 的縮寫。
- Case:
select c1,c2,c3 from t1 left outer join t2 using(c1) where t1.c2=2 and t2.c3=4
6九、不建議使用排它 JOIN
- Item: JOI.004
- Severity: L4
- Content: 只在右側表爲 NULL 的帶 WHERE 子句的 LEFT OUTER JOIN 語句,有多是在 WHERE 子句中使用錯誤的列,如:「... FROM l LEFT OUTER JOIN r ON l.l = r.r WHERE r.z IS NULL」,這個查詢正確的邏輯多是 WHERE r.r IS NULL。
- Case:
select c1,c2,c3 from t1 left outer join t2 on t1.c1=t2.c1 where t2.c2 is null
70、減小 JOIN 的數量
- Item: JOI.005
- Severity: L2
- Content: 太多的 JOIN 是複雜的裹腳布式查詢的症狀。考慮將複雜查詢分解成許多簡單的查詢,並減小 JOIN 的數量。
- Case:
select bp1.p_id, b1.d_d as l, b1.b_id from b1 join bp1 on (b1.b_id = bp1.b_id) left outer join (b1 as b2 join bp2 on (b2.b_id = bp2.b_id)) on (bp1.p_id = bp2.p_id ) join bp21 on (b1.b_id = bp1.b_id) join bp31 on (b1.b_id = bp1.b_id) join bp41 on (b1.b_id = bp1.b_id) where b2.b_id = 0
7一、將嵌套查詢重寫爲 JOIN 一般會致使更高效的執行和更有效的優化
- Item: JOI.006
- Severity: L4
- Content: 通常來講,非嵌套子查詢老是用於關聯子查詢,最可能是來自 FROM 子句中的一個表,這些子查詢用於 ANY, ALL 和 EXISTS 的謂詞。若是能夠根據查詢語義決定子查詢最多返回一個行,那麼一個不相關的子查詢或來自 FROM 子句中的多個表的子查詢就被壓平了。
- Case:
SELECT s,p,d FROM tbl WHERE p.p_id = (SELECT s.p_id FROM tbl WHERE s.c_id = 100996 AND s.q = 1 )
7二、不建議使用聯表刪除或更新
- Item: JOI.007
- Severity: L4
- Content: 當須要同時刪除或更新多張表時建議使用簡單語句,一條 SQL 只刪除或更新一張表,儘可能不要將多張表的操做在同一條語句。
- Case:
UPDATE users u LEFT JOIN hobby h ON u.id = h.uid SET u.name = 'pianoboy' WHERE h.hobby = 'piano';
7三、不要使用跨數據庫的 JOIN 查詢
- Item: JOI.008
- Severity: L4
- Content: 通常來講,跨數據庫的 JOIN 查詢意味着查詢語句跨越了兩個不一樣的子系統,這可能意味着系統耦合度太高或庫表結構設計不合理。
- Case:
SELECT s,p,d FROM tbl WHERE p.p_id = (SELECT s.p_id FROM tbl WHERE s.c_id = 100996 AND s.q = 1 )
7四、建議使用自增列做爲主鍵,如使用聯合自增主鍵時請將自增鍵做爲第一列
- Item: KEY.001
- Severity: L2
- Content: 建議使用自增列做爲主鍵,如使用聯合自增主鍵時請將自增鍵做爲第一列
- Case:
create table test(id int(11) NOT NULL PRIMARY KEY (id))
7五、無主鍵或惟一鍵,沒法在線變動表結構
- Item: KEY.002
- Severity: L4
- Content: 無主鍵或惟一鍵,沒法在線變動表結構
- Case:
create table test(col varchar(5000))
7六、避免外鍵等遞歸關係
- Item: KEY.003
- Severity: L4
- Content: 存在遞歸關係的數據很常見,數據常會像樹或者以層級方式組織。然而,建立一個外鍵約束來強制執行同一表中兩列之間的關係,會致使笨拙的查詢。樹的每一層對應着另外一個鏈接。您將須要發出遞歸查詢,以得到節點的全部後代或全部祖先。解決方案是構造一個附加的閉包表。它記錄了樹中全部節點間的關係,而不只僅是那些具備直接的父子關係。您也能夠比較不一樣層次的數據設計:閉包表,路徑枚舉,嵌套集。而後根據應用程序的須要選擇一個。
- Case:
CREATE TABLE tab2 (p_id BIGINT UNSIGNED NOT NULL,a_id BIGINT UNSIGNED NOT NULL,PRIMARY KEY (p_id, a_id),FOREIGN KEY (p_id) REFERENCES tab1(p_id),FOREIGN KEY (a_id) REFERENCES tab3(a_id))
7七、提醒:請將索引屬性順序與查詢對齊
- Item: KEY.004
- Severity: L0
- Content: 若是爲列建立複合索引,請確保查詢屬性與索引屬性的順序相同,以便 DBMS 在處理查詢時使用索引。若是查詢和索引屬性訂單沒有對齊,那麼 DBMS 可能沒法在查詢處理期間使用索引。
- Case:
create index idx1 on tbl (last_name,first_name)
7八、表建的索引過多
- Item: KEY.005
- Severity: L2
- Content: 表建的索引過多
- Case:
CREATE TABLE tbl ( a int, b int, c int, KEY idx_a (a),KEY idx_b(b),KEY idx_c(c));
7九、主鍵中的列過多
- Item: KEY.006
- Severity: L4
- Content: 主鍵中的列過多
- Case:
CREATE TABLE tbl ( a int, b int, c int, PRIMARY KEY(a,b,c));
80、未指定主鍵或主鍵非 bigint
- Item: KEY.007
- Severity: L4
- Content: 未指定主鍵或主鍵非 bigint,建議將主鍵設置爲 bigint unsigned。
- Case:
CREATE TABLE tbl (a bigint);
8一、ORDER BY 多個列但排序方向不一樣時可能沒法使用索引
- Item: KEY.008
- Severity: L4
- Content: 在 MySQL 8.0 以前當 ORDER BY 多個列指定的排序方向不一樣時將沒法使用已經創建的索引。
- Case:
SELECT * FROM tbl ORDER BY a DESC, b ASC;
8二、添加惟一索引前請注意檢查數據惟一性
-
Item: KEY.009安全
-
Severity: L0網絡
-
Content: 請提早檢查添加惟一索引列的數據惟一性,若是數據不惟一在線表結構調整時將有可能自動將重複列刪除,這有可能致使數據丟失。閉包
-
Case:併發
CREATE UNIQUE INDEX part_of_name ON customer (name(10));
8三、全文索引不是銀彈
- Item: KEY.010
- Severity: L0
- Content: 全文索引主要用於解決模糊查詢的性能問題,但須要控制好查詢的頻率和併發度。同時注意調整 ft_min_word_len, ft_max_word_len, ngram_token_size 等參數。
- Case:
CREATE TABLE tb ( id int(10) unsigned NOT NULL AUTO_INCREMENT, ip varchar(255) NOT NULL DEFAULT '', PRIMARY KEY (id), FULLTEXT KEY ip (ip) ) ENGINE=InnoDB;
8四、SQL_CALC_FOUND_ROWS 效率低下
- Item: KWR.001
- Severity: L2
- Content: 由於 SQL_CALC_FOUND_ROWS 不能很好地擴展,因此可能致使性能問題;建議業務使用其餘策略來替代 SQL_CALC_FOUND_ROWS 提供的計數功能,好比:分頁結果展現等。
- Case:
select SQL_CALC_FOUND_ROWS col from tbl where id>1000
8五、不建議使用 MySQL 關鍵字作列名或表名
- Item: KWR.002
- Severity: L2
- Content: 當使用關鍵字作爲列名或表名時程序須要對列名和表名進行轉義,若是疏忽被將致使請求沒法執行。
- Case:
CREATE TABLE tbl ( `select` int )
8六、不建議使用複數作列名或表名
- Item: KWR.003
- Severity: L1
- Content: 表名應該僅僅表示表裏面的實體內容,不該該表示實體數量,對應於 DO 類名也是單數形式,符合表達習慣。
- Case:
CREATE TABLE tbl ( `books` int )
8七、不建議使用使用多字節編碼字符 (中文) 命名
- Item: KWR.004
- Severity: L1
- Content: 爲庫、表、列、別名命名時建議使用英文,數字,下劃線等字符,不建議使用中文或其餘多字節編碼字符。
- Case:
select col as 列 from tb
8八、INSERT INTO xx SELECT 加鎖粒度較大請謹慎
- Item: LCK.001
- Severity: L3
- Content: INSERT INTO xx SELECT 加鎖粒度較大請謹慎
- Case:
INSERT INTO tbl SELECT * FROM tbl2;
8九、請慎用 INSERT ON DUPLICATE KEY UPDATE
- Item: LCK.002
- Severity: L3
- Content: 當主鍵爲自增鍵時使用 INSERT ON DUPLICATE KEY UPDATE 可能會致使主鍵出現大量不連續快速增加,致使主鍵快速溢出沒法繼續寫入。極端狀況下還有可能致使主從數據不一致。
- Case:
INSERT INTO t1(a,b,c) VALUES (1,2,3) ON DUPLICATE KEY UPDATE c=c+1;
90、用字符類型存儲 IP 地址
- Item: LIT.001
- Severity: L2
- Content: 字符串字面上看起來像 IP 地址,但不是 INET_ATON () 的參數,表示數據被存儲爲字符而不是整數。將 IP 地址存儲爲整數更爲有效。
- Case:
insert into tbl (IP,name) values('10.20.306.122','test')
9一、日期 / 時間未使用引號括起
- Item: LIT.002
- Severity: L4
- Content: 諸如 「WHERE col <2010-02-12」 之類的查詢是有效的 SQL,但多是一個錯誤,由於它將被解釋爲 「WHERE col <1996」; 日期 / 時間文字應該加引號。
- Case:
select col1,col2 from tbl where time < 2018-01-10
9二、一列中存儲一系列相關數據的集合
- Item: LIT.003
- Severity: L3
- Content: 將 ID 存儲爲一個列表,做爲 VARCHAR/TEXT 列,這樣能致使性能和數據完整性問題。查詢這樣的列須要使用模式匹配的表達式。使用逗號分隔的列表來作多表聯結查詢定位一行數據是極不優雅和耗時的。這將使驗證 ID 更加困難。考慮一下,列表最多支持存放多少數據呢?將 ID 存儲在一張單獨的表中,代替使用多值屬性,從而每一個單獨的屬性值均可以佔據一行。這樣交叉表實現了兩張表之間的多對多關係。這將更好地簡化查詢,也更有效地驗證 ID。
- Case:
select c1,c2,c3,c4 from tab1 where col_id REGEXP '[[:<:]]12[[:>:]]'
9三、請使用分號或已設定的 DELIMITER 結尾
- Item: LIT.004
- Severity: L1
- Content: USE database, SHOW DATABASES 等命令也須要使用使用分號或已設定的 DELIMITER 結尾。
- Case:
USE db
9四、非肯定性的 GROUP BY
- Item: RES.001
- Severity: L4
- Content: SQL 返回的列既不在聚合函數中也不是 GROUP BY 表達式的列中,所以這些值的結果將是非肯定性的。如:select a, b, c from tbl where foo="bar" group by a,該 SQL 返回的結果就是不肯定的。
- Case:
select c1,c2,c3 from t1 where c2='foo' group by c2
9五、未使用 ORDER BY 的 LIMIT 查詢
- Item: RES.002
- Severity: L4
- Content: 沒有 ORDER BY 的 LIMIT 會致使非肯定性的結果,這取決於查詢執行計劃。
- Case:
select col1,col2 from tbl where name=xx limit 10
9六、UPDATE/DELETE 操做使用了 LIMIT 條件
- Item: RES.003
- Severity: L4
- Content: UPDATE/DELETE 操做使用 LIMIT 條件和不添加 WHERE 條件同樣危險,它可將會致使主從數據不一致或從庫同步中斷。
- Case:
UPDATE film SET length = 120 WHERE title = 'abc' LIMIT 1;
9七、UPDATE/DELETE 操做指定了 ORDER BY 條件
- Item: RES.004
- Severity: L4
- Content: UPDATE/DELETE 操做不要指定 ORDER BY 條件。
- Case:
UPDATE film SET length = 120 WHERE title = 'abc' ORDER BY title
9八、UPDATE 語句可能存在邏輯錯誤,致使數據損壞
- Item: RES.005
- Severity: L4
- Content: 在一條 UPDATE 語句中,若是要更新多個字段,字段間不能使用 AND ,而應該用逗號分隔。
- Case:
update tbl set col = 1 and cl = 2 where col=3;
9九、永遠不真的比較條件
- Item: RES.006
- Severity: L4
- Content: 查詢條件永遠非真,若是該條件出如今 where 中可能致使查詢無匹配到的結果。
- Case:
select * from tbl where 1 != 1;
100、永遠爲真的比較條件
Item: RES.007 Severity: L4 Content: 查詢條件永遠爲真,可能致使 WHERE 條件失效進行全表查詢。 Case:函數
select * from tbl where 1 = 1;
10一、不建議使用 LOAD DATA/SELECT ... INTO OUTFILE
- Item: RES.008
- Severity: L2
- Content: SELECT INTO OUTFILE 須要授予 FILE 權限,這經過會引入安全問題。LOAD DATA 雖然能夠提升數據導入速度,但同時也可能致使從庫同步延遲過大。
- Case:
LOAD DATA INFILE 'data.txt' INTO TABLE db2.my_table;
10二、請謹慎使用 TRUNCATE 操做
- Item: SEC.001
- Severity: L0
- Content: 通常來講想清空一張表最快速的作法就是使用 TRUNCATE TABLE tbl_name; 語句。但 TRUNCATE 操做也並不是是毫無代價的,TRUNCATE TABLE 沒法返回被刪除的準確行數,若是須要返回被刪除的行數建議使用 DELETE 語法。TRUNCATE 操做還會重置 AUTO_INCREMENT,若是不想重置該值建議使用 DELETE FROM tbl_name WHERE 1; 替代。TRUNCATE 操做會對數據字典添加源數據鎖 (MDL),當一次須要 TRUNCATE 不少表時會影響整個實例的全部請求,所以若是要 TRUNCATE 多個表建議用 DROP+CREATE 的方式以減小鎖時長。
- Case:
TRUNCATE TABLE tbl_name
10三、不使用明文存儲密碼
- Item: SEC.002
- Severity: L0
- Content: 使用明文存儲密碼或者使用明文在網絡上傳遞密碼都是不安全的。若是攻擊者可以截獲您用來插入密碼的 SQL 語句,他們就能直接讀到密碼。另外,將用戶輸入的字符串以明文的形式插入到純 SQL 語句中,也會讓攻擊者發現它。若是您可以讀取密碼,黑客也能夠。解決方案是使用單向哈希函數對原始密碼進行加密編碼。哈希是指將輸入字符串轉化成另外一個新的、不可識別的字符串的函數。對密碼加密表達式加點隨機串來防護 「字典攻擊」。不要將明文密碼輸入到 SQL 查詢語句中。在應用程序代碼中計算哈希串,只在 SQL 查詢中使用哈希串。
- Case:
create table test(id int,name varchar(20) not null,password varchar(200)not null)
10四、使用 DELETE/DROP/TRUNCATE 等操做時注意備份
- Item: SEC.003
- Severity: L0
- Content: 在執行高危操做以前對數據進行備份是十分有必要的。
- Case:
delete from table where col = 'condition'
10五、建議使用 datetime 替換 timestamp 類型
- Item: SKEY.005
- Severity: L4
- Content: 建議使用 datetime 替換 timestamp 類型,且默認值設置爲 1970-01-01 00:00:00。 datetime 類型能保存大範圍的值,從 1001 年到 9999 年,且與時區無關。使用 8 個字節的存儲空間(比 timestamp 多出 4 字節)
- Case:
CREATE TABLE tbl (a datetime);
10六、缺乏數據庫必須字段 last_update_time 和 is_del
- Item: SKEY.006
- Severity: L4
- Content: 數據庫必須字段 (
last_update_time
TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT ' 最後更新時間 ';is_del
TINYINT (1) UNSIGNED NOT NULL DEFAULT '0' COMMENT ' 是否刪除 0:未刪除 1:已刪除 ') - Case:
CREATE TABLE tbl (`last_update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最後更新時間'; `is_del` TINYINT (1) UNSIGNED NOT NULL DEFAULT '0' COMMENT '是否刪除 0:未刪除 1:已刪除');
10七、last_update_time 和 is_del 類型不對
- Item: SKEY.006a
- Severity: L4
- Content: 數據庫必須字段 (
last_update_time
TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT ' 最後更新時間 ';is_del
TINYINT (1) UNSIGNED NOT NULL DEFAULT '0' COMMENT ' 是否刪除 0:未刪除 1:已刪除 ') - Case:
CREATE TABLE tbl (`last_update_time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最後更新時間'; `is_del` TINYINT (1) UNSIGNED NOT NULL DEFAULT '0' COMMENT '是否刪除 0:未刪除 1:已刪除');
10八、不建議使用大字段 TEXT BLOB
- Item: SKEY.010
- Severity: L1
- Content: BLOB 和 TEXT 都是爲存儲很大的數據而設計的字符串數據類型,且性能開銷較大,請檢查是否有必要使用
- Case:
CREATE TABLE tbl (a TEXT);
10九、整形建議使用 unsigned
- Item: SKEY.011
- Severity: L1
- Content: 請檢查整形是否有負數場景,如無特殊場景,建議使用 unsigned
- Case:
CREATE TABLE tbl (a int unsigned);
1十、'!=' 運算符是非標準的
- Item: STA.001
- Severity: L0
- Content: "<>" 纔是標準 SQL 中的不等於運算符。
- Case:
select col1,col2 from tbl where type!=0
1十一、庫名或表名點後建議不要加空格
- Item: STA.002
- Severity: L1
- Content: 當使用 db.table 或 table.column 格式訪問表或字段時,請不要在點號後面添加空格,雖然這樣語法正確。
- Case:
select col from sakila. film
1十二、索引發名不規範
- Item: STA.003
- Severity: L1
- Content: 建議普通二級索引以 idx_爲前綴,惟一索引以 uniq_爲前綴。
- Case:
select col from now where type!=0
11三、起名時請不要使用字母、數字和下劃線以外的字符
- Item: STA.004
- Severity: L1
- Content: 以字母或下劃線開頭,名字只容許使用字母、數字和下劃線。請統一大小寫,不要使用駝峯命名法。不要在名字中出現連續下劃線 '__',這樣很難辨認。
- Case:
CREATE TABLE ` abc` (a int);
11四、MySQL 對子查詢的優化效果不佳
- Item: SUB.001
- Severity: L4
- Content: MySQL 將外部查詢中的每一行做爲依賴子查詢執行子查詢。 這是致使嚴重性能問題的常見緣由。這可能會在 MySQL 5.6 版本中獲得改善,但對於 5.1 及更早版本,建議將該類查詢分別重寫爲 JOIN 或 LEFT OUTER JOIN。
- Case:
select col1,col2,col3 from table1 where col2 in(select col from table2)
11五、若是您不在意重複的話,建議使用 UNION ALL 替代 UNION
- Item: SUB.002
- Severity: L2
- Content: 與去除重複的 UNION 不一樣,UNION ALL 容許重複元組。若是您不關心重複元組,那麼使用 UNION ALL 將是一個更快的選項。
- Case:
select teacher_id as id,people_name as name from t1,t2 where t1.teacher_id=t2.people_id union select student_id as id,people_name as name from t1,t2 where t1.student_id=t2.people_id
11六、考慮使用 EXISTS 而不是 DISTINCT 子查詢
- Item: SUB.003
- Severity: L3
- Content: DISTINCT 關鍵字在對元組排序後刪除重複。相反,考慮使用一個帶有 EXISTS 關鍵字的子查詢,您能夠避免返回整個表。
- Case:
SELECT DISTINCT c.c_id, c.c_name FROM c,e WHERE e.c_id = c.c_id
11七、執行計劃中嵌套鏈接深度過深
- Item: SUB.004
- Severity: L3
- Content: MySQL 對子查詢的優化效果不佳,MySQL 將外部查詢中的每一行做爲依賴子查詢執行子查詢。 這是致使嚴重性能問題的常見緣由。
- Case:
SELECT * from tb where id in (select id from (select id from tb))
11八、子查詢不支持 LIMIT
- Item: SUB.005
- Severity: L8
- Content: 當前 MySQL 版本不支持在子查詢中進行 'LIMIT & IN/ALL/ANY/SOME'。
- Case:
SELECT * FROM staff WHERE name IN (SELECT NAME FROM customer ORDER BY name LIMIT 1)
11九、不建議在子查詢中使用函數
- Item: SUB.006
- Severity: L2
- Content: MySQL 將外部查詢中的每一行做爲依賴子查詢執行子查詢,若是在子查詢中使用函數,即便是 semi-join 也很難進行高效的查詢。能夠將子查詢重寫爲 OUTER JOIN 語句並用鏈接條件對數據進行過濾。
- Case:
SELECT * FROM staff WHERE name IN (SELECT max(NAME) FROM customer)
120、不建議使用分區表
- Item: TBL.001
- Severity: L4
- Content: 不建議使用分區表
- Case:
CREATE TABLE trb3(id INT, name VARCHAR(50), purchased DATE) PARTITION BY RANGE(YEAR(purchased)) (PARTITION p0 VALUES LESS THAN (1990), PARTITION p1 VALUES LESS THAN (1995), PARTITION p2 VALUES LESS THAN (2000), PARTITION p3 VALUES LESS THAN (2005) );
12一、請爲表選擇合適的存儲引擎
- Item: TBL.002
- Severity: L4
- Content: 建表或修改表的存儲引擎時建議使用推薦的存儲引擎,如:innodb
- Case:
create table test(`id` int(11) NOT NULL AUTO_INCREMENT)
12二、以 DUAL 命名的表在數據庫中有特殊含義
- Item: TBL.003
- Severity: L8
- Content: DUAL 表爲虛擬表,不須要建立便可使用,也不建議服務以 DUAL 命名錶。
- Case:
create table dual(id int, primary key (id));
12三、表的初始 AUTO_INCREMENT 值不爲 0
- Item: TBL.004
- Severity: L2
- Content: AUTO_INCREMENT 不爲 0 會致使數據空洞。
- Case:
CREATE TABLE tbl (a int) AUTO_INCREMENT = 10;
12四、請使用推薦的字符集
- Item: TBL.005
- Severity: L4
- Content: 表字符集只容許設置爲 utf8mb4
- Case:
CREATE TABLE tbl (a int) DEFAULT CHARSET = latin1;
12五、不建議使用視圖
- Item: TBL.006
- Severity: L1
- Content: 不建議使用視圖
- Case:
create view v_today (today) AS SELECT CURRENT_DATE;
12六、不建議使用臨時表
- Item: TBL.007
- Severity: L1
- Content: 不建議使用臨時表
- Case:
CREATE TEMPORARY TABLE `work` (`time` time DEFAULT NULL) ENGINE=InnoDB;