en

hi, it seems you are using microsoft internet explorer. it doesn't match web standard and causes problems browsing this site. please please please use mozilla firefox or google chrome instead. thank you!

zh

哦哦!您正在使用Internet Explorer 瀏覽器,它與我們的網頁標準並不相容,可能會導致畫面顯示不正常。
請改用 Mozilla Firefox 或者 Google Chrome 才能正常瀏覽本網站,謝謝!

6.24.2013

SQLite3 的二三事

MAC OS 自從 10.4 起就已經內建 SQLite 這套資料庫軟體, 由於內建的是第三版的 SQLite 所以又稱作是 SQLite3。這裡暫且不管資料庫是如何建立的,因為有太多建立 SQLite3 資料庫的方法,例如使用 Mozilla Filefox 瀏覽器的外掛模組(SQLite Manager) 或是其他包含 GUI 介面的軟體,都是透過 Terminal 終端機來下達 SQLite 的指令,當然你也可以自己使用 Terminal 來建立自己的資料庫。有關 SQLite 的相關語法指令集可以參閱官方網站獲得更多資訊。

環境設定
在環境設定的部份必須先引入 SQLite3 的標頭檔與 Framework(libsqlite3.dylib),如果你已經有現成的 Sqlite 資料庫檔案,則需要將檔案放置專案下的 Resources 內,Xcode 4 則是放入專案名稱資料夾下的 Supporting Files 中。

替專案加入 libsqlite3.dylib

#import <sqlite3.h>
如果是使用 Xcode 4 編譯器的朋友可以參閱 Xcode 4 新增 Framework 的方法來新增。

一切都設定好之後就要開始與資料庫取得聯繫,程式碼如下。


連結資料庫與建立
連結現有 Resources/Supporting Files 中的資料庫
在開始之前先來檢閱一下之後要用到資料庫的內容,這是一個我們使用 Firefox 瀏覽器外掛模組所建立的學生考試資料庫(Exam),裡面包含一個期末考試的表格(Final),表格內有兩個欄位分別是學號(SID)與分數(Score)。

Exam.sqlite 的內容

//設定資料庫檔案的路徑
NSString *url = [[[NSBundle mainBundle]resourcePath] stringByAppendingPathComponent:@"Exam.sqlite"];
sqlite3 *database = nil;

if (sqlite3_open([url UTF8String], &database) == SQLITE_OK) {
    NSLog(@"DB OK");
    //這裡寫入要對資料庫操作的程式碼

    //使用完畢後關閉資料庫聯繫
    sqlite3_close(database);
}

連結或建立其他路徑下的資料庫
除了連結現有的資料庫外,你也可以使用同樣的方式動態產生 Sqlite 資料庫,如果資料庫不存在於路徑下,Sqlite 就會幫你建立該資料庫,下面我們以 iOS 中 Documents 資料夾為例。
NSArray *documentsPath=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *databaseFilePath=[[documentsPath objectAtIndex:0] stringByAppendingPathComponent:@"Exam"];
sqlite3 *database = nil;

if (sqlite3_open([databaseFilePath UTF8String], &database)==SQLITE_OK) {
    NSLog(@"DB OK");
    //這裡寫入要對資料庫操作的程式碼

    //使用完畢後關閉資料庫聯繫
    sqlite3_close(database);
}



資料庫查詢
程式碼到這裡如果可以成功連結資料庫就會在 Console 印出 DB OK 的字樣。下面我們將對資料庫做兩個簡單的操作取得資料筆數與內容。
//查閱一共有多少筆資料
//建立 Sqlite 語法
const char *sql = "select count(*) from Final";

//statement 將存放查詢結果
sqlite3_stmt *statement =nil;

if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) {
    if (sqlite3_step(statement) == SQLITE_ROW) {

        int count = sqlite3_column_int(statement, 0);
        NSLog(@"共有 %d 比資料。", count);
    }

    //使用完畢後將statement清空
    sqlite3_finalize(statement);
}

//查閱所有資料內容
//建立 Sqlite 語法
const char *sql = "select * from Final";

//stm將存放查詢結果
sqlite3_stmt *statement =nil;

if (sqlite3_prepare_v2(database, sql, -1, &statement, NULL) == SQLITE_OK) {
    while (sqlite3_step(statement) == SQLITE_ROW) {

        NSString *sid, *score;

        sid = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 0)];
        score = [NSString stringWithUTF8String:(char *)sqlite3_column_text(statement, 1)];

        NSLog(@"學生: %@ ,分數: %@", sid, score);
    }

    //使用完畢後將statement清空
    sqlite3_finalize(statement);
}

執行結果

上述程式碼中 sqlite3_column_text 帶入參數 0 或是 1 分別是對應兩個表格中的欄位(學號與分數),另一種方式,可以考慮把擷取回來的資料作成物件放入 NSMutableArray 中,方便之後讀取與修改。


