Where語句設定不當導致索引失效

2021-06-21 08:28:39 字數 3508 閱讀 3484

雖然說索引在使用上可能有種種限制,但是還是在資料庫設計中被充分利用。因為在大部分情況下索引還是被用來提高資料庫效能的乙個工具。不過有些資料庫工程師往往會犯一些低階的錯誤,導致索引失效。如在where條件子句中設定了不合適的條件,從而在查詢等操作時導致原先在表中設定的索引不起作用。筆者以前也多次犯過類似的錯誤。筆者今天在這裡就拋磚引玉,把這些常見的問題總結一下。希望後來的人能夠盡量少犯這些錯誤。

錯誤一:在where子句中使用函式。

如現在在銷售訂單表中,有乙個訂單日期字段,其儲存的資料為年月日。假設現在使用者需要統計資料,需要統計2023年第一季度每隔月的各個業務員的接單情況。由於在銷售訂單中沒有儲存年與月份的資料,而只有訂單日期資料,那麼就需要利用extract函式從訂單日期欄位中獲取年份與月份字段,然後再查詢處各個業務員在2023年第一季度每個月的銷售訂單明細。下面的select語句就是查詢2023年1月份各個業務員的接單情況。

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

where extract(yyyy,訂單日期)=2009 and extract(mouth,訂單日期)=1

但是此時就需要在where條件語句中採用extract函式。這是oracle資料庫系統提供的從日期型字段中抽取年或者月份的函式。如果原先在這個日期欄位上建立了索引(不是函式索引),那麼此時會對資料庫的查詢產生什麼影響呢?

通常情況下,如果不使用基於函式的索引,那麼當sql語句在的where子句中隊存在索引的列使用函式時,這會讓資料庫的優化器忽略掉這些索引。也就是說,這種情況下即使只存在著少量的復合條件的資訊,資料庫仍然會對這張表進行全表掃瞄,以獲取相關的資料。這主要是因為這些索引實際上已經改變了被索引列的值。如一些常見的函式,如substr、extract等函式,都會改變索引列的值。此時資料庫系統也就無法使用已被函式引用(此時列的值已經發生改變)的索引和列。也即是說,如果在where子句的條件語句中,採用了函式的話,則即使列採用了索引(不是函式索引),就會讓設定在這個列上索引失效。此時資料庫就會對這個表進行全表掃瞄。這個結果可能是一些資料庫管理員始料未及的。

那麼該如何避免這種情況呢?最簡單的方法,就是資料庫管理員在資料庫設計的時候就預計到在以後操作中,可能要在where子句中要使用函式,此時就可以把這個列上的索引設定為函式索引。通常情況下,只要建立了函式索引,則即使在where語句中採用了函式,這個列上的索引仍然有效。在查詢中就可以避免全表掃瞄。因為函式索引實際上儲存了預先計算過的值。也就是說,在索引表中,其實已經儲存了年度與月份的值。而不是儲存具體的訂單日期。那麼此時在查詢時,資料庫就會直接對應索引表中的年度與月份的值。為此索引就不會因為採用了函式而失效。

錯誤二:不匹配的資料型別。

在資料庫中,有些資料型別雖然不同,但是資料庫會自動進行轉換。如現在在一張使用者資訊表中,可能有公民的身份證號碼字段,這個欄位的型別為字元型。通常情況下,為這個字元型別的字段賦值時需要加入單引號。但是如果把乙個純數字的字串賦值給乙個字元型的字段時,可以不用加單引號。因為此時資料庫系統會自動把這串數字轉換為字元型資料。現在資料庫在這錶中已經給這個身份證號碼字段設定了索引。如果現在使用者在對這個表進行查詢時,所採用的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,這就可以避免全表掃瞄了。當然如果對身份證字段能夠實現非空限制那時最好的了。

Where語句設定不當導致索引失效

雖然說索引在使用上可能有種種限制,但是還是在資料庫設計中被充分利用。因為在大部分情況下索引還是被用來提高資料庫效能的乙個工具。不過有些資料庫工程師往往會犯一些低階的錯誤,導致索引失效。如在where條件子句中設定了不合適的條件,從而在查詢等操作時導致原先在表中設定的索引不起作用。筆者以前也多次犯過類...

有些 where 條件會導致索引無效

在查詢中,where 條件也是乙個比較重要的因素,盡量少並且是合理的 where 條件是徆重要的,盡量在多個條件的時候,把會提取盡量少資料量的條件放在前 面,減少後乙個 where 條件的查詢時間。有些 where 條件會導致索引無效 1.where 子句的查詢條件裡有!mysql 將無法使用索引。...

quartz配置檔案設定不當導致報錯

一 問題描述 如果資料庫用的是pgsql且在quartz.properties檔案中設定org.quartz.jobstore.driverdelegateclass org.quartz.impl.jdbcjobstore.stdjdbcdelegate 這時服務啟動會報錯 couldn t re...