如何書寫高效的MySQL查詢?

WHERE子句中的書寫注意事項

  • 首先應考慮在 where 及 order by 涉及的列上創建索引。html

  • 下列操做會致使引擎放棄使用索引而進行全表掃描,是應儘可能避免的。mysql

    • 1).在where 子句中使用!=<>操做符sql

    • 2).在where子句中對字段進行null值判斷
      如:函數

      select id from t where num is null;

      能夠在num上設置默認值0,確保表中num列沒有null值,而後這樣查詢:性能

      select id from t where num=0;
    • 3).在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;
    • 4).in 和 not in 也要慎用
      如:spa

      select id from t where num in(1,2,3);

      對於連續的數值,能用between就不要用in
      如:.net

      select id from t where num between 1 and 3;

      不少時候能夠用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).在where子句中使用參數
      由於SQL只有在運行時纔會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時,它必須在編譯時進行選擇。
      若是在編譯時創建訪問計劃,變量的值仍是未知的,於是沒法做爲索引選擇的輸入項。
      以下面語句將進行全表掃描:

      select id from t where num=@num;

      能夠改成強制查詢使用索引:

      select id from t with(index(索引名)) where num=@num
    • 6).在where子句中對字段進行函數、算術運算或其餘表達式運算
      如:

      select id from t where num/2=100;
      select id from t where date(createdate)>='2016-07-01';

      應改成:

      select id from t where num=100*2
      select id from t where createdate>='2005-11-30 00:00:00';

模糊查詢(like)時須要注意的事項

  • 左右都須要模糊匹配時,會致使全表掃描;

    select id from t where tablename like '%abc%'
  • 若要提升效率,可從如下三點着手:

    • a.不以"%"開始,如:select id from t where tablename like 'abc%'

    • b.若是必須使用like "%xxx" 的sql,可基於REVERSE()函數來建立一個函數索引。

      參考:http://blog.csdn.net/wangjunjun2008/article/details/52131668

    • c.能夠考慮全文檢索。
      首先若是表默認是innoDB,這種表的類型不支持全文檢索,因此要先改變其類型爲MyISAM

      alter table song engine=MyISAM;

      而後要在對應的要進行查找的字段上面創建全文檢索的索引:

      alter table add fulltext index(songname);

      若是要同時對多個字段進行檢索能夠這樣:

      alter table add fulltext index(songname,singername);

      完成以上步驟後,就能夠對錶進行全文檢索了。
      例如:

      select * from song where match(singername) against('周杰倫') ;

      或者多字段:

      select * from song where match(singername,songname) against('風雨');

      MYSQL目前可能只支持英文字符的全文搜索,如需使用全文檢索須要把中文轉化爲拼音。
      能夠在表裏面爲須要檢索的字段添加一個拼音字段,檢索的時候直接對拼音進行檢索。
      參考:http://blog.sina.com.cn/s/blog_7865b0830101ach4.html

索引

  • 複合索引
    在使用索引字段做爲條件時,若是該索引是複合索引,那麼必須使用到該索引中的第一個字段做爲條件才能保證系統使用該索引,而且應儘量的讓字段順序與索引順序相一致。

  • 當索引列有大量數據重複時,SQL查詢可能不會去利用索引
    並非全部索引對查詢都有效,SQL是根據表中數據來進行查詢優化的;
    如一表中有字段sex[male|female]幾乎各一半,那麼即便在sex列上建了索引也對查詢效率起不了做用。

    這個時候能夠用ENUM(枚舉類型),而不是VARCHAR,並對錶結構進行優化(select column from table_name procedure analyse();)

    例如:

    SELECT loan_period FROM cbs_bsns_loan PROCEDURE ANALYSE();
  • 索引並非越多越好
    索引當然能夠提升相應的select的效率,但同時也下降了insertupdate的效率;
    由於insertupdate時有可能會重建索引,因此怎樣建索引須要慎重考慮,視具體狀況而定。
    原則上,一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。

  • 儘量的避免更新clustered索引數據列
    clustered索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將致使整個表記錄的順序的調整,會耗費至關大的資源。
    若應用系統須要頻繁更新clustered索引數據列,那麼須要考慮是否應將該索引建爲clustered索引。

    MySQL InnoDb中的索引分爲 Clustered Index (聚簇索引)和 Secondary Index (二級索引);
    Clustered Index:每個InnoDB表都有一個特殊的索引,叫作clustered index,一般來說,clustered indexplrimary key是同一個意思。

    InnoDB選擇clustered index原則以下:
    ①若是表上定義了primary key,則使用primary key做爲clustered index。
    ②若是沒有定義primary key,選擇第一個非空的UNIQUE索引做爲clustered index。因此,若是表只有一個非空的UNIQUE索引,那麼InnoDB就把它看成主健了。
    ③若是即沒有primary key,也沒有合適的UNIQUE索引,InnoDB內部產生一個隱藏列,這個列包含了每一行的row ID, row ID隨着新行的插入而單調增長。而後在這個隱藏列上創建索引做爲clustered index。

    Secondary Index:除了Clustered Index以外的索引都是Secondary Index,每個Secondary Index的記錄中除了索引列的值以外,還包含主健值。經過二級索引查詢首先查到是主鍵值,而後InnoDB再根據查到的主鍵值經過主鍵/聚簇索引找到相應的數據塊。

