咱們知道,在Android系統中,全部和聯繫人有關的數據,都存儲在數據庫/data/data/com.android.providers.contacts/databases/contacts2.db裏面的data數據表中,所以,能夠對該表進行查詢以得到聯繫人的raw_contact_id。對於上面假定的狀況,在data數據表中和聯繫人A有關的電話記錄有兩條,大體以下: android
… |A’s raw_contact_id| … … |32111268|… … sql
… |A’s raw_contact_id| … … |32111269|… … 數據庫
很顯然,這兩個電話號碼屬於同一個聯繫人。如今假定咱們要查詢電話號碼中包含「32111」的聯繫人,在sqlite命令行下能夠這麼寫(假定按raw_contact_id排序): ide
SELECT DISTINCT raw_contact_id FROMdata WHERE mimetype_id = 5 AND data1 LIKE ‘%32111%’ ORDER BY raw_contact_id; 函數
這樣就會獲得惟一的 性能
A’s raw_contact_id spa
可是若是寫成: 命令行
SELECT raw_contact_id FROM data WHEREmimetype_id = 5 AND data1 LIKE ‘%32111%’ ORDER BY raw_contact_id; sqlite
獲得的結果就是: 對象
A’s raw_contact_id
A’s raw_contact_id
這顯然不符合要求,由於對於同一個聯繫人的raw_contact_id,咱們不但願在查詢結果中出現兩次。這就是爲何在前面一條sql語句中加上DISTINCT的緣由。
在咱們本身的Android應用中,要對contacts2.db進行訪問進行訪問,只能經過ContentResolver對象,這是由於contacts2.db不屬於咱們本身的Android應用進程,所以,沒法獲得和contacts2.db相關的SQLiteDatabase對象,進而沒法調用SQLiteDatabase中的execSQL方法去執行上面的SQL語句。同時,咱們在使用ContentResolver對象對data數據表進行查詢的時候,沒法使用DISTINCT關鍵字!這就是說,若是一個聯繫人有兩個電話號碼符合查詢條件,那麼該聯繫人的raw_contact_id就會在返回的Cursor對象中出現兩次!下面的寫法
ContentResolver resolver = context.getContentResolver();
Cursor cursor = resolver.query(Data.CONTENT_URI,
new String[]{Data.RAW_CONTACT_ID}, // 竟然不支持distinct,若是在這裏加上distinct將會出現錯誤!
Data.MIMETYPE + " = '"+ Phone.CONTENT_ITEM_TYPE + "' AND " + Data.DATA1 + "LIKE '%32111%' ",
null,
Data.RAW_CONTACT_ID);
和前面的
SELECT raw_contact_id FROM data WHEREmimetype_id = 5 AND data1 LIKE ‘%32111%’ ORDER BY raw_contact_id;
所獲得的結果是同樣的,會獲得兩個如出一轍的A’ raw_contact_id,這顯然不符合要求。
那麼怎麼辦呢?咱們知道Java中Set具備「A collection that contains no duplicate elements」,也就是說Set中的元素是惟一的,當調用add方法,往Set對象加入對象時,若是被加的對象已經在Set中存在,那麼該對象將不會被再次加入,以保證該對象在Set中的惟一性。爲此,在上面代碼的基礎上,能夠考慮使用實現了Set接口的HashSet。
HashSet<Integer> hashSet = new HashSet<Integer>(); // 用這種方式(Set)來保證惟一性
while(cursor.moveToNext())
{
hashSet.add(cursor.getInt(0));
}
這樣一來,在hashSet中的A’s raw_contact_id就只有一個了,也就是說經過這種方式,變通地實現了distinct這個sql關鍵字的語義。
固然,這樣會增長額外的處理時間,在一個有1200條記錄,其中電話記錄有710條的data數據表中,上面的操做耗時30ms左右(ThinkPad T410, Android 模擬器環境下),對於普通的和聯繫人有關的應用而言,30ms的延遲算不了什麼大事,所以這種變通的方式應該是可行的。
我的感受,Android系統自帶的聯繫人數據庫及其ContentResolver在不少時候都還算比較方面,但一樣在不少狀況下,也存在很明顯的限制。對於喜歡本身寫SQL語句的朋友而言,這種限制幾乎是難以忍受的,好比沒法經過ContentResolver在contacts2.db中增長觸發器(在sqlite命令行下是能夠的,但這樣對於要發佈的和聯繫人有關的引用而言,這樣作是不合適的)等等。
進而言之,SQLite這個數據庫短小精悍,包含的特色也算很多,整體說來至關不錯,不然也就沒有那麼多公司採用它了。但同時也存在諸多不足:
1. 不支持存儲過程;
2. 用C、C++能夠比較方便地開發相似於存儲函數之類的東西(就是在SQL語句中可使用的那種函數),但用Java作一樣的事情就相對很麻煩;
3. 在觸發器內,不能顯式地執行事務處理;
4. 沒法預先制定觸發器的觸發執行順序。這個從原理上來說,稍微改動一下源碼應該能夠作到。
5. 在缺省狀況下,插入數據的性能很糟糕。一秒鐘插入數據記錄的數量一般在20左右。用事務進行批量數據處理,能夠大幅度提升insert的性能,但一個事務中批量的上限不能超過500(好比500次insert)。
而這些特色,在進行某些嵌入式應用開發的時候是很是有用的。所以在使用SQLite數據庫的時候,要充分考慮到這些限制,或者可以找到能夠變通解決問題的辦法。