iOS開發數據庫SQLite的使用

iOS系統自帶Core Data來進行持久化處理,並且Core Data可使用圖形化界面來建立對象,可是Core Data不是關係型數據庫,對於Core Data來講比較擅長管理在設備上建立的數據持久化存儲用戶建立的對象,可是要處理大量的數據時就應該優先選擇SQL關係型數據庫來存儲這些數據。
Core Data在後臺也是使用SQLite來存儲數據的,可是開發人員不能直接訪問這些數據,只能經過Core Data提供的API來操做,若是一旦人爲的經過SQLite修改這些數據那麼使用Core Data再次訪問這些數據時就會發生錯誤。java

SQLite庫

SQLite是使用C語言寫的開源庫,實現了一個自包含的SQL關係型數據庫引擎,可使用SQLite存儲操做大量的數據,做爲關係型數據庫咱們能夠在一個數據庫中創建多張相關聯的表來解決大量數據重複的問題。並且SQLite庫也針對移動設備上的使用進行了優化。
由於SQLite的接口使用C寫的,並且Objective-CC的超集因此能夠直接在項目中使用SQLiteios

設計一個數據庫

開始以前首先要想到須要存什麼數據,而後怎麼設計這個數據庫。
首先咱們設計一個數據庫用來存儲人員信息以下:sql

id name age email region
1 jhon 20 jhon@mail beijing
2 peter 20 peter@mail shanghai
3 july 20 july@mail beijing
4 elev 20 elev@mail shenzhen
5 ribet 20 ribet@mail beijing

上面是全部的人員信息,實際可能比這個多不少。可是咱們發現region這一行中有不少的數據重複出現。不少人可能來自同一個地方,爲了不這種狀況咱們應該再從新建立一張表來單獨存儲region這列的信息而後在這個表中引用region表中的信息。固然咱們還能夠在region表中添加更多的信息好比詳細地址。如今建立兩張表peopleregion以下所示shell

  • people表
id name age email region
1 jhon 20 jhon@mail 1
2 peter 20 peter@mail 2
3 july 20 july@mail 1
4 elev 20 elev@mail 3
5 ribet 20 ribet@mail 1
  • region表
regionid regioninfo address
1 beijing fengtai
2 shanghai jingan
3 shenzhen futian

使用SQLite建立數據庫

爲了熟悉SQLite語句,打開shell使用SQLite命令行來建立一個數據庫數據庫

  • 打開建立數據庫
    打開shell切換到指定目錄輸入
sqlite3 database.db

這行命令是啓動sqlite命令行而且建立新的數據庫database.db並附加該數據到命令行
此時已經進入sqlite命令行經過輸入.help能夠顯示可使用哪些命令,經過輸入.databases來查看當前有哪些數據庫附加到當前的命令行工具中。輸入.quit.exit退出當前命令行工具安全

  • 建立表
create table "main"."people" ("id" integer primary key autoincrement not null, "name" text,"age" integer,"email" text,"region" integer);

這條命令是建立一個people的表,而且將id字段設爲primary key主鍵將其指定爲一個autoincrement自動增加的字段。表示不用提供id的值數據庫將自動生成。後面的表示該張表中所含有的字段。ruby

由於要設計兩張表因此還須要建立regionmarkdown

create table "main"."region" ("regionid" integer primary key autoincrement not null, "regioninfo" text,"address" text not null);
  • 添加數據
    此時已經成功建立了兩張表咱們要添加數據進去
insert into "main"."people" ("name","age","email","region") values ('jhon','20','jhon@mail','1');

這樣成功往people表成功的插入了一條數據。這樣寫效率比較低。每次只能插入一條數據不要擔憂SQLite支持將文件直接導入數據庫中。能夠是普通的文件文件也能夠是excel文件。下面咱們建立一個people.txt文件格式以下:app

1   jhon    20  jhon@mail   1
2   peter   20  peter@mail  2
3   july    20  july@mail   1
4   elev    20  elev@mail   3
5   ribet   20  ribet@mail  1