建立表格與輸入資料
在這部份我們動態產生一份與使用 Firefox 瀏覽器外掛模組所建立的學生考試資料庫(Exam)完全一樣內容的資料表,請看以下程式碼。
//建立表格
char *errorMsg;
const char *createSql="create table if not exists Final (SID integer primary key autoincrement, Score text)";

if (sqlite3_exec(database, createSql, NULL, NULL, &errorMsg)==SQLITE_OK) {
    NSLog(@"TABLE OK");
    //建立成功之後要對資料庫操作的程式碼

    } else {
    //建立失敗時的處理
    NSLog(@"error: %s",errorMsg);

//清空錯誤訊息
    sqlite3_free(errorMsg);
}

//插入資料
char *insertErrorMsg;

//建立 Sqlite 語法
const char *insertSql="insert into Final (Score)values('80')";
if (sqlite3_exec(database, insertSql, NULL, NULL, &insertErrorMsg)==SQLITE_OK) {
    NSLog(@"INSERT OK");
}
insertSql="insert into Final (Score)values('70')";
if (sqlite3_exec(database, insertSql, NULL, NULL, &insertErrorMsg)==SQLITE_OK) {
    NSLog(@"INSERT OK");
}
insertSql="insert into Final (Score)values('90')";
if (sqlite3_exec(database, insertSql, NULL, NULL, &insertErrorMsg)==SQLITE_OK) {
    NSLog(@"INSERT OK");
}
insertSql="insert into Final (Score)values('85')";
if (sqlite3_exec(database, insertSql, NULL, NULL, &insertErrorMsg)==SQLITE_OK) {
    NSLog(@"INSERT OK");
}
insertSql="insert into Final (Score)values('95')";
if (sqlite3_exec(database, insertSql, NULL, NULL, &insertErrorMsg)==SQLITE_OK) {
    NSLog(@"INSERT OK");
}

查詢上述動態產生資料庫的執行結果







