Where語句設置不當致使索引失效

雖說索引在使用上可能有種種限制,可是仍是在數據庫設計中被充分利用。由於在大部分狀況下索引仍是被用來提升數據庫性能的一個工具。

雖說索引在使用上可能有種種限制,可是仍是在數據庫設計中被充分利用。由於在大部分狀況下索引仍是被用來提升數據庫性能的一個工具。不過有些數據庫工程師每每會犯一些低級的錯誤,致使索引失效。如在Where條件子句中設置了不合適的條件,從而在查詢等操做時致使原先在表中設置的索引不起做用。筆者之前也屢次犯過相似的錯誤。筆者今天在這裏就拋磚引玉,把這些常見的問題總結一下。但願後來的人可以儘可能少犯這些錯誤。數據庫

錯誤一:在Where子句中使用函數。數據庫設計

如如今在銷售訂單表中,有一個訂單日期字段,其存儲的數據爲年月日。假設如今用戶須要統計數據,須要統計2009年第一季度每隔月的各個業務員的接單狀況。因爲在銷售訂單中沒有存儲年與月份的數據,而只有訂單日期數據,那麼就須要利用Extract函數從訂單日期字段中獲取年份與月份字段,而後再查詢處各個業務員在2009年第一季度每月的銷售訂單明細。下面的Select語句就是查詢2009年1月份各個業務員的接單狀況。函數

Select 業務員,訂單日期,銷售訂單號碼,客戶名稱,訂單金額工具

Where Extract(yyyy,訂單日期)=2009 and Extract(mouth,訂單日期)=1性能

可是此時就須要在Where條件語句中採用Extract函數。這是Oracle數據庫系統提供的從日期型字段中抽取年或者月份的函數。若是原先在這個日期字段上創建了索引(不是函數索引),那麼此時會對數據庫的查詢產生什麼影響呢?優化

一般狀況下,若是不使用基於函數的索引,那麼當SQL語句在的Where子句中隊存在索引的列使用函數時,這會讓數據庫的優化器忽略掉這些索引。也就是說,這種狀況下即便只存在着少許的複合條件的信息,數據庫仍然會對這張表進行全表掃描,以獲取相關的數據。這主要是由於這些索引實際上已經改變了被索引列的值。如一些常見的函數,如SUBSTR、Extract等函數,都會改變索引列的值。此時數據庫系統也就沒法使用已被函數引用(此時列的值已經發生改變)的索引和列。也便是說,若是在Where子句的條件語句中,採用了函數的話,則即便列採用了索引(不是函數索引),就會讓設置在這個列上索引失效。此時數據庫就會對這個表進行全表掃描。這個結果多是一些數據庫管理員始料未及的。spa

那麼該如何避免這種狀況呢?最簡單的方法,就是數據庫管理員在數據庫設計的時候就預計到在之後操做中,可能要在Where子句中要使用函數,此時就能夠把這個列上的索引設置爲函數索引。一般狀況下,只要創建了函數索引,則即便在Where語句中採用了函數,這個列上的索引仍然有效。在查詢中就能夠避免全表掃描。由於函數索引實際上存儲了預先計算過的值。也就是說,在索引表中,其實已經存儲了年度與月份的值。而不是存儲具體的訂單日期。那麼此時在查詢時,數據庫就會直接對應索引表中的年度與月份的值。爲此索引就不會由於採用了函數而失效。設計

錯誤二:不匹配的數據類型。orm

在數據庫中,有些數據類型雖然不一樣,可是數據庫會自動進行轉換。如如今在一張用戶信息表中,可能有公民的身份證號碼字段,這個字段的類型爲字符型。一般狀況下,爲這個字符類型的字段賦值時須要加入單引號。可是若是把一個純數字的字符串賦值給一個字符型的字段時,能夠不用加單引號。由於此時數據庫系統會自動把這串數字轉換爲字符型數據。如今數據庫在這表中已經給這個身份證號碼字段設置了索引。若是如今用戶在對這個表進行查詢時,所採用的Where條件語句爲 Where 身份證號碼=123456789900。此時數據庫會如何查詢呢?索引

筆者要很是悲痛的告訴你們,此時數據庫會忽略掉設置在身份證號碼字段上的索引,而採用全表掃描。相似的比較不匹配的數據類型,會致使設置在表中字段上的索引失效,這是不少數據庫管理員常常容易犯的錯誤。Oracle數據庫系統在數據類型字段上的兼容性,雖然提升了用戶操做數據的便利性,可是毋庸置疑的也給用戶留下很多的麻煩。就拿上面這個例子來講,數據庫優化器會對以上這個條件語句進行一些轉換,如可能會換成:

