程序員老鳥寫sql語句的經驗之談、百萬數據查詢優化技巧三十則

原文地址http://blog.csdn.net/chenleixing/article/details/42610529

一、程序員老鳥寫sql語句的經驗之談

做管理系統的,無論是bs結構的還是cs結構的,都不可避免的涉及到數據庫表結構的設計,sql語句的編寫等。因此在開發系統的時候,表結構設計是否合理,sql語句是否標準,寫出的sql性能是否優化往往會成爲公司衡量程序員技術水平的標準。

我們程序員不是dba,不需要時刻關注sql運行時間,想方設法優化表結構,存儲空間,優化表讀取速度等等,但是在開發系統時,時刻保持優良的寫sql語句的作風是很有必要的,這關乎到個人在公司的聲譽,嘿嘿,你懂的。。。

新來的程序員老鳥,在一個開發團隊中,需要表現一下自己的水平,奠定在公司的地位,需要努力表現一把,最簡單的從寫的sql語句就很容易表現出來,曾經就有一次,一個老程序員,上面定位是要做團隊領導的,先歷練一下做個制單的模塊,列表sql中有一列這位老鳥直接寫了個select語句從別的表中取之,而不是用表之間關聯得到,一下破壞自己程序員老鳥光輝形象。

做技術的還是要注重自己的內涵,提升內功,哈哈。

閒話少說,總結一點程序員老鳥寫sql順手拈來的功夫吧:

1. 不論一個sql中涉及到多個表,每次都用兩個表(結果集)操作,得到新的結果後,再和下一個表(結果集)操作。

2. 避免在select f1,(select f2 from tableB ).... from tableA 這樣得到字段列。直接用tableA和tableB關聯得到A.f1,B.f2就可以了。

3.避免隱含的類型轉換
 如 
 select id from employee where emp_id='8'  (錯)
 select id from employee where emp_id=8    (對)
 emp_id是整數型,用'8'會默認啓動類型轉換,增加查詢的開銷。
 
4. 儘量減少使用正則表達式,儘量不使用通配符。

5. 使用關鍵字代替函數
   如:
   select id from employee where UPPER(dept) like 'TECH_DB'  (錯)
   select id from employee where SUBSTR(dept,1,4)='TECH'    (錯)
   select id from employee where dept like 'TECH%'         (對)
 
6.不要在字段上用轉換函數,儘量在常量上用
  如:
  select id from employee where to_char(create_date,'yyyy-mm-dd')='2012-10-31'  (錯)
  select id from employee where create_date=to_date('2012-10-31','yyyy-mm-dd')   (對)
  
7.不使用聯接做查詢
 如:select id from employee where first_name || last_name like 'Jo%'  (錯)
 
8. 儘量避免前後都用通配符
  如:
  select id from employee where dept like '%TECH%' (錯)
  select id from employee where dept like 'TECH%' (對)

9. 判斷條件順序
  如:
  select id from employee where creat_date-30>to_date('2012-10-31','yyyy-mm-dd')   (錯)
    select id from employee where creat_date >to_date('2012-10-31','yyyy-mm-dd')+30   (對)
    
10. 儘量使用exists而非in
 當然這個也要根據記錄的情況來定用exists還是用in, 通常的情況是用exists
 select id from employee where salary in (select salary from emp_level where....)   (錯)   
 select id from employee where salary exists(select 'X' from emp_level where ....)   (對)
 
11. 使用not exists 而非not in
    和上面的類似
    
12. 減少查詢表的記錄數範圍

13.正確使用索引
  索引可以提高速度,一般來說,選擇度越高,索引的效率越高。


14. 索引類型
  唯一索引,對於查詢用到的字段,儘可能使用唯一索引。
  還有一些其他類型,如位圖索引,在性別字段,只有男女的字段上用。

15. 在經常進行連接,但是沒有指定爲外鍵的列上建立索引

16. 在頻繁進行排序會分組的列上建立索引,如經常做group by 或 order by 操作的字段。

17. 在條件表達式中經常用到的不同值較多的列上建立檢索,在不同值少的列上不建立索引。如性別列上只有男,女兩個不同的值,就沒必要建立索引(或建立位圖索引)。如果建立索引不但不會提高查詢效率,反而會嚴重降低更新速度。

18. 在值比較少的字段做order by時,翻頁會出現記錄紊亂問題,要帶上id字段一起做order by.

19. 不要使用空字符串進行查詢
    如:
    select id from employee where emp_name like '%%' (錯)
    
20. 儘量對經常用作group by的關鍵字段做索引。

21. 正確使用表關聯
    利用外連接替換效率十分低下的not in運算,大大提高運行速度。
    如:
    select a.id from employee a where a.emp_no not in (select emp_no from employee1 where job ='SALE')  (錯)
    
22. 使用臨時表    
   在必要的情況下,爲減少讀取次數,可以使用經過索引的臨時表加快速度。
   如:
   select e.id from employee e ,dept d where e.dept_id=d.id and e.empno>1000 order by e.id   (錯)
   
   select id,empno from employee into temp_empl where empno>1000 order by id
   select m.id from temp_emp1 m,dept d where m.empno=d.id      (對)

    
 對於大數據量sql語句性能優化更多的工作就交給dba去實踐,我們程序員做好這些基本功就好了。

