sqlserver 的篩選索引(filter index)與常規的非篩選索引,加了一定的filter條件,可以按照某些條件對表中的字段進行索引,但是filter 索引在查詢 使用上,並不等同於常規的索引,
如果忽略了這些差異,可能會造成潛在的問題,因此在使用filter索引的時候,一定要結合具體的查詢,做充分的測試。
測試case
if object_id('test_filter_index') is not null drop table test_filter_index GO create table test_filter_index ( id int identity(1,1) not null primary key, col2 varchar(10), col3 varchar(10), create_date datetime ) GO --寫入10W行測試數據,col2 col3非空 insert into test_filter_index select concat('A',cast(rand()*1000000 as int)),concat('B',cast(rand()*1000000 as int)),getdate() GO 100000 --寫入1W行測試數據,col2 col3為空 insert into test_filter_index select null,null,getdate() GO 10000
非filter索引
如果是正常的索引,也即不加filter條件,如下創建測試表上的索引
--col2和col3上,如果是常規索引(非篩選索引) create index idx_col2 on test_filter_index(col2,col3) GO
如下,只要是按照索引的前導列進行查詢或join,都可以使用到索引


filter索引
如果在創建索引的時候增加filter條件
--刪除之前創建的索引 drop index idx_col2 on test_filter_index --col2和col3上,如果是篩選索引(增加col2和col3上的篩選條件) create index idx_col2 on test_filter_index(col2,col3) where col2 is not null and col3 is not null GO
在執行上述的兩個查詢,會發現,盡管使用的查詢條件為索引的前導列,但是扔無法使用到上面創建的filter索引


其實不難理解,為什么上面兩種情況無法使用到創建的filter索引?由於在創建索引的時候,增加篩選條件,這個索引樹種的數據,可能是不完全符合查詢語義的
就比如select * from test_filter_index where col2 = 'A632395',除了 col2 = 'A632395'這個條件之外,對於col3字段,潛在兩種符合條件的數據
第一種:select * from test_filter_index where col2 = 'A632395' and col3 is null
第二種:select * from test_filter_index where col2 = 'A632395' and col3 is not null
如果走了filter索引,也即idx_col2 ,查詢出來的結果可能就是不完整的,因此不會使用到idx_col2 這個索引
事實上,執行計划很清楚地顯示了,什么情況下才可以用到filter索引,只有查詢條件的數據被filter索引的篩選條件覆蓋,或者說查詢條件是filter條件的子集,才有可能用到filter索引


查詢是否可以使用到filter索引,只有滿足當前的查詢結果集,一定是屬於索引的filter篩選之后的子集的情況下,才能使用到filter索引,否則都無法使用到filter索引
filter索引只能針對具體的語句進行創建,而不能作為通用的索引使用,這個比較簡單,記錄一下,防止犯錯。