To_number(身份證號碼) =123456789900

也就是說,會在身份證號碼字段前面隱性的加入一個函數,把身份證號碼轉換爲數字型。而後再與後面提供的身份證號碼進行比對。此時就至關於對索引列採用了函數,跟上面提到的第一個錯誤相似。當Where條件語句中採用了函數,則即便這個列中設置了索引(不是函數索引),則數據庫優化器也會忽略掉這個索引。此時即便一個身份證號碼在數據庫中只有一條記錄,數據庫仍然須要進行全表掃描。

因爲相似的錯誤很隱蔽,故一些經驗不深的數據庫管理員與程序開發人員常常會犯這個錯誤。那麼該如何避免這種狀況呢?其實只要瞭解有這種風險的存在,那麼在處理起來也是比較簡單的。如只須要在查詢的時候把Where語句寫成Where 身份證號碼=’123456789900’便可,即加入單引號,表示輸入的條件是一個字符數據類型便可。此時二者的數據類型一致,數據庫就不會利用數據類型轉換函數了。不過有時候終端用戶並不會這麼配合,每次輸入身份證號碼查詢的時候,還利用單引號。此時程序開發人員應該把這個單引號在程序設計中實現。即終端用戶只須要輸入18位的身份證號碼便可,不須要輸入單引號。而應用程序在把這個身份證號碼傳遞給數據庫系統的時候,應用程序會先給其加上單引號,而後再傳遞給數據庫系統進行查詢。爲此這個單引號對用戶來講就是透明的。

另外雖然能夠修改數據庫中的身份證字段的數據類型,把其設置爲數字型便可。可是一般狀況下不建議這麼作。由於有些老的身份證號碼中含有字符,針對這些身份證號碼就很差存儲。並且有時候在身份證查詢中也只須要進行模糊查詢,如只知道出身地與出生年月日,來查詢身份證號碼。若是是數據類型的字段的話,則在實現模糊查詢的時候會遇到問題。因此遇到這種狀況,最好的處理方式就是應用程序在傳遞傳輸的時候,強制加入單引號。從而防止由於比較不匹配的數據類型而致使的全表掃描。

錯誤三:在Where子句中使用IS NULL或者IS NOT NULL。

在數據庫設計的時候,容許某些字段爲非空。而即便某個字段容許爲非空,數據庫仍然容許在這個字段上創建索引。可是這種狀況下,使用索引就是一個很危險的事情。由於一不當心,就可能使得這個索引失效,在查詢時須要用到全表掃描。如在以上這個表中,用戶須要查詢身份證號碼爲空的紀錄,以方便用戶補全身份證號碼。此時用戶就須要用到如下這個條件語句:WHERE 身份證號碼 IS NULL。經過這個語句能夠查詢出全部身份證號碼爲空的紀錄。可是,在Where子句中若是使用IS NULL或者IS NOT NULL等條件語句的話,會讓在這個列上的索引失效。爲此若是在幾百萬的信息中,若是隻有兩條記錄沒有身份證號碼,則此事數據庫仍然須要進行全表掃描,以查找相關的信息。這主要是由於普通狀況下,若是一個字段爲空,並且又在這個字段上設置了索引的話,則這個索引的值不會保存在索引表中。由於根本沒法保存。爲何呢?由於空值(NULL)在數據庫中是一個很特殊的值。其NULL不等於‘’,甚至不等於NULL。

因此在容許NULL字段上創建索引要特別注意這個狀況。爲了不這種狀況筆者有幾個建議。如容許身份證這個字段爲NULL,那麼最好在這個字段上創建位圖索引。由於建立位圖索引時,數據庫系統會對整個表進行索引,併爲索引列的每一個取值創建一個位圖,包括NULL字段。因此說位圖索引一般對於NULL字段的搜索有獨到之處。可是位圖索引一般狀況下是用在基數比較小的狀況,即重複數值比較多時。而對於身份證號碼的話,基本上都是惟一的,也就是說基數很大,此時並不適合採用位圖索引。既然不可以採用位圖索引,那麼就最好可以給這個字段設置默認值。如能夠把這個字段默認設置爲0。當沒有輸入身份證號而保存這個資料的時候,則數據庫中以字符0表示。如此在之後想查詢身份證號碼爲空的紀錄時,只須要輸入0,而不須要用IS NULL,這就能夠避免全表掃描了。固然若是對身份證字段可以實現非空限制那時最好的了。

相關文章
相關標籤/搜索