打造出色查詢:如何優化SQL查詢?

2020-12-08 讀芯術

全文共2630字,預計學習時長14分鐘

圖源:unsplash

我們致力於打造能夠較好運行並延續較長一段時間的query(查詢)。本文將給出關於優化SQL語句的幾點建議,希望能夠幫到你。

1.嘗試不去用select *來查詢SQL,而是選擇專用欄位。

反例:

select * from employee;

正例:

select id,name fromemployee;

理由:

· 通過只用必要欄位進行查詢,能夠節省資源並減少網絡開銷。

· 這樣做可能不會使用覆蓋索引,會導致一個查詢返回到表中。

2.如果已知只有一個查詢結果,推薦使用limit 1

假設有一張員工表格,想在其中找到一名叫jay的員工。

CREATE TABLE employee (

id int(11) NOT NULL,

name varchar(255) DEFAULT NULL,

age int(11) DEFAULT NULL,

date datetime DEFAULT NULL,

sex int(1) DEFAULT NULL,

PRIMARY KEY (`id`) );

反例:

select id,name from employeewhere name='jay';

正例:

select id,name from employeewhere name='jay' limit 1;

理由:添加limit 1後,查找到相應的記錄時,便不會繼續查找下去,效率會大大提高。

3.嘗試避免在 where 子句中使用or來連接條件

創建一個新的用戶表格,其有一個常規索引userId,表格結構如下:

CREATE TABLE `user` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`userId` int(11) NOT NULL,

`age` int(11) NOT NULL,

`name` varchar(255) NOT NULL,

PRIMARY KEY (`id`),

KEY `idx_userId` (`userId`) )

現在假設需要查詢userid為1或為18歲的用戶,使用如下的SQL就會很簡單。

反例:

select * from user where userid = 1 or age = 18;

正例:

//Use union all

select * from user where userid=1

union all

select * from user where age = 18;//Or write two separate SQL

select * from user where userid=1;

select * from user where age = 18;

理由:or 的使用可能會使索引無效,因此需要進行全表掃描。

在or 無索引的情況下,假設已採用userId索引,但是當涉及到 age(年齡)查詢條件時,必須執行全表掃描,其過程分為三步:全表掃描+索引掃描+合併。

圖源:unsplash

4.儘可能避免在where子句中使用!=或<>運算符,否則,引擎將放棄使用索引並執行全表掃描。

反例:

select age,name from user where age<>18;

正例:

//You can consider separate two sql writeselect age,name from user where age <18;

select age,name from user where age>18;

理由:使用!=和<>可能使索引無效。

5.優化limit分頁

通常用limits來實現日常分頁,但當偏移量特別大時,查詢效率便會降低。因為Mysql不會跳過偏移量,而是直接獲取數據。

反例:

select id,name,age from employeelimit 10000,10;

正例:

//Solution 1: Return the largest record (offset) of the last query

select id,name from employeewhere id>10000 limit 10;//Solution 2: order by + index

select id,name from employeeorder by id limit 10000,10;

理由:

· 如果使用了優化方案1,則會返回最末的查詢記錄(偏移量),因此可以跳過該偏移量,效率自然會大幅提高。

· 選項二:使用+索引排序,也可以提高查詢效率。

6.優化like語句

在日常開發中,如果使用模糊關鍵字查詢,我們很容易想到like,但like可能會使索引無效。

反例:

select userId,name from user where userId like '%123';

正例:

select userId,name from user where userId like '123%';

理由:https://medium.com/@pawanjain.432/hey-thanks-dovid-for-pointing-out-a-typo-in-13-1000a4103fe6

7.使用where條件限制將要查詢的數據來避免返回額外行

假設要查詢一名用戶是否為會員,老式執行代碼會這樣做。

反例:

List<Long> userIds = sqlMap.queryList("select userId from userwhere isVip=1");boolean isVip = userIds.contains(userId);

正例:

Long userId = sqlMap.queryObject("select userId from user whereuserId='userId' and isVip='1' ")boolean isVip = userId!=null;

理由:能夠檢查需要的數據,避免返回非必要數據,並能節省費用和計算機開銷。

