一、前言
每個程序員的身上,都背負着幾行祖傳代碼,這些代碼,沒有注釋,令人久久尋味
這不就在前幾天,上家公司的同事突然找到我,曬出了我的一句祖傳 sql.....
二、情節對話
圖1:

圖2:

說實話,當時看到這句sql的時候,我的心情是這樣的

“ 這個真的是我寫的? ”
“ 我寫這玩意干啥? ”
“ 這么多的查詢嵌套和計算效率會不會太低? ”
“ 我自己都看哭了,他能看懂嗎? ”
...
一連串的自問自答,我想起來了,是由於之前的某張統計表設計的不太合理,導致表內數據時間段內冗余較多,而統計展示又要很精細,所以逼出了我的這句祖傳sql,嗯,都是表設計的鍋,哈哈哈,甩鍋成功!
其實當時,只要稍稍改一下表設計和統計入數據的代碼邏輯,那么此處的sql就簡單了,但是我太懶了,不想改別人的設計,也不想改別人的代碼,所以只能苦了我的這位同事了。大家 以我為戒,切勿跟風
三、題外:你的sql太慢了,應該如何優化?
1、統一SQL語句的格式
如,對於以下兩句SQL語句,很多人認為是相同的,但是,數據庫查詢優化器認為是不同的。
select * from student
select * From student
雖然只是大小寫不同,查詢分析器就認為是兩句不同的SQL語句,必須進行兩次解析。生成2個執行計划。
所以作為程序員,應該保證相同的查詢語句在任何地方都一致,多一個空格都不行!
2、少用 * ,用具體的字段列表代替“*”,不要返回用不到的任何字段
3、對查詢進行優化,應盡量避免全表掃描
1)應考慮在 where 及 order by 涉及的列上建立索引。
2)應盡量避免在 where 子句中對字段進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:
select id from t where score is null
可以在score上設置默認值0,確保表中score列沒有null值,然后這樣查詢:
select id from t where score=0
3)應盡量避免在 where 子句中使用!=或<>操作符,否則將導致引擎放棄使用索引而進行全表掃描
4)應盡量避免在 where 子句中使用 or 來連接條件,否則將導致引擎放棄使用索引而進行全表掃描,如:
select id from t where num=10 or num=20
可以這樣查詢:
select id from t where num=10
union all
select id from t where num=20
5)慎用in 和 not in,否則會導致全表掃描,如:
select id from t where num in(1,2,3)
對於連續的數值,能用 between 就不要用 in 了:
select id from t where num between 1 and 3
6)合理使用like模糊查詢
有時候需要進行一些模糊查詢,如:
select * from contact where username like ‘%yue%’
關鍵詞 %yue%,由於yue前面用到了“%”,因此該查詢必然走全表掃描,除非必要,否則不要在關鍵詞前加%
7)應盡量避免在 where 子句中對字段進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:
select id from t where num/2=100
應改為:
select id from t where num=100*2
8)應盡量避免在where子句中對字段進行函數操作,這將導致引擎放棄使用索引而進行全表掃描。如:查詢name以abc開頭的id
select id from t where substring(name,1,3)='abc'
應改為:
select id from t where name like 'abc%'
4、用 exists 代替 in
很多時候用 exists 代替 in 是一個好的選擇,Exists只檢查存在性,性能比in強很多。例:
select num from a where num in(select num from b)
用下面的語句替換:
select num from a where exists(select 1 from b where num=a.num)
5、不要把SQL語句寫得太長,太過冗余、要簡潔;能用一句千萬不要用兩句
6、考慮使用“臨時表”暫存中間結果
簡化SQL語句的重要方法就是采用臨時表暫存中間結果,但是,臨時表的好處遠遠不止這些,將臨時結果暫存在臨時表,后面的查詢就在tempdb中了,
這可以避免程序中多次掃描主表,也大大減少了程序執行中“共享鎖”阻塞“更新鎖”,減少了阻塞,提高了並發性能。
7、注意所以字段作為條件
在使用索引字段作為條件時,如果該索引是復合索引,那么必須使用到該索引中的第一個字段作為條件時才能保證系統使用該索引,
否則該索引將不會被使用,並且應盡可能的讓字段順序與索引順序相一致。
8、盡量使用數字型字段,若只含數值信息的字段盡量不要設計為字符型,這會降低查詢和連接的性能,並會增加存儲開銷
因為引擎在處理查詢和連接時會逐個比較字符串中每一個字符,而對於數字型而言只需要比較一次就夠了。
9、盡可能的使用 varchar 代替 char ,因為首先變長字段存儲空間小,可以節省存儲空間, 其次對於查詢來說,在一個相對較小的字段內搜索效率顯然要高些
10、避免頻繁創建和刪除臨時表,以減少系統表資源的消耗
11、盡量避免使用游標,因為游標的效率較差,如果游標操作的數據超過1萬行,那么就應該考慮改寫
12、盡量避免大事務操作,提高系統並發能力
13、盡量避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理
14、選擇最有效率的表名順序
數據庫的解析器按照從右到左的順序處理FROM子句中的表名,FROM子句中寫在最后的表將被最先處理,在FROM子句中包含多個表的情況下,
你必須選擇記錄條數最少的表放在最后,如果有3個以上的表連接查詢,那就需要選擇那個被其他表所引用的表放在最后。
1)如果三個表是完全無關系的話,將記錄和列名最少的表,寫在最后,然后依次類推
2)如果三個表是有關系的話,將引用最多的表,放在最后,然后依次類推
15、用TRUNCATE替代DELETE
16、用WHERE子句替換HAVING子句
17、使用內部函數提高SQL效率
18、注意WHERE子句中的連接順序
數據庫采用自右而左的順序解析WHERE子句,根據這個原理,表之間的連接必須寫在其他WHERE條件之左,
那些可以過濾掉最大數量記錄的條件必須寫在WHERE子句的之右。
【備注】:看完記得優化自己的sql,別被同事追着打,哈哈哈