23 則留言:

  1. 匿名7/04/2013

    您好!
    請問sqlite3.h跟sqlite3.0.h有什麼差別嗎?

    回覆刪除
    回覆
    1. 您好:

      我怎麼不知道有 sqlite3.0.h 這個標頭檔。

      在framework裡只有sqlite3.h耶,我這邊沒看到 sqlite3.0.h

      刪除
  2. 匿名7/08/2013

    http://pro.ctlok.com/2010/07/iphone-ipad-with-sqlite.html
    這個教學網站有看到,我的xcode新增也有看到,請參考..謝謝您

    回覆刪除
    回覆
    1. 您好:

      謝謝您,
      不過,我覺得是一樣的東西,因為最後都是import sqlite3.h 標頭檔。

      另外不管如何,這邊文章與您所提供的連結 Sqlite 的版本都已經過時,目前我看到的好像是3.7.4,我不確定最新的 iOS sdk 是否就內建此版本,有興趣的話可以去官方網站查閱看看!!

      刪除
  3. Hi牛奶 ~請教一下 關於FMDB 裡面 我用資料庫sqlite型式,我的APP做好了~但我希望我在外部另有一個sever 資料庫 ~ 主要是希望我在外部sever 改資料 APP裡面資料庫 也可以下載更新~~請問有建議方式嗎?

    回覆刪除
    回覆
    1. Bryan Chen
      沒什麼特別的建議耶,如果你希望資料可以同步,可以使用推播的方式(主動),另一方便你也可以在開啟app的時候連線到server做版本確認來更新資料(被動)。

      刪除
  4. 不好意思~請問一下~
    我想把iphone拍的照片存到sqlite裡面~而不是手機的相簿裡面
    我該怎麼處理,我有使用BLOB這個屬性建立一個欄位,可是我放照片進去再讀出來的時候是一片黑,我是放UIImage進去這個欄位,這樣對嗎?
    另外我將我建立的像本讀出的時候使用collection view但在選取照片然後放大顯示這部分~以及滑動切換上下一張照片要全部自己寫嗎?

    回覆刪除
    回覆
    1. Ohlulu 你好:

      sqlite 的欄位應該沒有任何一個屬性是叫做UIImage,所以你必須把UIImage轉成 NSData,之後再轉成byte或是char做儲存,讀取的時候也一樣,不過這樣欄位的大小將非常可觀,另一種方式將圖片統一純在一個資料夾內,而 sqlite 只儲存檔案名稱,相對來說這個方式會比較好實作。

      collection view 這是什麼view?
      是的都要自己實作,不過您可以參考這一篇「使用 UIScrollView 實作圖片的縮放與移動」。
      http://furnacedigital.blogspot.tw/2013/09/uiscrollview.html

      刪除
    2. soga~那只好把圖片存在Documents裡面了~因為這算是我第一次使用"資料庫"(不是第一次使用sqlite喔~)
      我詢問朋友什麼屬性的欄位可以存照片,他說用BLOB,我以為這是可以放圖檔的意思XD

      collectionView 跟 tableView 很像,一個是橫的,另一個是一格一格的~
      我想要做的樣子是跟手機裡面內建的相簿相似的模式,另外使用UIScrollView這方法我知道~
      只是我想要的是點小圖片(collectionView),會跑出全螢幕的圖片,然後左右滑動可以切換上下一張圖片

      刪除
    3. Ohlulu 您好
      BLOB就是意指大量的2位元資料(binary large object),所以一樣是要將您的影像轉換成 NSData 才可以做存取,不過這部份我還沒有實作過,存放至資料庫中。
      另外關於 collectionView 我還沒有研究過咧,所以沒辦法回答你的問題,抱歉唷。

      刪除
    4. 我剛剛發現您要的功能在官方的sample code 中已經完成了大半...
      https://developer.apple.com/library/ios/navigation/#section=Resource%20Types&topic=Sample%20Code

      至於左右滑動可以切換上下一張圖片,你可以能要先定義下一張圖片與上一張圖片的的關係,使用 UIGestureRecognizer 來辨識換圖片的動作應該可以輕鬆完成,剩下就是 UIViewController 的調換。

      刪除
    5. 真的超感謝!!
      說真的這種官方文件我幾乎沒怎麼在看,最多就看看reference找方法,因為他是全英文的,完全有看沒有懂~

      至於你說的定義上下張圖片的關係,如果我在存照片的時候給他一個photo_id,然後可以用這個判斷嗎?
      不過這樣用的話,我刪除中間的照片後,判斷會不會出問題?

      還是我把相簿裡面的照片抓出來之後建成一個陣列,用這個去判斷它們之間上一張下一張的關係。

      刪除
    6. Ohlulu 您好:
      陣列比較好喔,反正你是使用NSMutableArray做操作,NSMutableArray也可以存放uiimage,記得對NSMutableArray做任何動作之後都要反饋到你的畫面上,要更新畫面上的圖片。

      刪除
    7. 這sample code 也太複雜...
      我連哪個viewController對到哪個view都搞不太清楚...
      不曉得牛奶大有沒有時間幫我看一下,那堆.m檔各是處理什麼功能><

      刪除
    8. Ohlulu 您好

      哈哈你在開玩笑吧,我沒辦法幫你看啦!
      不過我倒是找到有個大陸網站說明這些程式碼。
      http://blog.sina.com.cn/s/blog_5a6efa330101doc9.html

      你可以參考看看唷

      刪除
  5. 你好:
    內容中有提到可以把擷取回來的資料作成物件放入 NSMutableArray 中,想請問要如何做><" 謝謝

    回覆刪除
    回覆
    1. NSMutableArray 可以以陣列的方式存放任何物件,與一般的nsarray不同。
      你可以把擷取回來的東西包裝成一個物件,或是結構,在將其存放至NSMutableArray中,應該有個add object的函式可以使用。

      刪除
  6. HI~我在儲存中文時、轉出來變成亂碼

    回覆刪除
    回覆
    1. jacoblogger
      只是編碼問題,試試看存取都使用NSUTF8StringEncoding 編碼。

      刪除
  7. 請問 都是在appdeleage.m 中的 didFinishLaunchingWithOptions 做嗎?

    回覆刪除
    回覆
    1. 您好:
      您是說上述範例嘛,我是在ViewController中實作的,appdeleage中實作也是可以,只是 didFinishLaunchingWithOptions 中ViewController還並未生成,在介面的部份必須等到didViewLoad中才能實作

      刪除
    2. 牛奶你好
      我是一個剛碰觸xcode的新手 我用的版本是最新的5 會沒辦法使用此code嗎?
      我在firefox中的SQlite Manger中 創了 SID(int) Score(int) 新增幾筆資料後輸出資料 並改名為XX.sqlite 丟進xcode
      framework 匯入 libsqlite3.dylib 然而在viewcontroler.h 中 打上
      後續在 viewcontroler.m 的 - (void)viewDidLoad 丟入你的程式碼 (原封不動只有改資料庫名稱 stringByAppendingPathComponent:@"XXX.sqlite")
      運行後 有顯示"DB OK"

      但是後續我貼上你的第二段 要查詢資料 成功執行 但是卻沒有資料回來 想問我是不是少了什麼動作
      有更改
      const char *sql = "select * from XXX"


      另外 不知道可否提供信箱 方便問問題?

      刪除
    3. 您好:

      信箱在這裡:furnacedigital@gmail.com
      這樣我也看不出問題在哪,你把CODE整理一下寄來有空我幫您看看,
      另外,XCODE的版本都可以向下相容,並不會有問題。

      刪除