1.影響查詢性能的因素:
1.表格中的行數越少越好。
2.排序與否。
3.是否檢查壹個索引。
4.查詢語句的形式
二、幾種查詢優化的轉換
1.對於單個表的單個列,如果有T.C=expr形式的子句,並且都用OR運算符連接,比如:x = expr1或者expr2 = x或者x = expr3。此時,對於或,索引不能用於SQLite中的優化。所以可以在(expr1,expr2,expr3)中用IN運算符:x轉換成子句,這樣就可以用索引進行優化,效果很明顯,但如果沒有索引,or語句的執行效率會比IN語句稍好。
2.如果壹個子句的運算符是BETWEEN,那麽在SQLite中是無法通過index進行優化的,所以要進行相應的等價轉換。例如,b和c之間的a可以轉換為:(b和c之間的a)和(a & gt=b)和(a & lt=c).在上述條款中,(a & gt=b)和(a & lt=c)壹個被設置為dynamic且為(a BETWEEN b AND c)的子句,那麽如果BETWEEN語句已經被編碼,則該子句將被忽略,如果有可用的索引使該子句滿足條件,則父語句將被忽略。
3.如果壹個單元的運算符是LIKE,則進行如下轉換:x like' ABC% ',轉換為:x >;='abc '和x & ltabd .因為SQLite中的LIKE是無法通過索引進行優化的,如果有索引的話,遠談不上轉換和不轉換,因為它對LIKE不起作用,但是如果沒有索引,LIKE還是沒有轉換後那麽高效。
第三,幾個查詢語句的處理(復合查詢)
1.查詢語句是:< SelectA & gt& lt操作員& gt& lt選擇& gtORDER BY & ltorderbylist & gt以...排序
執行方法:是union all、union、except或intersect之壹。該語句的執行過程是先執行並排序selectA和selectB,然後掃描兩個結果。以上四種操作不同,執行過程分為七個子過程:
OutA:將selectA的壹行結果放入最終結果集中。
OutB:將selectA的壹行結果放入最終結果集中(最終結果集中只放入UNION操作和UNION ALL操作)。
AltB:當selectA的當前記錄小於selectB的當前記錄時。
AeqB:當selectA的當前記錄等於selectB的當前記錄時。
AgtB:selectA的當前記錄大於selectB的當前記錄時。
EofA:當selectA的結果被遍歷時。
EofB:當selectB的結果被遍歷時。
下面是四個操作的執行過程:
執行順序
聯合所有
聯盟
除...之外
橫斷
AltB:
outA,nextA
outA,nextA
outA,nextA
nextA
AeqB:
outA,nextA
nextA
nextA
outA,nextA
AgtB:
outB,nextB
outB,nextB
nextB
nextB
EofA:
outB,nextB
outB,nextB
停止
停止
EofB:
outA,nextA
outA,nextA
outA,nextA
停止
2.如果可能,可以將使用GROUP BY查詢的語句轉換為DISTINCT語句進行查詢,因為GROUP BY有時可能會使用index,但不會使用index for DISTINCT。
第四,子查詢扁平化
示例:選擇a from(選擇x+y作為a from t1其中z
壹般來說,執行這個SQL語句的默認方法是先執行內部查詢,把結果放到壹個臨時表中,然後在這個表上做壹個外部查詢,需要兩次數據處理。此外,這個臨時表沒有索引,因此無法優化外部查詢。如果處理上面的SQL,可以得到下面的SQL語句:select x+y as a from t 1其中z
查詢壹次數據就夠了,如果表t1上有索引,就避免遍歷整個表。
使用flatten方法優化SQL的條件:
1.子查詢和外部查詢都不使用集合函數。
2.子查詢沒有set函數,或者外部查詢不是表連接。
3.子查詢不是左外部聯接的右操作數。
4.子查詢不明確,或者外部查詢不是表連接。
5.子查詢不明確或外部查詢不是set函數。
6.子查詢沒有set函數,或者外部查詢沒有DISTINCT關鍵字。
7.子查詢有壹個FROM語句。
8.子查詢未使用LIMIT,或者外部查詢不是表連接。
9.子查詢不使用LIMIT,或者外部查詢不使用set函數。
10.子查詢沒有集合函數,或者外部查詢沒有限制。
11.子查詢和外部查詢不都是ORDER BY子句。
12.子查詢和外部查詢都不使用LIMIT。
13.子查詢中未使用偏移量
14.外部查詢不是復合查詢的壹部分,或者子查詢沒有同時使用關鍵字ORDER BY和LIMIT。
15.外部查詢沒有用。集合函數的子查詢不包含ORDER BY。
16.復合子查詢的扁平化:子查詢不是復合查詢,或者是UNION ALL復合查詢,但都是由幾個帶有非集合函數的查詢組成。他的父查詢不是復合查詢的子查詢,也不是set函數或DISTINCT查詢,並且FROM語句中沒有其他表或子查詢。父查詢和子查詢都可能包含WHERE語句,這將受到上述65438的影響。
示例:從(
從選項卡中選擇x
聯合所有
從選項卡中選擇y
聯合所有
從選項卡2中選擇abs(z*2)
)哪裏a!=5階乘1
轉換為:
從選項卡中選擇x+1,其中x+1!=5
聯合所有
從選項卡中選擇y+1,其中y+1!=5
聯合所有
從tab2中選擇abs(z*2)+1,其中abs(z*2)+1!=5
按1排序
17.如果子查詢是復合查詢,父查詢的所有ORDER BY語句都必須是對子查詢列的簡單引用。
18.子查詢沒有使用LIMIT,或者外部查詢沒有WHERE語句。
子查詢的展平是通過壹個特殊的函數實現的,它是:
靜態int flattenSubquery(
Parse *pParse,/*解析上下文*/
Select *p,/*父或外部Select語句*/
int iFrom,/* Index in p-& gt;pSrc->內部子查詢的a[]*/
int isAgg,/*如果外部選擇使用聚合函數,則為True */
int subqueryIsAgg /*如果子查詢使用聚合函數,則為True */
)
它在Select.c文件中實現。顯然,對於壹個復雜的查詢,如果在滿足上述條件的情況下,查詢語句被展平,查詢就可以得到優化。如果有指數就更好了!
動詞 (verb的縮寫)連接查詢
在返回查詢結果之前,相關表的每壹行都必須已經連接。在SQLite中,這是通過嵌套循環實現的。在早期版本中,最左邊是最外面的循環,最右邊是最裏面的循環。當連接兩個或多個表時,如果有索引,就放在內循環中,也就是FROM的末尾,因為對於前面選中的每壹行,都會快速找到後面對應的行,如果有索引,否則就是。
優化方法如下:
對於要查詢的每個表,統計該表上的索引信息。首先,將成本分配給SQLITE_BIG_DBL(系統定義的常數):
1)如果沒有索引,查找該表上是否有rowid的查詢條件:
1.如果有Rowid=EXPR,如果有,則返回該表的成本估算,成本為零,查詢得到的記錄數為1,完成該表的成本估算。
2.如果沒有Rowid=EXPR,但在(...)而IN是壹個列表,那麽記錄返回的記錄數就是IN列表中的元素數,估計開銷為NlogN。
3.如果IN不是列表而是子查詢結果,那麽因為無法確定具體的子查詢,所以只能估計壹個值,返回的記錄數是100,代價是200。
4.如果rowid是範圍查詢,那麽估計所有符合條件的記錄是總記錄的三分之壹,總記錄估計為1000000,估計開銷也是記錄數。
5.如果查詢還需要排序,則添加排序的成本NlogN。
6.如果此時得到的成本小於總成本,則更新總成本,否則不更新。
2)如果WHERE子句中有or運算符,則應該單獨分析由這些OR連接的所有子句。
1.如果存在由AND連接符組成的子句,那麽分別分析由AND連接的子句。
2.如果鏈接子句是X的形式
3.下壹步是計算整個OR操作的總成本。
4.如果查詢需要排序,將上述總開銷乘以排序開銷NlogN。
5.如果此時得到的成本小於總成本,則更新總成本,否則不更新。
3)如果有索引,則統計每個表的索引信息。對於每個指數:
1.首先找到這個索引對應的列號,然後找到對應的可以在這個索引中使用的WHERE子句(運算符必須是=或IN(…))。如果沒有找到,那麽退出每個索引的循環。如果找到,判斷這個子句的操作符是什麽。如果是=,那麽就沒有額外的成本。如果是在(子選擇),那就估計壹下。
2.然後計算總費用和總查詢結果記錄和費用。
3.nRow = ppro be-& gt;aiRowEst[I]* in multiplier;/*計算行數*/
4.cost = nRow * est log(in multiplier);/*統計成本*/
5.如果在(…)中找不到operator = or的子句,而是壹個範圍查詢,那麽妳就要把查詢結果中的記錄數估計為nRow/3,估計的成本為cost/3。
6.類似地,如果查詢需要排序,將NlogN添加到上面的總開銷中。
7.如果此時得到的成本小於總成本,則更新總成本,否則不更新。
4)通過上面的優化過程,可以得到壹個表查詢的總開銷(即上述開銷之和),然後對第二個表做同樣的操作,使FROM子句中的所有表都計算出各自的開銷,最後取最小的壹個,這將是嵌套循環的最內層,依次可以得到整個嵌套循環的嵌套順序,此時最優,達到優化的目的。
5)所以循環的嵌套順序不壹定與FROM子句中的順序相同,因為在執行過程中會使用索引優化來重新排列順序。
不及物動詞索引
在SQLite中,有以下索引:
1)單列索引
2)多列索引
3)獨特性指數
4)對於聲明為:INTEGER PRIMARY KEY的主鍵,默認情況下會對該列進行排序,所以雖然在數據字典中沒有對其進行索引,但其作用類似於索引。所以,如果在這個主鍵上單獨建立索引,會浪費空間,沒有任何好處。
使用該指數的註意事項:
1)沒有必要為非常小的表建立索引。
2)如果經常對表進行插入和更新操作,應該控制索引的使用。
3)不要在壹個表上建立太多的索引。如果構建了太多的索引,SQLite可能不會選擇最好的壹個來執行查詢。壹個解決方案是建立壹個聚簇索引。
指數應用的時間:
1)運算符:=、>、& lt,在等
2)運算符BETWEEN、LIKE或不能使用索引。
如between:select * from my table where my field介於10和20之間;
那麽它應該被轉換成:
SELECT * FROM my table WHERE my field & gt;= 10和myfield & lt= 20;
這時候如果myfield上有索引就可以使用,大大提高了速度。
另壹個例子是這樣的:select * from my table其中我的字段就像' SQL % ';
此時,它應該轉換成:
SELECT * FROM my table WHERE my field & gt;= 'sql '和myfield & ltsqm ';
這時候如果myfield上有索引就可以使用,大大提高了速度。
或者:select * from my table where my field = ' ABC '或my field = ' XYZ ';
此時,它應該轉換成:
SELECT * FROM my table WHERE my field IN(' ABC ',' XYZ ');
這時候如果myfield上有索引就可以使用,大大提高了速度。
3)有時候索引用不上,要遍歷整個表(程序演示)。
SELECT * FROM mytable,其中my field % 2 = 1;
SELECT * FROM my table WHERE substr(my field,0,1)= ' w ';
SELECT * FROM my table WHERE length(my field)& lt;5;