提升SQL效能
===
[TOC]
避免使用Select *
---
由於每次都以一個頁面為IO對象,故在MySQL中無論是否設定select的列,實際上拉出的資料量都是一樣多的,但假如select的對象有覆蓋到index,則可以透過index查找對應內容並吐回,進而減少IO操作
避免隨機Select資料,例如使用Rand()
---
避免使用雙重Select、子查詢
---
這是一個雙重查詢的例子:
```SQL=
SELECT * FROM table1 WHERE id (SELECT id FROM table2 WHERE address = 'A');
```
一般執行時會先查table1全部,才查table2去篩選所以效能會很差
除了SELECT,在UPDATE、DELETE等也會碰到
特別是Mysql5.5前尤其容易撞到,故請極力避免這種寫法
在MySQL5.6以後或MariaDB10,有設計改善此寫法並自動轉換為JOIN:
```SQL=
SELECT t1.* FROM table1 JOIN table2 on table1.id = table2.id
```
但自動轉換的貼心設計僅限於SELECT,所以能不要這樣寫就不要這樣寫
聯集查詢時,若已確定兩Select資料不重複,則用Union All取代Union
---
union的用途是將兩個SELECT結果一起秀出並刪掉多出的重複項目:
```SQL=
select test_pk, test_val from test1
union
select test_pk, test_val from test2
```
而union all則是忠實呈現兩個SELECT結果,即使重複也如實show出:
```SQL=
select test_pk, test_val from test1
union all
select test_pk, test_val from test2
```
由於找出並刪除重複項目牽涉到排序,故會耗損CPU資源產生延遲
所以在show出不重複聯集的情境時,若已100%確定不會有重複結果,那可以棄用union而改用不對資料多做整理的union all來節省資源
JOIN > between > IN > OR
---
高效率查詢(IN):
```SQL=
SELECT * FROM t WHERE id IN (10,20,30);
```
低效率查詢(OR):
```SQL=
SELECT * FROM t WHERE id = 10 OR id = 20 OR id = 30;
```
**IN的使用注意**:
IN查出錯誤結果時可能整組噴錯誤,盡量在確定、明確,且有限的集合內才用IN,例如IN(10, 20, 30)
外表小用exist,內表小用IN
---
外/前表小用exist:
```SQL=
select * from 表A where exists(select * from 表B where 表B.id=表A.id)
```
內/後表小用IN:
```SQL=
select * from 表A where id in (select id from 表B)
```
使用LIMIT時已">"跳過前面的offset不讀,而非僅用LIMIT m,n
---
高效率查詢(跳過前200行,只返回201開始的20行):
```SQL=
select id,name from table
where id> 200 limit 20
```
低效率查詢(讀了前200行):
```SQL=
select id,name from table
limit 200, 20
```
利用">"能做出與原表的JOIN關聯效果,來跳過那些不想讀的行數,進而只讀取offset後面所需的20行。這個問題特別容易在MySQL撞到,須盡量避免
禁用Order!Group時停掉預設的Order By
---
沒事不要用Order By
在使用Group時order by NULL來停掉預設的排序功能:
```SQL=
SELECT id,count(*) FROM t GROUP BY id ORDER BY NULL
```
特別在MySQL中會預設幫Group排序,故請用Group by NULL停掉
假如真的需要排:
1. 盡量靠index來排
確認方法:explain結果內沒有"Using Temporary"或"Using Filesort"
2. 盡量用ram臨時表格而非HDD臨時表格
透過些微調大tmp_table_size參數來達成
3. 對於極大資料量,用SQL_BIG_RESULT來使用HDD臨時表格排序
4. 優先使用where,避免使用having
高效排序:
```SQL=
SELECT JOB, AVG(SAL) FROM EMP
WHERE JOB = ‘PRESIDENT’ OR JOB = ‘MANAGER’
GROUP by JOB
```
低效排序:
```SQL=
SELECT JOB, AVG(SAL) FROM EMP
GROUP by JOB
HAVING JOB = ‘PRESIDENT’ OR JOB = ‘MANAGER’
```
將多次INSERT改成一個INSERT中多組插入
---
高效率插入:
```SQL=
INSERT INTO t(id, name)
VALUES(1, 'aaa'),(2, 'bbb'),(3, 'ccc');
```
低效率插入:
```SQL=
INSERT INTO t(id, name) VALUES(1, 'aaa');
INSERT INTO t(id, name) VALUES(2, 'bbb');
INSERT INTO t(id, name) VALUES(3, 'ccc');
```
盡量使用數字列名取代字串列名(假如情況允許)
---
由於查詢及JOIN時會逐字比較字串中每個字元,而數字列名只比較一次會相對省效能
---
**參考資料:** [[CSDN]实践中如何优化MySQL(精)](https://blog.csdn.net/qq_35642036/article/details/82820129)
###### tags: `SQL`