使用Android系統提供的ContentResolver,無法進行distinct查詢的workaround

來源:互聯網
上載者:User

情境假定:一個連絡人A有兩個電話號碼,分別是32111268和32111269。現在要對連絡人的電話進行查詢,以得到連絡人的raw_contact_id。

 

我們知道,在Android系統中,所有和連絡人有關的資料,都儲存在資料庫/data/data/com.android.providers.contacts/databases/contacts2.db裡面的data資料表中,因此,可以對該表進行查詢以獲得連絡人的raw_contact_id。對於上面假定的情況,在data資料表中和連絡人A有關的電話記錄有兩條,大致如下:

…  |A’s raw_contact_id| … … |32111268|… …

…  |A’s raw_contact_id| … … |32111269|… …

很顯然,這兩個電話號碼屬於同一個連絡人。現在假定我們要查詢電話號碼中包含“32111”的連絡人,在sqlite命令列下可以這麼寫(假定按raw_contact_id排序):

SELECT DISTINCT raw_contact_id FROMdata WHERE mimetype_id = 5 AND data1 LIKE ‘%32111%’ ORDER BY raw_contact_id;

這樣就會得到唯一的

A’s raw_contact_id

 

但是如果寫成:

SELECT raw_contact_id FROM data WHEREmimetype_id = 5 AND data1 LIKE ‘%32111%’ ORDER BY raw_contact_id;

得到的結果就是:

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資料庫的時候,要充分考慮到這些限制,或者能夠找到可以變通解決問題的辦法。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.