圖源:unsplash

8.考慮在where子句中使用默認值而不是null

反例:

select * from user where age is not null;

正例:

select * from user where age>0; //Set 0 as default

理由:如果用默認值取代null值,則通常可以建立索引,與此同時,表達式將相對清晰。

9.如果插入數據過多,可以考慮批量插入

反例:

for(User u :list){

INSERT into user(name,age)values(#name#,#age#)

}

正例:

//One batch of 500 inserts, carried out in batchesinsert intouser(name,age) values

<foreach collection="list" item="item"index="index" separator=",">

(#{item.name},#{item.age})

</foreach>

理由:批量插入性能良好且省時。

打個比方,在有電梯的情況下,你需要將1萬塊磚移送到建築物的頂部。電梯一次可以放置適當數量的磚塊(最多500塊),你可以選擇一次運送一塊磚,也可以一次運送500塊。哪種方案更好?

10.謹慎使用distinct關鍵詞

Distinct關鍵詞通常用於過濾重複記錄以返回唯一記錄。當其被用於查詢一個或幾個欄位時,Distinct關鍵詞將為查詢帶來優化效果。然而,在欄位過多的情況下,Distinct關鍵詞將大大降低查詢效率。

反例:

SELECT DISTINCT * from user;

正例:

select DISTINCT name from user;

理由:帶有「distinct」語句的CPU時間和佔用時間高於沒有「 distinct」的語句。

如果在查詢多欄位時使用distinct,資料庫引擎將比較數據,並濾除重複的數據。然而,該比較和濾除過程將消耗系統資源和CPU時間。

圖源:unsplash

11.刪除多餘和重複的索引

反例:

KEY `idx_userId` (`userId`)

KEY `idx_userId_age` (`userId`,`age`)

正例:

//Delete the userId index, because the combined index (A, B) is equivalentto creating the (A) and (A, B) indexesKEY `idx_userId_age` (`userId`,`age`)

理由:若保留重複的索引,那麼優化程序在優化查詢時也需要對其進行一一考量,這會影響性能。

12.如果數據量很大,優化 modify或delete語句

避免同時修改或刪除過多數據,因其將導致CPU利用率過高,從而影響他人對資料庫的訪問。

反例:

//Delete 100,000 or 1 million+ at a time?

delete from user where id <100000;//Or use single cycle operation, lowefficiency and long time

for(User user:list){

delete from user;}

正例:

//Delete in batches, such as 500 each timedelete user where id<500;

delete product where id>=500 and id<1000;

理由:一次刪除過多數據,可能會導致lock wait timeout exceed error(鎖定等待超時錯誤),因此建議分批操作。

13.使用explain分析SQL方案

在日常開發中編寫SQL時,嘗試養成習慣:使用explain來分析自己編寫的SQL,尤其是索引。

explain select * from user where userid = 10086 or age =18;

圖源:unsplash

14.嘗試用union all代替union

如果搜索結果裡沒有重複的記錄,我推薦用union all代替union。

反例:

select * from user where userid=1

union

select * from user where age = 10

正例:

select * from user where userid=1

union all

select * from user where age = 10

理由:

· 如果使用union,則無論有沒有重複的搜索結果,它都會嘗試對其進行合併、排序,然後輸出最終結果。

· 若已知搜索結果中沒有重複記錄,用union all代替union將提高效率。

15.儘可能使用數字欄位。如果欄位僅包含數字信息,嘗試不將其設置為字符類型。

反例:

`king_id` varchar(20) NOT NULL;

正例:

`king_id` int(11) NOT NULL;

理由:與數字欄位相比,字符類型將降低查詢和連接的性能,並會增加存儲開銷。

16.儘可能用varchar或nvarchar代替char或nchar

反例:

`deptName` char(100) DEFAULT NULL

正例:

`deptName` varchar(100) DEFAULT NULL

理由:

· 首先,由於可變長度欄位的存儲空間很小,該方法可以節省存儲空間。

· 其次,對於查詢而言,在相對較小的欄位中搜索會更有效率。

優化和加速SQL查詢是門技術活,常常思考和嘗試,你會打開新世界的大門。

留言點讚關注

我們一起分享AI學習與發展的乾貨

如轉載,請後臺留言,遵守轉載規範

相關焦點

  • 一次神奇的 sql 查詢經歷,group by 慢查詢優化記錄
    一、問題背景  現網出現慢查詢,在500萬數量級的情況下,單表查詢速度在30多秒,需要對sql進行優化,sql如下:  我在測試環境構造了500萬條數據,模擬了這個慢查詢。  簡單來說,就是查詢一定條件下,都有哪些用戶的,很簡單的sql,可以看到,查詢耗時為37秒。
  • 優化SQL查詢:如何寫出高性能SQL語句
    執行計劃是資料庫根據SQL語句和相關表的統計信息作出的一個查詢方案,這個方案是由查詢優化器自動分析產生的,比如一條SQL語句如果用來從一個 10萬條記錄的表中查1條記錄,那查詢優化器會選擇「索引查找」方式,如果該表進行了歸檔,當前只剩下5000條記錄了,那查詢優化器就會改變方案,採用 「全表掃描」方式。
  • SQL優化系列之 in與range 查詢
    MySQL優化器將in這種方式轉化成 n*m 種組合進行查詢,最終將返回值合併,有點類似union但是更高效。MySQL在 IN() 組合條件過多的時候會發生很多問題。查詢優化可能需要花很多時間,並消耗大量內存。新版本MySQL在組合數超過一定的數量就不進行計劃評估了,這可能導致MySQL不能很好的利用索引。
  • MySQL資料庫SQL查詢優化技巧之SELECT
    ,以及SELECT的執行流程,了解了SELECT的執行流程還是很重要的,重要我們才能有針對性地去進行優化,比如select在執行的時候 會把所以的欄位編程成大寫字母,這樣我們在起初編寫SELECT語句時就可以把所以的字母進行大寫,這樣就省去了程序為把SQL語句變成大寫的消耗,速度自然就會提高。
  • MySQL優化:定位慢查詢的兩種方法以及使用explain分析SQL
    定位慢查詢SQL在平時工作中,我想你肯定遇到過一條sql發出去了,但是等了好久才出現了返回值,這不僅僅影響了測試速度也大大降低了開發效率。所以我們有必要學習sql慢查詢定位。發現慢查詢及時優化或者提醒開發改寫。一般測試環境建議 long_query_time 設置的閥值比生產環境的小,比如生產環境是 1 秒,則測試環境建議配置成 0.5 秒。便於在測試環境及時發現一些效率低的 SQL。
  • 教你快速掌握資料庫查詢優化的實用技巧
    資料庫查詢優化的實用技巧: 本文中,abigale代表查詢字符串,ada代表數據表名,alice代表欄位名。 技巧一: 問題類型:ACCESS資料庫欄位中含有日文片假名或其它不明字符時查詢會提示內存溢出。
  • SQL 查詢優化之 WHERE 和 LIMIT 使用索引的奧秘
    查詢結果:第一條要5.018s,第二條0.016s為什麼會是這樣的結果呢?第一,acct_id和create_time都有索引,不應該出現5s查詢時間這麼慢啊 仔細觀察會發現,索引只使用了idx_create_time,沒有用到idx_acct_id。這能解釋第一條sql很慢,因為where查詢未用到索引,那麼第二條為什麼這麼快?看起來匪夷所思,其實搞清楚mysql查詢的原理之後,其實很簡單。
  • SQL語句面試題目:一般查詢和高級子查詢(上)
    通過兩條 sql 查詢:[sql] view plain copy select manager_id   from employees   where lower(last_name) = 'chen'      select *   from employees
  • 帶臨時表的SQL查詢語句的優化方法
    觀察此執行計劃,CBO預估的查到數據非常少,但由於報表的SQL語句中使用上了臨時表(TYBBSALEDAILYBD21 ),而臨時表的數據在庫中是無法查看到的,我也不清楚臨時表的數據是如何生成的,因此,無法判斷CBO預估的臨時表的行數是否準確,該如何下手?  此時,想起測試環境上,有上月對該庫進行RMAN恢復測試後留下的測試庫。
  • Mysql中一條SQL查詢語句是如何執行的?
    2.查詢流程解析select * from table1 where ID=10;這條語句相信大家再熟悉不過了,下面我們就看看這一條語句在mysql中是怎麼執行的。第一步:一條sql語句要經過連接器,客戶端要和mysql建立連接。
  • 用Select×進行SQL查詢的七宗罪
    【51CTO.com快譯】如今,網上許多文章都已明確地指出:使用「SELECT * 」作為SQL查詢方式是一種極其危險的代碼書寫習慣。開發人員應該儘量在自己的程序中避免出現此類查詢,取而代之的應該是明確地指定要查詢的列名。不過,大家可能只是「知其然,而不知其所以然」。
  • MySQL如何完成一次查詢?
    MySQL相信大家都不陌生,是一種關係型資料庫,通過sql語言操作數據的增刪改查。那麼從發出一條sql指令到返回結果mysql都做了什麼事情呢?
  • 在 Linux 上用 SQL 語句查詢 Apache 日誌
    這次,我們會通過另類的途徑來分析 Apache access 日誌,我們使用的工具是 asql。asql 是一個開源的工具,它能夠允許使用者使用 SQL 語句來查詢日誌,從而通過更加友好的格式展現相同的信息。
  • sqltoy-orm-4.16.16 發版,並行查詢場景增強、級聯增加排序
    -- <table name="SQLTOY_DICT_DETAIL" update-cascade="delete" load="STATUS=1" orderBy=""/> --></cascade>2、並行查詢可以自行設置每個查詢的條件參數3、優化findEntity 資料庫關鍵詞的處理4、sqltoy-boot-starter
  • sql子查詢面試題
    這是《從零學會sql》系列課程第4節課《複雜查詢》的練習題,也是常考常考的面試題。
  • SQL 查詢效率優化原則
    1.3 結論從這兩種方式查詢資料庫結果看,建立索引之後查詢速度提高了些,現在數據量還不明顯,如果表中有10萬條速度,差異就會很明顯了.2、寫數據語句時儘可能減少表的全局掃描2.1 減少where 欄位值null判斷SELECT * FROM "tb_real_time_car"where pay_status =null如何這樣做,就會導致引擎放棄使用索引而進行全表掃描應該這樣去設置(也就是在沒有值時,我們在存資料庫時自動默認給個o值,而不是什麼都不寫):
  • 面對MySQL 查詢索引失效,程式設計師的六大優化技巧!
    不知道你有沒有碰到過這種情況,一條創建了索引的SQL語句在查詢過程中卻沒有使用索引,或是一條本來可以執行的很快的語句,卻由於MySQL選錯了索引,而導致查詢速度變得很慢?充分優化和利用索引能夠大大提高數據的查詢效率,但是在實際的應用中MySQL可能並不總會選擇合適且效率高的索引。
  • sagacity-sqltoy-4.16.3 發版,融合 jpa 式對象 crud+極致查詢!
    if中存在2個邏輯判斷表達式切割bug,增加@if(:status in '1,2,3') 和@if(:status out '1,2,3') 判斷邏輯,進一步增強if判斷能力3、優化日誌輸出,排除批量更新和保存提示「無參數」導致理解偏差4、優化convertType功能,不強制要求做exception捕獲處理快速了解 sqltoy-orm:
  • 女朋友都能看懂的,SQL優化乾貨
    '%老師'優化:在欄位後面使用模糊查詢select * from teacher where name like '李%'如果一定要在欄位開頭模糊查詢,那可以使用INSTR(str,substr)意思是:在字符串str裡面,字符串substr出現的第一個位置(index
  • 《MySQL慢查詢優化》之SQL語句及索引優化
    1、慢查詢優化方式伺服器硬體升級優化Mysql伺服器軟體優化資料庫表結構優化SQL語句及索引優化>本文重點關注於SQL語句及索引優化,關於其他優化方式以及索引原理等,請關注本人《MySQL慢查詢優化》系列博文。