Sqlite優化總結

一.使用索引

創建索引的基本語法如下:   
CREATE INDEX index_name ON table_name (column_name,...)

間接索引:
定義主鍵約束或者唯一性約束,可以間接創建索引,主鍵默認爲唯一約束.所	有不需要再創建索引,以免造成浪費.

注意事項:
	1.建立索引會增加數據庫的大小.
	2.對於insert,update,delete操作來說,使用索引會變慢,因爲同時需要維護索引的變化.
	3.爲數據量較小的表建立索引,往往會事倍功半.
	
使用索引需要根據實際情況權衡利弊,對於查詢操作量級較大,查詢要求較高的推薦使用索引

使用場景
	a.  當某字段數據更新頻率較低,查詢頻率較高,經常有範圍查詢(>, <, =, >=, <=)或order by、group by發生時建議使用索引。並且選擇度越大,建索引越有優勢,這裏選擇度指一個字段中唯一值的數量/總的數量。
	b.  經常同時存取多列,且每列都含有重複值可考慮建立複合索引

索引使用規則

	a.對於複合索引,把使用最頻繁的列做爲前導列(索引中第一個字段)。如果查詢時前導列不在查詢條件中則該複合索引不會被使用。
	如create unique index PK_GRADE_CLASS on student (grade, class)
	select * from student where class = 2未使用到索引
	select * from dept where grade = 3使用到了索引

	b.避免對索引列進行計算,對where子句列的任何計算如果不能被編譯優化,都會導致查詢時索引失效
	select * from student where tochar(grade)=’2′
	c.比較值避免使用NULL
	d.多表查詢時要注意是選擇合適的表做爲內表。連接條件要充份考慮帶有索引的表、行數多的表,內外表的選擇可由公式:外層表中的匹配行數*內層表中每一次查找的次數確定,乘積最小爲最佳方案。實際多表操作在被實際執行前,查詢優化器會根據連接條件,列出幾組可能的連接方案並從中找出系統開銷最小的最佳方案。
	e.  查詢列與索引列次序一致
	f.  用多表連接代替EXISTS子句
	g.  把過濾記錄數最多的條件放在最前面
	
	
索引的運用時機:

     1) 操作符:=、>、<、IN等
     2) 操作符BETWEEN、LIKE、OR不能用索引,
     如BETWEEN:SELECT * FROM mytable WHERE myfield BETWEEN 10 and 20;
     這時就應該將其轉換成:
     SELECT * FROM mytable WHERE myfield >= 10 AND myfield <= 20;
     此時如果在myfield上有索引的話就可以用了,大大提高速度
     再如LIKE:SELECT * FROM mytable WHERE myfield LIKE 'sql%';
     此時應該將它轉換成:
     SELECT * FROM mytable WHERE myfield >= 'sql' AND myfield < 'sqm';
     此時如果在myfield上有索引的話就可以用了,大大提高速度
     再如OR:SELECT * FROM mytable WHERE myfield = 'abc' OR myfield = 'xyz';
     此時應該將它轉換成:
     SELECT * FROM mytable WHERE myfield IN ('abc', 'xyz');
     此時如果在myfield上有索引的話就可以用了,大大提高速度

二.編譯SQL語句

SQLite想要執行操作,需要將程序中的sql語句編譯成對應的SQLiteStatement,比如select * from record這一句,被執行100次就需要編譯100次。對於批量處理插入或者更新的操作,我們可以使用顯式編譯來做到重用SQLiteStatement。

想要做到重用SQLiteStatement也比較簡單,基本如下:
1.編譯sql語句獲得SQLiteStatement對象,參數使用?代替
2.在循環中對SQLiteStatement對象進行具體數據綁定,bind方法中的index		從1開始,不是0

請參考如下簡單的使用代碼:
	private void insertWithPreCompiledStatement(SQLiteDatabase db) {
		String sql = "INSERT INTO " + TableDefine.TABLE_RECORD + "( " + TableDefine.COLUMN_INSERT_TIME + ") VALUES(?)";
		SQLiteStatement  statement = db.compileStatement(sql);
		int count = 0;
		while (count < 100) {
			count++;
			statement.clearBindings();
			statement.bindLong(1, System.currentTimeMillis());
			statement.executeInsert();
		}
	}