注意每一個字段之間的空隙是用製表符\t來分割的,也就是建立文件是每一個字段用tab鍵進行分割。字段的順序必須和表中的順序相同而後將people.txt文件導入people表中函數

.separator "\t"

根據\t來分割字段,而後接着輸入

.import "people.txt" people

導入people.txt文件到people表中此時會提示以下錯誤信息

people.txt:1: INSERT failed: UNIQUE constraint failed: people.id

不用擔憂這個意思是說已經存在了一個id爲1的數據因此這條數據插入失敗,是由於咱們以前手動了插入了一條數據。能夠經過如下指令來查插入的數據

select * from people;

而後用一樣的方法建立一個region.txt的文件並將其導入region表中。

注意
使用SQLite命令行可能會出現...>這表示指令輸入錯誤,按ctrl+d便可退出

  • 查詢數據上面已經添加完數據經過select指令能夠查詢這些數據
select * from people;

查詢popple表中的全部數據

連接表數據

select name,regioninfo from people,region where people.region=region.regionid;

輸出結果

jhon    beijing
peter   shanghai
july    beijing
elev    shenzhen
ribet   beijing

peopleregion表中查找nameregioninfo字段而且只查詢people.region=region.regionid相匹配的結果,若是沒有這個條件那麼將出現5*3=15條數據

若是要查找某個地區的人使用where來篩選條件

select name,regioninfo from people,region where people.region=region.regionid and region.regioninfo="beijing";

輸出結果

jhon    beijing
july    beijing
ribet   beijing

iOS中SQLite的使用

開始以前應該在項目中引用SQLite庫。TARGETS->General->Linked Frameworks and Libraries以下圖所示

將以前建立好的database.db文件導入項目中,並引入sqlite3.h頭文件

#import <sqlite3.h>

使用SQLite須要一下幾個步驟

  • 聲明類變量sqlite3來保存對數據庫的引用
  • 使用sqlite3_open打開數據庫
  • 建立SQLite語句
  • 建立SQLite語句對象sqlite3_stmt
  • 準備SQLite語句sqlite3_prepare_v2
  • 開始遍歷結果sqlite3_step

初始化打開數據庫

sqlite3 * database;
-(void)initDatabase
{
    NSString *path = [[NSBundle mainBundle] pathForResource:@"database" ofType:@"db"];   
    if (sqlite3_open([path UTF8String], &database) == SQLITE_OK) {
        NSLog(@"open database");
    }
    else{
        sqlite3_close(database);
        NSLog(@"error %s",sqlite3_errmsg(database));
    }
}

打開數據庫若是返回的狀態碼不是SQLITE_OK那麼打開失敗關閉數據庫而且輸出錯誤信息

查詢數據

-(void)operateDatabase
{
    const char * sql = "select name,regioninfo from people,region where people.region=region.regionid";
    sqlite3_stmt *statement;     //建立sql語句對象
    int sqlResult = sqlite3_prepare_v2(database, sql, -1, &statement, NULL);    //準備sql語句
    if ( sqlResult== SQLITE_OK) //是否準備結束
    {
        while (sqlite3_step(statement) == SQLITE_ROW)   //開始遍歷查詢結果
        {
            NSLog(@"name %s, region %s",sqlite3_column_text(statement, 0),sqlite3_column_text(statement, 1));
        }
    }
}

輸出結果:

name jhon, region beijing
name peter, region shanghai
name july, region beijing
name elev, region shenzhen
name ribet, region beijing

sqlite3_prepare_v2的參數第一個是數據庫鏈接,第二個是sql語句,第三個是這個語句的長度傳入-1表示地道第一個null終止符爲止,第四個是返回一個語句對象,第五個是返回一個指向該sql語句的第一個字節的指針。
sqlite3_prepare_v2返回狀態碼SQLITE_OK時開始遍歷結果。
sqlite3_step用來遍歷結果若是返回爲SQLITE_ROW表示下一行準備結束能夠開始查詢。因此此處用一個while來便利因此查詢的結果
遍歷的過程當中要取到結果經過一下的函數獲取遍歷結果

