簡介:
SQLite 內置函數比較有限,有時通過添加自定義函數(User-Defined Fuctions)的方式可以實現一些通過普通 SQL 操作無法實現或者實現很麻煩的功能;也可以替換 SQLite 原有的內置函數,使其符合我們的要求。本文側重說明在 Android 環境下的做法。
現在假設我們現在要在 Android 系統的視頻播放器增加一個按照文件擴展名排序的功能,如果不是用自定義函數,就需要先從多媒體數據庫中查詢出來視頻的路徑,然后取出視頻文件的擴展名並進行排序,因為查詢得到的 Cursor 對象不可寫,所以需要生成一個 MatrixCursor,把排序后的數據寫入,最后返回這個 MatrixCursor。偽代碼表示如下:
1 public Cursor getSortedByFileExtensionCursor() { 2 Cursor rawCursor = queryVideoFileNameFromDb(); // 從數據庫查詢出 id 和路徑 3 HashMap<int,String> idAndExtensionMap = getVideoFileExtension(rawCursor); // 獲取 id 和擴展名的 HashMap 4 Cursor result = sortAndCreateNewMatrixCursor(idAndExtensionMap); // 對擴展名進行排序,生成 MatrixCursor 作為結果 5 return result; 6 }
而如果我們能夠向 SQLite 注冊一個自定義函數,很多類似問題就要簡單不少。
1. 通過 C 語言接口添加自定義函數
如果使用的是 Android 提供的 sqlite 程序,需要修改 Android 源碼目錄下的 external/sqlite/android/sqlite3_android.cpp 文件。注冊一個自定義函數,函數名稱是 get_file_ext,在 SQL 語句中的函數名稱是 "_GET_FILE_EXT",簡要代碼如下:
1 extern "C" int register_android_functions(sqlite3 * handle, int utf16Storage) 2 { 3 // ... 4 5 // 注冊自定義函數 6 err = sqlite3_create_function(handle, "_GET_FILE_EXT", 1, SQLITE_UTF8, NULL, get_file_ext, NULL, NULL); 7 if (err != SQLITE_OK) { 8 return err; 9 } 10 11 // ... 12 } 13 14 // 返回文件擴展名函數的具體實現 15 static void get_file_ext(sqlite3_context * context, int argc, sqlite3_value ** argv) 16 { 17 // ... 18 }
然后編譯 external/sqlite,把編譯出來的 libsqlite.so 替換手機 /system/lib/ 下面的庫。使用該自定義函數也很簡單:
1 public Cursor getSortedByFileExtensionCursor() { 2 // 在 sort by 那里使用我們的 SQLite 自定義函數 3 Cursor result = query("SELECT * FROM video SORT BY _GET_FILE_EXT(_data)"); 4 }
Note:如果不是在 Android 環境下,或者無法修改 Android 相關代碼,則需要自行通過 c 語言的接口操作數據庫,然后同樣使用 sqlite3_create_function 這個函數注冊自定義函數。如果使用的是 C#/Python/PHP 等語言,這些語言都有相應的 SQLite wrapper,可以類似的操作數據庫並注冊自定義函數。
2. 通過 Java 語言接口添加自定義函數
Android SQLiteDatabase 類里面有一個添加自定義函數的隱藏接口(@hide),在 Android 源碼中編譯的話可以使用該接口,使用方法大致如下:
1 public class DatabaseHelper extends SQLiteOpenHelper { 2 private String mFileExt; 3 public DatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { 4 super(context, name, factory, version); 5 } 6 7 @Override 8 public void onOpen(SQLiteDatabase db) { 9 db.addCustomFunction("_GET_FILE_EXT", 1, mGetFileExtension); 10 } 11 12 private final SQLiteDatabase.CustomFunction mGetFileExtension = 13 new SQLiteDatabase.CustomFunction() { 14 @Override 15 public void callback(String[] args) { 16 String file = args[0]; 17 int index = file.lastIndexOf("."); 18 if (index != -1 && file.length() > index + 1) { 19 mFileExt = file.substring(index); // 獲取到擴展名 20 } 21 } 22 }; 23 }
然后對這個數據庫進行查詢操作時,就可以使用我們添加的 _GET_FILE_EXT 函數了。Android 源碼目錄下的 pacakges/providers/MediaProvider 這個程序就使用了此接口,可以參考。
Note: 對外發布的程序最好不要依賴隱藏的 Java 接口,兼容性難以保證。
擴展閱讀:
- Create Or Redefine SQL Functions:創建自定義函數的解釋。
- Quickstart:使用 c 語言接口打開 SQLite 數據庫的簡單示例代碼。
轉載請注明出處:http://www.cnblogs.com/imouto/archive/2013/06/14/how-to-add-user-defined-functions-for-sqlite.html
本文外部鏡像:http://oteku.blogspot.com/2013/06/how-to-add-user-defined-functions-for-sqlite.html
