乾貨!SQL性能優化,書寫高質量SQL語句

寫SQL語句的時候咱們每每關注的是SQL的執行結果,可是是否真的關注了SQL的執行效率,是否注意了SQL的寫法規範?mysql

如下的乾貨分享是在實際開發過程當中總結的,但願對你們有所幫助!sql

1. limit分頁優化

當偏移量特別大時,limit效率會很是低。數據庫

SELECT id FROM A LIMIT 1000,10 很快緩存

SELECT id FROM A LIMIT 90000,10 很慢bash

方案一函數

select id from A order by id limit 90000,10;
複製代碼

若是咱們結合order by使用。很快,0.04秒就OK。 由於使用了id主鍵作索引!固然,是否可以使用索引還須要根據業務邏輯來定,這裏只是爲了提醒你們,在分頁的時候還需謹慎使用!性能

方案二測試

select id from A order by id  between 90000 and 90010;
複製代碼

2.利用limit 1 、top 1 取得一行

有些業務邏輯進行查詢操做時(特別是在根據某一字段DESC,取最大一筆).可使用limit 1 或者 top 1 來終止[數據庫索引]繼續掃描整個表或索引。優化

反例ui

SELECT id FROM A LIKE 'abc%' 
複製代碼

正例

SELECT id FROM A LIKE 'abc%' limit 1
複製代碼

3. 任何狀況都不要用 select * from table ,用具體的字段列表替換"*",不要返回用不到的字段,避免全盤掃描!

反例

SELECT * FROM A
複製代碼

正例

SELECT id FROM A 
複製代碼

4. 批量插入優化

反例

INSERT into person(name,age) values('A',24)
INSERT into person(name,age) values('B',24)
INSERT into person(name,age) values('C',24)
複製代碼

正例

INSERT into person(name,age) values('A',24),('B',24),('C',24),
複製代碼

sql語句的優化主要在於對索引的正確使用,而咱們在開發中常常犯的錯誤即是對錶進行全盤掃描,一來影響性能,而來耗費時間!

5.like語句的優化

反例

SELECT id FROM A WHERE name like '%abc%'
複製代碼

因爲abc前面用了「%」,所以該查詢必然走全表查詢,除非必要(模糊查詢須要包含abc),不然不要在關鍵詞前加%

正例

SELECT id FROM A WHERE name like 'abc%'
複製代碼

實例

mysql版本:5.7.26

select nick_name from member where nick_name like '%小明%'
複製代碼

like'%小明%' 並未使用索引!

select nick_name from member where nick_name like '小明%'
複製代碼

like'小明%' 成功使用索引!

6.where子句使用or的優化

一般使用 union all 或 union 的方式替換「or」會獲得更好的效果。where子句中使用了or關鍵字,索引將被放棄使用。

反例

SELECT id FROM A WHERE num = 10 or num = 20
複製代碼

正例

SELECT id FROM A WHERE num = 10 union all SELECT id FROM A WHERE num=20

複製代碼

7.where子句中使用 IS NULL 或 IS NOT NULL 的優化

反例

SELECT id FROM A WHERE num IS NULL
複製代碼

在where子句中使用 IS NULL 或 IS NOT NULL 判斷,索引將被放棄使用,會進行全表查詢

正例

優化成num上設置默認值0,確保表中num沒有null值, IS NULL 的用法在實際業務場景下SQL使用率極高,咱們應注意避免全表掃描

SELECT id FROM A WHERE num=0
複製代碼

8.where子句中對字段進行表達式操做的優化

不要在where子句中的「=」左邊進行函數、算數運算或其餘表達式運算,不然系統將可能沒法正確使用索引。

  • 1
SELECT id FROM A WHERE datediff(day,createdate,'2019-11-30')=0 
複製代碼

優化爲

SELECT id FROM A WHERE createdate>='2019-11-30' and createdate<'2019-12-1'
複製代碼
  • 2
SELECT id FROM A WHERE year(addate) <2020
複製代碼

優化爲

SELECT id FROM A where addate<'2020-01-01'
複製代碼

9.排序的索引問題 

mysql查詢只是用一個索引,所以若是where子句中已經使用了索引的話,那麼order by中的列是不會使用索引。所以數據庫默認排序能夠符合要求狀況下不要使用排序操做;

儘可能不要包含多個列的排序,若是須要最好給這些列建立複合索引

10. 儘可能用 union all 替換 union