SQLITE_API const void *SQLITE_STDCALL sqlite3_column_blob(sqlite3_stmt*, int iCol);
SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes(sqlite3_stmt*, int iCol);
SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
SQLITE_API double SQLITE_STDCALL sqlite3_column_double(sqlite3_stmt*, int iCol);
SQLITE_API int SQLITE_STDCALL sqlite3_column_int(sqlite3_stmt*, int iCol);
SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_column_int64(sqlite3_stmt*, int iCol);
SQLITE_API const unsigned char *SQLITE_STDCALL sqlite3_column_text(sqlite3_stmt*, int iCol);
SQLITE_API const void *SQLITE_STDCALL sqlite3_column_text16(sqlite3_stmt*, int iCol);
SQLITE_API int SQLITE_STDCALL sqlite3_column_type(sqlite3_stmt*, int iCol);
SQLITE_API sqlite3_value *SQLITE_STDCALL sqlite3_column_value(sqlite3_stmt*, int iCol);

上面是所支持的結果類型,第一個參數爲sql語句對象,第二個爲獲取哪一列的信息。

參數化查詢

上面的狀況每次sql語句都寫死了,若是想要改變某個條件就須要從新寫一條語句,幸虧sqlite支持參數化查詢,每次只須要更改查詢條件就能夠而不用更改整條sql語句,若是如今只想查詢北京地區的人口信息使用參數化查詢以下:

-(void)operateDatabase
{
    const char * sql = "select name,regioninfo from people,region where people.region=region.regionid and regioninfo=?";
    sqlite3_stmt *statement;     //建立sql語句對象
    int sqlResult = sqlite3_prepare_v2(database, sql, -1, &statement, NULL);    //準備sql語句

    sqlite3_bind_text(statement, 1, "beijing", -1,SQLITE_TRANSIENT);    //綁定參數

    if ( sqlResult== SQLITE_OK) //是否準備結束
    {
        while (sqlite3_step(statement) == SQLITE_ROW)   //開始遍歷查詢結果
        {
            NSLog(@"name %s, region %s",sqlite3_column_text(statement, 0),sqlite3_column_text(statement, 1));
        }
    }
}

輸出結果:

name jhon, regionbeijing
name july, regionbeijing
name ribet, regionbeijing

可見須要更改的條件sql中用?來代替,而後用sqlite3_bind_text函數來綁定參數。根據類型不一樣綁定的函數也不一樣

SQLITE_API int SQLITE_STDCALL sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
SQLITE_API int SQLITE_STDCALL sqlite3_bind_blob64(sqlite3_stmt*, int, const void*, sqlite3_uint64,void(*)(void*));
SQLITE_API int SQLITE_STDCALL sqlite3_bind_double(sqlite3_stmt*, int, double);
SQLITE_API int SQLITE_STDCALL sqlite3_bind_int(sqlite3_stmt*, int, int);
SQLITE_API int SQLITE_STDCALL sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64);
SQLITE_API int SQLITE_STDCALL sqlite3_bind_null(sqlite3_stmt*, int);
SQLITE_API int SQLITE_STDCALL sqlite3_bind_text(sqlite3_stmt*,int,const char*,int,void(*)(void*));
SQLITE_API int SQLITE_STDCALL sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*));
SQLITE_API int SQLITE_STDCALL sqlite3_bind_text64(sqlite3_stmt*, int, const char*, sqlite3_uint64,void(*)(void*), unsigned char encoding);
SQLITE_API int SQLITE_STDCALL sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
SQLITE_API int SQLITE_STDCALL sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n);

上面列出了全部支持綁定類型的函數。

結束

本篇只是列出了SQLite經常使用的基礎方法,實際開發中數據庫可能要比這複雜許多,並且還要考慮數據競爭線程安全的問題。具體仍是要本身在開發中實踐。

相關文章
相關標籤/搜索