如何寫出高質量效率高的sql語句,看完這篇就懂了

本文主要從工作經驗中總結出來的經驗總結sql語句優化問題,下面我們用Demo來具體說明如何提高sql的執行效率:

1、關於limit分頁優化的問題

SELECT * FROM message_1 LIMIT 10000,10

這條語句執行速度很快,當我們把語句改成下面語句的時候再看看用了多久?

SELECT * FROM message_1 LIMIT 1000000,10

運行結果如下圖:

上面的語句整整用了17.7秒!這麼慢,用戶可是等不了了,那麼我們如何優化這句sql呢?

解決方法:

我們加“order by id”改造後的sql爲:

SELECT * FROM message_1 order by id LIMIT 1000000,10

執行圖如下:

只用了1.1秒,可以說稍微改動一下,效率提升了17倍。這是因爲這個sql語句使用了主鍵id來作爲索引,所以速度很快。

2、like語句的優化

SELECT content message_1 A WHERE content like '%我要學習%'

like後面使用了“%”,所以該sql語句查詢會進行全表掃描,使索引失效,因此速度會很慢,如果想走索引的話,可以改下下面的語句:

SELECT content FROM message_1 WHERE content like '我要學習%'

3、避免where語句中使用IS NULL或IS NOT NULL語句

SELECT content FROM message_1 WHERE content IS NULL

上面的語句同樣會使索引失效而進行全表掃描,我們在where語句中避免使用 IS NULL或者IS NOT NULL來進行條件選擇,這樣做會使索引失效,從而導致查詢速度很慢

4、不要在where條件中出現函數、算數運算或其他表達式運算

SELECT content FROM message_1 WHERE datediff(day,createTime,'2020-05-25')=0

改爲

SELECT content FROM message_1 WHERE createTime>='2020-05-01' and createTime<'2020-05-25'

這句話告訴我們不要在where條件中出現函數、算數運算或其他表達式運算,否則也會使索引失效。

5、排序的索引問題

如果mysql查詢語句中只用了一個索引,而where條件中已經使用了索引,則order by中的字段就不會使用索引。因此儘量不要同時對多個字段進行排序,如果有這樣的場景存在的話,那最好給這些字段設置聯合索引

6、union all替換union

union和union all的區別在於前者需要合併兩個以上的結果集,然後在進行唯一性過濾操作,這樣做肯定會涉及到數據的排序,增大cpu的運算與資源消耗和延遲。因此,當在確定沒有重複數據或者不關心重複數據的情況下,要使用union all。

7、left joinright join和inner join

SELECT A.id,A.name,B.id,B.name FROM message_1 LEFT JOIN message_2 b ON b.id =B.id;SELECT A.id,A.name,B.id,B.name FROM message_1 RIGHT JOIN message_2 b ON B A.id= B.id;SELECT A.id,A.name,B.id,B.name FROM message_1 INNER JOIN message_2 b ON A.id =B.id;

上面的語句運行後發現inner join運行速度比較快,因爲inner join是等值連接,返回的行數比較少。所以在項目開發過程中最好使用inner join

8、索引的數量控制在5個以內

因爲不是索引越多就越好。索引儘管提高了查詢效率,但是也是降低修改和新增的效率。而且insert和update有重建索引的可能,所以一張表的索引數最好不要超過五個,如果超過五個那麼就得針對這張表做優化處理了

9、任何情況下都禁止使用 select * from table

因爲這樣會進行全表掃描而導致效率很低

SELECT * FROM message_1

應改爲

SELECT content FROM message_1

10、優化批量插入

INSERT INTO message_1(id,content) values(1,'內容1')INSERT INTO message_1(id,content) values(2,'內容2')INSERT INTO message_1(id,content) values(3,'內容3')INSERT INTO message_1(id,content) values(4,'內容4')INSERT INTO message_1(id,content) values(5,'內容5')

應改爲

INSERT INTO message_1(id,content) values(1,'內容1'),(2,'內容2'),(3,'內容3'),(4,'內容4'),(5,'內容5'

11、使用聯合索引時,注意索引列的順序,一般遵循最左匹配原則

表結構:(有一個聯合索引 idxuseridage,userId 在前,age 在後)

CREATE TABLE `user` (  `id` int(11) NOT NULL AUTO_INCREMENT,  `userId` int(11) NOT NULL,  `age` int(11) DEFAULT NULL,  `name` varchar(255) NOT NULL,  PRIMARY KEY (`id`),  KEY `idx_userid_age` (`userId`,`age`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

反例:

select * from user where age = 10;

正例:

//符合最左匹配原則select * from user where userid=10 and age =10;//符合最左匹配原則select * from user where userid =10;

理由如下:

  • 當我們創建一個聯合索引的時候,如(k1,k2,k3),相當於創建了(k1)、(k1,k2)和(k1,k2,k3)三個索引,這就是最左匹配原則。

  • 聯合索引不滿足最左原則,索引一般會失效,但是這個還跟 MySQL 優化器有關的。

12、慎用 distinct 關鍵字

distinct 關鍵字一般用來過濾重複記錄,以返回不重複的記錄。在查詢一個字段或者很少字段的情況下使用時,給查詢帶來優化效果。但是在字段很多的時候使用,卻會大大降低查詢效率。

反例:

SELECT DISTINCT * from user;

正例:

select DISTINCT name from user;

理由:帶 distinct 的語句 CPU 時間和佔用時間都高於不帶 distinct 的語句。

因爲當查詢很多字段時,如果使用 distinct,數據庫引擎就會對數據進行比較,過濾掉重複數據,然而這個比較、過濾的過程會佔用系統資源,CPU 時間。

13 、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()需要查詢數據庫

原創聲明:本文爲【Java學習提升】原創博文,轉載請註明出處。

本文來源於公衆號:【Java學習提升】 專注於Java領域技術分享,Java知識體系學習、分享面試經驗,讓我們結伴而行,共同成長!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章