以上文章來着博客園web報表的博客。

二、百萬數據查詢優化技巧三十則

       1.對查詢進行優化,應儘量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。 

  2.應儘量避免在 where 子句中對字段進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:

  select id from t where num is null

  可以在num上設置默認值0,確保表中num列沒有null值,然後這樣查詢:

  select id from t where num=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.下面的查詢也將導致全表掃描:

  select id from t where name like '%abc%'

  若要提高效率,可以考慮全文檢索。

  7.如果在 where 子句中使用參數,也會導致全表掃描。因爲SQL只有在運行時纔會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然而,如果在編譯時建立訪問計劃,變量的值還是未知的,因而無法作爲索引選擇的輸入項。如下面語句將進行全表掃描:

  select id from t where num=@num <mailto:num=@num>

  可以改爲強制查詢使用索引:

  select id from t with(index(索引名)) where num=@num <mailto:num=@num>

  8.應儘量避免在 where 子句中對字段進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:

  select id from t where num/2=100

  應改爲:

  select id from t where num=100*2

  9.應儘量避免在where子句中對字段進行函數操作,這將導致引擎放棄使用索引而進行全表掃描。如:

  select id from t where substring(name,1,3)='abc'--nameabc開頭的id

  select id from t where datediff(day,createdate,'2005-11-30')=0--2005-11-30生成的id

  應改爲:

  select id from t where name like 'abc%'

  select id from t where createdate>='2005-11-30' and createdate<'2005-12-1'

  10.不要在 where 子句中的=左邊進行函數、算術運算或其他表達式運算,否則系統將可能無法正確使用索引。

  11.在使用索引字段作爲條件時,如果該索引是複合索引,那麼必須使用到該索引中的第一個字段作爲條件時才能保證系統使用該索引,否則該索引將不會被使用,並且應儘可能的讓字段順序與索引順序相一致。

  12.不要寫一些沒有意義的查詢,如需要生成一個空表結構:

  select col1,col2 into #t from t where 1=0

  這類代碼不會返回任何結果集,但是會消耗系統資源的,應改成這樣:

  create table #t(...)

  13.很多時候用 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)

  14.並不是所有索引對查詢都有效,SQL是根據表中數據來進行查詢優化的,當索引列有大量數據重複時,SQL查詢可能不會去利用索引,如一表中有字段sexmalefemale幾乎各一半,那麼即使在sex上建了索引也對查詢效率起不了作用。

  15.索引並不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因爲 insert 或 update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。

  16.應儘可能的避免更新 clustered 索引數據列,因爲 clustered 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將導致整個表記錄的順序的調整,會耗費相當大的資源。若應用系統需要頻繁更新 clustered 索引數據列,那麼需要考慮是否應將該索引建爲 clustered 索引。

  17.儘量使用數字型字段,若只含數值信息的字段儘量不要設計爲字符型,這會降低查詢和連接的性能,並會增加存儲開銷。這是因爲引擎在處理查詢和連接時會逐個比較字符串中每一個字符,而對於數字型而言只需要比較一次就夠了。

  18.儘可能的使用 varchar/nvarchar 代替 char/nchar ,因爲首先變長字段存儲空間小,可以節省存儲空間,其次對於查詢來說,在一個相對較小的字段內搜索效率顯然要高些。

  19.任何地方都不要使用 select * from t ,用具體的字段列表代替*,不要返回用不到的任何字段。

  20.儘量使用表變量來代替臨時表。如果表變量包含大量數據,請注意索引非常有限(只有主鍵索引)。

  21.避免頻繁創建和刪除臨時表,以減少系統表資源的消耗。

  22.臨時表並不是不可使用,適當地使用它們可以使某些例程更有效,例如,當需要重複引用大型表或常用表中的某個數據集時。但是,對於一次性事件,最好使用導出表。

  23.在新建臨時表時,如果一次性插入數據量很大,那麼可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果數據量不大,爲了緩和系統表的資源,應先create table,然後insert

  24.如果使用到了臨時表,在存儲過程的最後務必將所有的臨時表顯式刪除,先 truncate table ,然後 drop table ,這樣可以避免系統表的較長時間鎖定。

  25.儘量避免使用遊標,因爲遊標的效率較差,如果遊標操作的數據超過1萬行,那麼就應該考慮改寫。

  26.使用基於遊標的方法或臨時表方法之前,應先尋找基於集的解決方案來解決問題,基於集的方法通常更有效。

  27.與臨時表一樣,遊標並不是不可使用。對小型數據集使用 FAST_FORWARD 遊標通常要優於其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的數據時。在結果集中包括合計的例程通常要比使用遊標執行的速度快。如果開發時間允許,基於遊標的方法和基於集的方法都可以嘗試一下,看哪一種方法的效果更好。

  28.在所有的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF 。無需在執行存儲過程和觸發器的每個語句後向客戶端發送 DONE_IN_PROC 消息。

  29.儘量避免大事務操作,提高系統併發能力。

  30.儘量避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。


發佈了26 篇原創文章 · 獲贊 7 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章