聲明:完全轉(zhuǎn)自何登成的博客:http://hedengcheng.com/?p=577秸应,因?yàn)橐呀?jīng)無法訪問,為了引用方便碑宴,直接摘抄過來软啼。
給定一條SQL,如何提取其中的where條件延柠?where條件中的每個(gè)子條件祸挪,在SQL執(zhí)行的過程中有分別起著什么樣的作用?
關(guān)系型數(shù)據(jù)庫中的數(shù)據(jù)組織
關(guān)系型數(shù)據(jù)庫中贞间,數(shù)據(jù)組織涉及到兩個(gè)最基本的結(jié)構(gòu):表與索引贿条。
表中存儲的是完整記錄,一般有兩種組織形式:堆表(所有的記錄無序存儲)增热,或者是聚簇索引表(所有的記錄整以,按照記錄主鍵進(jìn)行排序存儲)。
索引中存儲的是完整記錄的一個(gè)子集峻仇,用于加速記錄的查詢速度公黑,索引的組織形式,一般均為B+樹結(jié)構(gòu)摄咆。
測試
讓我們創(chuàng)建一張測試表凡蚜,為表新增幾個(gè)索引,然后插入幾條記錄吭从,最后看看表的完整數(shù)據(jù)組織朝蜘、存儲結(jié)構(gòu)式怎么樣的。
create table t1 (a int primary key, b int, c int, d int, e varchar(20));
create index idx_t1_bcd on t1(b, c, d);
insert into t1 values (4,3,1,1,’d’);
insert into t1 values (1,1,1,1,’a’);
insert into t1 values (8,8,8,8,’h’):
insert into t1 values (2,2,2,2,’b’);
insert into t1 values (5,2,3,5,’e’);
insert into t1 values (3,3,2,2,’c’);
insert into t1 values (7,4,5,5,’g’);
insert into t1 values (6,6,4,4,’f’);
t1表的存儲結(jié)構(gòu)如下圖所示(只畫出了idx_t1_bcd索引與t1表結(jié)構(gòu)影锈,沒有包括t1表的主鍵索引):idx_t1_bcd索引上有[b,c,d]三個(gè)字段(若是InnoDB類的聚簇索引表芹务,idx_t1_bcd上還會包括主鍵a字段)蝉绷,idx_t1_bcd索引,首先按照b字段排序枣抱,b字段相同熔吗,則按照c字段排序,以此類推佳晶。
SQL的 where 條件提取
考慮以下的一條SQL桅狠,會走idx_t1_bcd索引:
select * from t1 where b >= 2 and b < 8 and c > 1 and d != 4 and e != 'a';
思考這條SQL的幾個(gè)關(guān)鍵性問題:
1. 此SQL,覆蓋索引idx_t1_bcd上的哪個(gè)范圍轿秧?
起始范圍:記錄[2,2,2]是第一個(gè)需要檢查的索引項(xiàng)中跌。索引起始查找范圍由b >= 2,c > 1決定菇篡。
終止范圍:記錄[8,8,8]是第一個(gè)不需要檢查的記錄漩符,而之前的記錄均需要判斷。索引的終止查找范圍由b < 8決定驱还;
2. 在確定了查詢的起始嗜暴、終止范圍之后,SQL中還有哪些條件可以使用索引idx_t1_bcd過濾议蟆?
固定了索引的查詢范圍[(2,2,2),(8,8,8))之后闷沥,此索引范圍中并不是每條記錄都是滿足where查詢條件的。例如:(3,1,1)不滿足c > 1的約束咐容;(6,4,4)不滿足d != 4的約束舆逃。而c,d列戳粒,均可在索引idx_t1_bcd中過濾掉不滿足條件的索引記錄的路狮。
因此,SQL中還可以使用c > 1 and d != 4條件進(jìn)行索引記錄的過濾享郊。
3. 在確定了索引中最終能夠過濾掉的條件之后览祖,還有哪些條件是索引無法過濾的?
顯而易見炊琉,e !='a'這個(gè)查詢條件展蒂,無法在索引idx_t1_bcd上進(jìn)行過濾,因?yàn)樗饕⑽窗琫列苔咪。e列只在堆表上存在锰悼,為了過濾此查詢條件,必須將已經(jīng)滿足索引查詢條件的記錄回表团赏,取出表中的e列箕般,然后使用e列的查詢條件e != ‘a(chǎn)’進(jìn)行最終的過濾。
在理解以上的問題解答的基礎(chǔ)上舔清,做一個(gè)抽象丝里,可總結(jié)出一套放置于所有SQL語句而皆準(zhǔn)的where查詢條件的提取規(guī)則曲初,可歸納為3大類:Index Key (First Key & Last Key),Index Filter杯聚,Table Filter臼婆。
Index Key
用于確定SQL查詢在索引中的連續(xù)范圍的查詢條件,被稱之為Index Key幌绍。一個(gè)范圍包含一個(gè)起始與一個(gè)終止颁褂,因此Index Key也被拆分為Index First Key和Index Last Key,分別用于定位索引查找的起始傀广,以及索引查詢的終止條件颁独。
- Index First Key
提取規(guī)則:從索引的第一個(gè)鍵值開始,檢查其在where條件中是否存在伪冰,若存在并且條件是=誓酒、>=,則將對應(yīng)的條件加入Index First Key之中贮聂,繼續(xù)讀取索引的下一個(gè)鍵值丰捷,使用同樣的提取規(guī)則;若存在并且條件是>寂汇,則將對應(yīng)的條件加入Index First Key中,然后終止Index First Key的提取捣染。
針對上面的SQL骄瓣,應(yīng)用這個(gè)提取規(guī)則,提取出來的Index First Key為(b >= 2, c > 1)耍攘。由于c的條件為 >榕栏,提取結(jié)束,不包括d蕾各。
- Index Last Key
與Index First Key正好相反扒磁,用于確定索引查詢的終止范圍。提取規(guī)則:從索引的第一個(gè)鍵值開始式曲,檢查其在where條件中是否存在妨托,若存在并且條件是=、<=吝羞,則將對應(yīng)條件加入到Index Last Key中兰伤,繼續(xù)提取索引的下一個(gè)鍵值,使用同樣的提取規(guī)則钧排;若存在并且條件是 < 敦腔,則將條件加入到Index Last Key中,同時(shí)終止提群蘖铩符衔;若不存在找前,同樣終止Index Last Key的提取。
針對上面的SQL判族,應(yīng)用這個(gè)提取規(guī)則躺盛,提取出來的Index Last Key為(b < 8),由于是 < 符號五嫂,因此提取b之后結(jié)束颗品。
Index Filter
在Index Key的提取之后固定了索引的查詢范圍,但是此范圍中的項(xiàng)沃缘,并不都是滿足查詢條件的項(xiàng)躯枢。在上面的SQL用例中,(3,1,1)槐臀,(6,4,4)均屬于范圍中锄蹂,但是均不滿足SQL的查詢條件。
Index Filter的提取規(guī)則:同樣從索引列的第一列開始水慨,檢查其在where條件中是否存在:
- 若存在并且where條件僅為 =得糜,則跳過第一列繼續(xù)檢查索引下一列,下一索引列采取與索引第一列同樣的提取規(guī)則晰洒;
- 若where條件為 >=朝抖、>、<谍珊、<= 其中的幾種治宣,則跳過索引第一列,將其余where條件中索引相關(guān)列全部加入到Index Filter之中砌滞;
- 若索引第一列的where條件包含 =侮邀、>=、>贝润、<绊茧、<= 之外的條件,則將此條件以及其余where條件中索引相關(guān)列全部加入到Index Filter之中打掘;
- 若第一列不包含查詢條件华畏,則將所有索引相關(guān)條件均加入到Index Filter之中。
針對上面的用例SQL尊蚁,索引第一列只包含 >=唯绍、< 兩個(gè)條件,因此第一列可跳過枝誊,將余下的c况芒、d兩列加入到Index Filter中。因此獲得的Index Filter為 c > 1 and d != 4 。
Table Filter
Table Filter是最簡單绝骚,也是提取最為方便的耐版。提取規(guī)則:所有不屬于索引列的查詢條件,均歸為Table Filter之中压汪。
針對上面的用例SQL粪牲,Table Filter就為 e != 'a'。
總結(jié)
SQL語句中的where條件止剖,使用以上的提取規(guī)則腺阳,最終都會被提取到Index Key (First Key & Last Key),Index Filter與Table Filter之中穿香。
Index First Key亭引,只是用來定位索引的起始范圍,在索引第一次Search Path(沿著索引B+樹的根節(jié)點(diǎn)一直遍歷皮获,到索引正確的葉節(jié)點(diǎn)位置)時(shí)使用焙蚓,一次判斷即可;
Index Last Key洒宝,用來定位索引的終止范圍购公,因此對于起始范圍之后讀到的每一條索引記錄,均需要判斷是否已經(jīng)超過了Index Last Key的范圍雁歌,若超過宏浩,則當(dāng)前查詢結(jié)束;
Index Filter靠瞎,用于過濾索引查詢范圍中不滿足查詢條件的記錄绘闷,因此對于索引范圍中的每一條記錄,均需要與Index Filter進(jìn)行對比较坛,若不滿足Index Filter則直接丟棄,繼續(xù)讀取索引下一條記錄扒最;
Table Filter丑勤,最后一道where條件的防線,用于過濾通過前面索引的層層考驗(yàn)的記錄吧趣,判斷完整記錄是否滿足Table Filter中的查詢條件法竞,若不滿足,跳過當(dāng)前記錄强挫,繼續(xù)讀取索引的下一條記錄岔霸,若滿足,則返回記錄俯渤,此記錄滿足了where的所有條件呆细。