場景假定:一個聯(lián)系人A有兩個電話號碼,分別是32111268和32111269�,F(xiàn)在要對聯(lián)系人的電話進行查詢,以得到聯(lián)系人的raw_contact_id。 (PS:^_^不錯的Android開發(fā)交流群:314230976,驗證:eec,有興趣的話可以加入進來一起討論) 我們知道,在Android系統(tǒng)中,所有和聯(lián)系人有關(guān)的數(shù)據(jù),都存儲在數(shù)據(jù)庫 /data/data/com.android.providers.contacts/databases/contacts2.db里面的data數(shù) 據(jù)表中,因此,可以對該表進行查詢以獲得聯(lián)系人的raw_contact_id。對于上面假定的情況,在data數(shù)據(jù)表中和聯(lián)系人A有關(guān)的電話記錄有兩 條,大致如下: … |A’s raw_contact_id| … … |32111268|… … … |A’s raw_contact_id| … … |32111269|… … 很顯然,這兩個電話號碼屬于同一個聯(lián)系人。現(xiàn)在假定我們要查詢電話號碼中包含“32111”的聯(lián)系人,在sqlite命令行下可以這么寫(假定按raw_contact_id排序): 1 SELECT DISTINCT raw_contact_id FROMdata WHERE mimetype_id = 5 ANDdata1 LIKE ‘2111%’ ORDER BY raw_contact_id; 這樣就會得到唯一的 A’s raw_contact_id 但是如果寫成: 1 SELECT raw_contact_id FROM data WHEREmimetype_id = 5 AND data1 LIKE‘2111%’ ORDER BY raw_contact_id; 得到的結(jié)果就是: A’s raw_contact_id A’s raw_contact_id 這顯然不符合要求,因為對于同一個聯(lián)系人的raw_contact_id,我們不希望在查詢結(jié)果中出現(xiàn)兩次。這就是為什么在前面一條sql語句中加上DISTINCT的原因。 在我們自己的Android應(yīng)用中,要對contacts2.db進行訪問進行訪問, 只能通過ContentResolver對象,這是因為contacts2.db不屬于我們自己的Android應(yīng)用進程,因此,無法得到和 contacts2.db相關(guān)的SQLiteDatabase對象,進而無法調(diào)用SQLiteDatabase中的execSQL方法去執(zhí)行上面的SQL 語句。同時,我們在使用ContentResolver對象對data數(shù)據(jù)表進行查詢的時候,無法使用DISTINCT關(guān)鍵字!這就是說,如果一個聯(lián)系人 有兩個電話號碼符合查詢條件,那么該聯(lián)系人的raw_contact_id就會在返回的Cursor對象中出現(xiàn)兩次!下面的寫法 01 ContentResolver resolver = context.getContentResolver(); 02 03 Cursor cursor = resolver.query(Data.CONTENT_URI, 04 05 newString[]{Data.RAW_CONTACT_ID}, // 居然不支持distinct,如果在這里加上distinct將會出現(xiàn)錯誤! 06 07 Data.MIMETYPE + " = '"+ Phone.CONTENT_ITEM_TYPE + "' AND "+ Data.DATA1 + "LIKE '2111%' ", 08 09 null, 10 11 Data.RAW_CONTACT_ID); 和前面的 1 SELECT raw_contact_id FROM data WHEREmimetype_id = 5 AND data1 LIKE‘2111%’ ORDER BY raw_contact_id; 所得到的結(jié)果是一樣的,會得到兩個一模一樣的A’ raw_contact_id,這顯然不符合要求。 那么怎么辦呢?我們知道Java中Set具有“A collection that contains no duplicate elements”,也就是說Set中的元素是唯一的,當調(diào)用add方法,往Set對象加入對象時,如果被加的對象已經(jīng)在Set中存在,那么該對象將不會 被再次加入,以保證該對象在Set中的唯一性。為此,在上面代碼的基礎(chǔ)上,可以考慮使用實現(xiàn)了Set接口的HashSet。
當然,這樣會增加額外的處理時間,在一個有1200條記錄,其中電話記錄有710條的 data數(shù)據(jù)表中,上面的操作耗時30ms左右(ThinkPad T410, Android 模擬器環(huán)境下),對于普通的和聯(lián)系人有關(guān)的應(yīng)用而言,30ms的延遲算不了什么大事,因此這種變通的方式應(yīng)該是可行的。 個人感覺,Android系統(tǒng)自帶的聯(lián)系人數(shù)據(jù)庫及其ContentResolver 在很多時候都還算比較方面,但同樣在很多情況下,也存在很明顯的限制。對于喜歡自己寫SQL語句的朋友而言,這種限制幾乎是難以忍受的,比如無法通過 ContentResolver在contacts2.db中增加觸發(fā)器(在sqlite命令行下是可以的,但這樣對于要發(fā)布的和聯(lián)系人有關(guān)的引用而言, 這樣做是不合適的)等等。 進而言之,SQLite這個數(shù)據(jù)庫短小精悍,包含的特點也算不少,總體說來相當不錯,否則也就沒有那么多公司采用它了。但同時也存在諸多不足: 1. 不支持存儲過程; 2. 用C、C++可以比較方便地開發(fā)類似于存儲函數(shù)之類的東西(就是在SQL語句中可以使用的那種函數(shù)),但用Java做同樣的事情就相對很麻煩; 3. 在觸發(fā)器內(nèi),不能顯式地執(zhí)行事務(wù)處理; 4. 無法預(yù)先制定觸發(fā)器的觸發(fā)執(zhí)行順序。這個從原理上來講,稍微改動一下源碼應(yīng)該可以做到。 5. 在缺省情況下,插入數(shù)據(jù)的性能很糟糕。一秒鐘插入數(shù)據(jù)記錄的數(shù)量通常在20左右。用事務(wù)進行批量數(shù)據(jù)處理,可以大幅度提高insert的性能,但一個事務(wù)中批量的上限不能超過500(比如500次insert)。 而這些特點,在進行某些嵌入式應(yīng)用開發(fā)的時候是非常有用的。因此在使用SQLite數(shù)據(jù)庫的時候,要充分考慮到這些限制,或者能夠找到可以變通解決問題的辦法。 |