字段類型

  • 儘可能使用數字型字段
    若只含數值信息的字段儘可能不要設計爲字符型,這會下降查詢和鏈接的性能,並會增長存儲開銷。
    由於引擎在處理查詢和鏈接時會逐個比較字符串中每個字符,而對於數字型而言只須要比較一次就夠了。
    好比,咱們的表不少是用字符串來作主鍵或鏈接字段,這裏其實能夠用字符串做爲業務主鍵,但使用自增id作主鍵和鏈接字段;

  • 儘量的使用 varchar/nvarchar(指定長度) 代替 char/nchar
    首先變長字段存儲空間小,能夠節省存儲空間;
    其次對於查詢來講,在一個相對較小的字段內搜索效率顯然要高些。

  • 對於少數枚舉值的字段使用 ENUM 而不是 VARCHAR 枚舉類型,限定值的取值範圍,好比性別(男,女,未知)等。
    ENUM 類型是很是快和緊湊的,其保存的是 TINYINT,但外表上顯示爲字符串。這樣一來,用這個字段來作一些選項列表變得至關的完美。

    若是你有一個字段,好比「性別」,「國家」,「民族」,「狀態」或「部門」,你知道這些字段的取值是有限並且固定的,那麼,你應該使用 ENUM 而不是 VARCHAR。

    注意:不推薦在mysql中設置某一字段類型爲enum,可是存的值爲數字,好比‘0’,‘1’,‘2’;

    表結構優化建議

    select column from table_name procedure analyse();

錶鏈接時的注意事項

  • 儘可能用 inner join
    避免 LEFT JOINNULL

  • 儘可能將條件寫到on中,而不是都寫到where
    onwhere的執行順序:
    ON條件(A LEFT JOIN B ON條件表達式中的ON)用來決定如何從 B 表中檢索數據行。
    僅在匹配階段完成之後,WHERE子句條件纔會被使用,它將從匹配階段產生的數據中檢索過濾。
    因此在使用鏈接時,尤爲是 left join(或right join)時,必定要在先給出儘量多的匹配知足條件,以減小須要匹配的數據量,並減小Where的執行。

    關於鏈接的用法參考:http://www.cnblogs.com/BeginMan/p/3754322.html

其餘注意事項

  • 任何地方都不要使用 select * from t ,而應用具體的字段。

  • 儘可能使用表變量來代替臨時表。
    若是表變量包含大量數據,請注意索引很是有限(只有主鍵索引)。

  • 避免頻繁建立和刪除臨時表,以減小系統表資源的消耗。

  • 在新建臨時表時,若是一次性插入數據量很大,那麼可使用 select into 代替 create table,避免形成大量 log ,以提升速度;若是數據量不大,爲了緩和系統表的資源,應先create table,而後insert。

  • 若是使用到了臨時表,在存儲過程的最後務必將全部的臨時表顯式刪除,先 truncate table ,而後 drop table ,這樣能夠避免系統表的較長時間鎖定。

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

  • 千萬不要 ORDER BY RAND()。

相關文章
相關標籤/搜索