union和union all的差別主要是前者須要將兩個(或者多個)結果集合並後再進行惟一性過濾操做,這就會涉及到排序,增長大量的cpu運算,加大資源消耗及延遲。因此當咱們能夠確認不可能出現重複結果集或者不在意重複結果集的時候,儘可能使用union all而不是union

11.Inner join 和 left join、right join、子查詢

  • 第一:inner join內鏈接也叫等值鏈接是,left/rightjoin是外鏈接。
SELECT A.id,A.name,B.id,B.name FROM A LEFT JOIN B ON A.id =B.id;

SELECT A.id,A.name,B.id,B.name FROM A RIGHT JOIN ON B A.id= B.id;

SELECT A.id,A.name,B.id,B.name FROM A INNER JOIN ON A.id =B.id;
複製代碼

通過來之多方面的證明 inner join性能比較快,由於inner join是等值鏈接,或許返回的行數比較少。可是咱們要記得有些語句隱形的用到了等值鏈接,如:

SELECT A.id,A.name,B.id,B.name FROM A,B WHERE A.id = B.id;

推薦:能用inner join鏈接儘可能使用inner join鏈接

  • 第二:子查詢的性能又比外鏈接性能慢,儘可能用外鏈接來替換子查詢。

反例

mysql是先對外表A執行全表查詢,而後根據uuid逐次執行子查詢,若是外層表是一個很大的表,咱們能夠想象查詢性能會表現比這個更加糟糕。

Select* from A where exists (select * from B where id>=3000 and A.uuid=B.uuid);
複製代碼

執行時間:2s左右

正例

Select* from A inner join B ON A.uuid=B.uuid where b.uuid>=3000;  這個語句執行測試不到一秒;
複製代碼

執行時間:1s不到

  • 第三:使用JOIN時候,應該用小的結果驅動大的結果

left join 左邊表結果儘可能小,若是有條件應該放到左邊先處理,right join同理反向。如:

反例

Select * from A left join B A.id=B.ref_id where  A.id>10
複製代碼

正例

select * from (select * from A wehre id >10) T1 left join B on T1.id=B.ref_id;
複製代碼

12.exist & in 優化

SELECT * from A WHERE id in ( SELECT id from B )
複製代碼
SELECT * from A WHERE id EXISTS ( SELECT 1 from A.id= B.id )
複製代碼

分析:

in 是在內存中遍歷比較

exist 須要查詢數據庫,因此當B的數據量比較大時,exists效率優於in**

in()只執行一次,把B表中的全部id字段緩存起來,以後檢查A表的id是否與B表中的id相等,若是id相等則將A表的記錄加入到結果集中,直到遍歷完A表的全部記錄。

In 操做的流程原理如同一下代碼

List resultSet={};

    Array A=(select * from A);
    Array B=(select id from B);

    for(int i=0;i<A.length;i++) {
          for(int j=0;j<B.length;j++) {
          if(A[i].id==B[j].id) {
             resultSet.add(A[i]);
             break;
          }
       }
    }
    return resultSet;
複製代碼

能夠看出,當B表數據較大時不適合使用in(),由於會把B表數據所有遍歷一次

如:A表有10000條記錄,B表有1000000條記錄,那麼最多有可能遍歷10000*1000000次,效率不好。

再如:A表有10000條記錄,B表有100條記錄,那麼最多有可能遍歷10000*100次,遍歷次數大大減小,效率大大提高。

  結論:in()適合B表比A表數據小的狀況

exist()會執行A.length()次,執行過程代碼以下

List resultSet={};
Array A=(select * from A);
for(int i=0;i<A.length;i++) {
    if(exists(A[i].id) {  //執行select 1 from B where B.id=A.id是否有記錄返回
       resultSet.add(A[i]);
    }
}return resultSet;
複製代碼

當B表比A表數據大時適合使用exists(),由於它沒有那麼多遍歷操做,只須要再執行一次查詢就行。

如:A表有10000條記錄,B表有1000000條記錄,那麼exists()會執行10000次去判斷A表中的id是否與B表中的id相等。

如:A表有10000條記錄,B表有100000000條記錄,那麼exists()仍是執行10000次,由於它只執行A.length次,可見B表數據越多,越適合exists()發揮效果。

再如:A表有10000條記錄,B表有100條記錄,那麼exists()仍是執行10000次,還不如使用in()遍歷10000*100次,由於in()是在內存裏遍歷比較,而exists()須要查詢數據庫,

咱們都知道查詢數據庫所消耗的性能更高,而內存比較很快。   

結論:exists()適合B表比A表數據大的狀況

相關文章
相關標籤/搜索