三.顯式使用事務

	在Android中,每次數據庫插入,更新操作都開啓了事務,確保操作的原子性.事務的實現需要藉助rollback journal文件,因此每次數據庫操作都會對文件進行打開讀寫關閉操作.
在批量的進行操作時,就會反覆的對文件進行打開讀寫關閉,降低執行的效率.此時,我們可以顯式的使用事務,將批量操作導致的文件操作次數變爲一次.

	具體的實現代碼如下:
		private void insertWithTransaction(SQLiteDatabase db) {
			int count = 0;
			ContentValues values = new ContentValues();
			try {
				db.beginTransaction();
				while (count++ < 100) {
					values.put(TableDefine.COLUMN_INSERT_TIME, System.currentTimeMillis());
					db.insert(TableDefine.TABLE_RECORD, null, values);
				}
				  db.setTransactionSuccessful();
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				db.endTransaction();
			}
		}

四.查詢時返回更少的結果集及更少的字段

	查詢時,查詢列的參數爲null時,默認查詢所有的列.如果你不需要查詢所有的列時,請將具體的列名傳入參數中,避免使用null,這樣可以提高查詢效率.
	如果只需要返回一條數據時,可以添加limit限制,這樣找到一條結果後就不會再繼續查詢了.

五.避免使用Cursor.getColumnIndex()

	Cursor.getColumnIndex()的時間差不多跟Cursor.getInt()相同,因此,避免使用它,尤其是在循環中.
	使用常量來代替Cursor.getColumnIndex(),或者在循環外使用一次Cursor.getColumnIndex()來獲取index.

六.設定合適的ContentValues容量

	ContentValues內部採用了HashMap來存儲數據,初始容量是8.當添加的數據超過該數值時,會進行擴容.因此可以爲ContentValues設置恰當的容量初始值,
	避免內部頻繁進行擴容,尤其是在循環中使用的時候.
	在使用其他List,Map等容器的時候,也可以參考此點.

七.及時關閉Cursor

八.查詢優化

影響查詢性能的因素:

    1. 對錶中行的檢索數目,越小越好
    2. 排序與否。
    3. 是否要對一個索引。
    4. 查詢語句的形式
	
幾個查詢優化的轉換

		1. 對於單個表的單個列而言,如果都有形如T.C=expr這樣的子句,並且都是用OR操作符連接起來,
			形如: x = expr1 OR expr2 = x OR x = expr3 此時由於對於OR,在SQLite中不能利用索引來優化,
			所以可以將它轉換成帶有IN操作符的子句:
				x IN(expr1,expr2,expr3)這樣就可以用索引進行優化,
			效果很明顯,但是如果在都沒有索引的情況下OR語句執行效率會稍優於IN語句的效率。

		2. 如果一個子句的操作符是BETWEEN,在SQLite中同樣不能用索引進行優化,所以也要進行相應的等價轉換: 
			如:a BETWEEN b AND c可以轉換成:(a BETWEEN b AND c) AND (a>=b) AND (a<=c)。 
			在上面這個子句中, (a>=b) AND (a<=c)將被設爲dynamic且是	(a BETWEEN b AND c)的子句,
			那麼如果BETWEEN語句已經編碼,那麼子句就忽略不計,如果存在可利用的index使得子句已經滿足條件,那麼父句則被忽略。

		3. 如果一個單元的操作符是LIKE,那麼將做下面的轉換:x LIKE ‘abc%’,轉換成:x>=‘abc’ AND x<‘abd’。
			因爲在SQLite中的LIKE是不能用索引進行優化的,所以如果存在索引的話,則轉換後和不轉換相差很遠,因爲對LIKE不起作用,
			但如果不存在索引,那麼LIKE在效率方面也還是比不上轉換後的效率的。

