作者 | 番茄發燒了
雖然你這列上建了索引,查詢條件也是索引列,但最終執行計劃沒有走它的索引。下面是引起這種問題的幾個關鍵點。
列與列對比
某個表中,有兩列(id和c_id)都建了單獨索引,下面這種查詢條件不會走索引
select * from test where id=c_id;
這種情況會被認爲還不如走全表掃描。
存在NULL值條件
我們在設計數據庫表時,應該盡力避免NULL值出現,如果非要不可避免的要出現NULL值,也要給一個DEFAULT值,數值型可以給0、-1之類的, 字符串有時候給空串有問題,就給一個空格或其他。
如果索引列是可空的,很可能是不會給其建索引的,索引值是少於表的count(*)
值的,所以這種情況下,執行計劃自然就去掃描全表了。
select * from test where id is not null;
NOT條件
我們知道建立索引時,給每一個索引列建立一個條目,如果查詢條件爲等值或範圍查詢時,索引可以根據查詢條件去找對應的條目。
反過來當查詢條件爲非時,索引定位就困難了,執行計劃此時可能更傾向於全表掃描,這類的查詢條件有:<>
、NOT
、in
、not exists
select * from test where id<>500;
select * from test where id in (1,2,3,4,5);
select * from test where not in (6,7,8,9,0);
select * from test where not exists (select 1 from test_02 where test_02.id=test.id);
LIKE通配符
當使用模糊搜索時,儘量採用後置的通配符,例如:name||’%’,因爲走索引時,其會從前去匹配索引列,這時候是可以找到的,如果採用前匹配,那麼查索引就會很麻煩,比如查詢所有姓張的人,就可以去搜索’張%’。
相反如果你查詢所有叫‘明’的人,那麼只能是%明。這時候索引如何定位呢?前匹配的情況下,執行計劃會更傾向於選擇全表掃描。後匹配可以走INDEX RANGE SCAN。
所以業務設計的時候,儘量考慮到模糊搜索的問題,要更多的使用後置通配符。
select * from test where name like 張||'%';
條件上包括函數
查詢條件上儘量不要對索引列使用函數,比如下面這個SQL
select * from test where upper(name)='SUNYANG';
這樣是不會走索引的,因爲索引在建立時會和計算後可能不同,無法定位到索引。但如果查詢條件不是對索引列進行計算,那麼依然可以走索引。比如
select * from test where name=upper('sunyang');
--INDEX RANGE SCAN
這樣的函數還有:to_char、to_date、to_number、trunc等。搜索公衆號Java知音,回覆“2021”,送你一份Java面試題寶典
複合索引前導列區分大
當複合索引前導列區分小的時候,我們有INDEX SKIP SCAN,當前導列區分度大,且查後導列的時候,前導列的分裂會非常耗資源,執行計劃想,還不如全表掃描來的快,然後就索引失效了。
select * from test where owner='sunyang';
數據類型的轉換
當查詢條件存在隱式轉換時,索引會失效。比如在數據庫裏id存的number類型,但是在查詢時,卻用了下面的形式:
select * from sunyang where id='123';
Connect By Level
使用connect by level時,不會走索引。
謂詞運算
我們在上面說,不能對索引列進行函數運算,這也包括加減乘除的謂詞運算,這也會使索引失效。建立一個sunyang表,索引爲id,看這個SQL:
select * from sunyang where id/2=:type_id;
這裏很明顯對索引列id進行了’/2’除二運算,這時候就會索引失效,這種情況應該改寫爲:
select * from sunyang where id=:type_id*2;
就可以使用索引了。
Vistual Index
先說明一下,虛擬索引的建立是否有用,需要看具體的執行計劃,如果起作用就可以建一個,如果不起作用就算了。
普通索引這麼建:
create index idx_test_id on test(id);
虛擬索引Vistual Index這麼建:
create index idx_test_id on test(id) nosegment;
做了一個實驗,首先創建一個表:
CREATE TABLE test_1116(
id number,
a number
);
CREATE INDEX idx_test_1116_id on test_1116(id);
CREATE INDEX idx_test_1116_a on test_1116(a)nosegment;
其中id爲普通索引,a爲虛擬索引。
在表中插入十萬條數據
begin
for i in 1 .. 100000 loop
insert into test_1116 values (i,i);
end loop;
commit;
end;
接着分別去執行下面的SQL看時間,由於在內網機做實驗,圖貼不出來,數據保證真實性。
select count(id) from test_1116;
--第一次耗時:0.061秒
--第二次耗時:0.016秒
select count(a) from test_1116;
--第一次耗時:0.031秒
--第二次耗時:0.016秒
因爲在執行過一次後,oracle對結果集緩存了,所以第二次執行耗時不走索引,走內存就都一樣了。可以看到在這種情況下,虛擬索引比普通索引快了一倍。
具體虛擬索引的使用細節,這裏不再展開討論。
Invisible Index
Invisible Index是oracle 11g提供的新功能,對優化器(還接到前面博客裏講到的CBO嗎)不可見,我感覺這個功能更主要的是測試用,假如一個表上有那麼多索引,一個一個去看執行計劃調試就很慢了,這時候不如建一個對錶和查詢都沒有影響的Invisible Index來進行調試,就顯得很好了。
通過下面的語句來操作索引
alter index idx_test_id invisible;
alter index idx_test_id visible;
如果想讓CBO看到Invisible Index,需要加入這句:
alter session set optimizer_use_invisible_indexes = true;
基本就這些了,有問題歡迎留言指出,共同進步!
往期推薦
喜歡的這裏報道
↘↘↘
本文分享自微信公衆號 - 程序猿DD(didispace)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。