子查詢扁平化 

    例子:SELECT a FROM (SELECT x+y AS a FROM t1 WHERE z<100) WHERE a>5

    對這個SQL語句的執行一般默認的方法就是先執行內查詢,把結果放到一個臨時表中,再對這個表進行外部查詢,這就要對數據處理兩次,
	另外這個臨時表沒有索引,所以對外部查詢就不能進行優化了,如果對上	面的SQL進行處理後可以得到如下SQL語句:
	SELECT x+y AS a FROM t1 WHERE z<100 AND a>5,這個結果顯然和上面的一樣,但此時只需要對數據進行查詢一次就夠了,
	另外如果在表t1上有索引的話就避免了遍歷整個表。

	 運用flatten方法優化SQL的條件:

    1.子查詢和外查詢沒有都用集函數
    2.子查詢沒有用集函數或者外查詢不是個表的連接
    3.子查詢不是一個左外連接的右操作數
    4.子查詢沒有用DISTINCT或者外查詢不是個表的連接
    5.子查詢沒有用DISTINCT或者外查詢沒有用集函數
    6.子查詢沒有用集函數或者外查詢沒有用關鍵字DISTINCT
    7.子查詢有一個FROM語句
    8.子查詢沒有用LIMIT或者外查詢不是表的連接
    9.子查詢沒有用LIMIT或者外查詢沒有用集函數
    10.子查詢沒有用集函數或者外查詢沒用LIMIT
    11.子查詢和外查詢不是同時是ORDER BY子句
    12.子查詢和外查詢沒有都用LIMIT
    13.子查詢沒有用OFFSET
    14.外查詢不是一個複合查詢的一部分或者子查詢沒有同時用關鍵字ORDER BY和LIMIT
    15.外查詢沒有用集函數子查詢不包含ORDER BY
    16.複合子查詢的扁平化:子查詢不是一個複合查詢,或者他是一個UNION ALL複合查詢,但他是都由若干個非集函數的查詢構		    成,他的父查詢不是一個複合查詢的子查詢,也沒有用集函數或者是DISTINCT查詢,並且在FROM語句中沒有其它的表或者子查詢,父查詢和子查詢可能會包含WHERE語句,這些都會受到上面11、12、13條件的限制。


	

	SELECT a+1 FROM (
	SELECT x FROM tab
	UNION ALL
	SELECT y FROM tab
	UNION ALL
	SELECT abs(z*2) FROM tab2
	) WHERE a!=5 ORDER BY 1
		   
	轉換爲:

	SELECT x+1 FROM tab WHERE x+1!=5

	UNION ALL
	SELECT y+1 FROM tab WHERE y+1!=5
	UNION ALL
	SELECT abs(z*2)+1 FROM tab2 WHERE abs(z*2)+1!=5
	ORDER BY 1

九.表數據拷貝到另外一張表中

	INSERT INTO COMPANY_BKP
	SELECT * FROM COMPANY 
	WHERE ID IN (SELECT ID 
              FROM COMPANY) ;

	可以先將原表重命名,然後創建新的表,在將數據拷貝到新表中.

十.常用函數

SQLite 有許多內置函數用於處理字符串或數字數據。下面列出了一些有用的 SQLite 內置函數,且所有函數都是大小寫不敏感,這意味着您可以使用這些函數的小寫形式或大寫形式或混合形式:

序號	函數 & 描述
1	SQLite COUNT 函數
	SQLite COUNT 聚集函數是用來計算一個數據庫表中的行數。
	
2	SQLite MAX 函數
	SQLite MAX 聚合函數允許我們選擇某列的最大值。
	
3	SQLite MIN 函數
	SQLite MIN 聚合函數允許我們選擇某列的最小值。
	
4	SQLite AVG 函數
	SQLite AVG 聚合函數計算某列的平均值。
	
5	SQLite SUM 函數
	SQLite SUM 聚合函數允許爲一個數值列計算總和。
	
6	SQLite RANDOM 函數
	SQLite RANDOM 函數返回一個介於 -9223372036854775808 和 +9223372036854775807 之間的僞隨機整數。
	
7	SQLite ABS 函數
	SQLite ABS 函數返回數值參數的絕對值。
	
8	SQLite UPPER 函數
	SQLite UPPER 函數把字符串轉換爲大寫字母。
	
9	SQLite LOWER 函數
	SQLite LOWER 函數把字符串轉換爲小寫字母。
	
10	SQLite LENGTH 函數
	SQLite LENGTH 函數返回字符串的長度。
	
11	SQLite sqlite_version 函數
	SQLite sqlite_version 函數返回 SQLite 庫的版本。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章