SQLite數據庫基礎(看完就上手)

標籤(空格分隔): iOS數據庫 數據庫總結 SQLite數據庫ios


ios中的數據存儲方式及其特色git

  1. Preference(偏好設置\NSUserDefaults):也不能存儲自定義對象
  2. NSCoding(NSKeyedArchiver\NSkeyedUnarchiver):歸檔,侷限:一次性存取,讀所有讀出來,寫會覆蓋
  3. SQLite3 :關係型數據庫,不能直接存儲對象,要將對象拆開存儲
  4. Core Data :對象型的數據庫,內部透明

##1、什麼是SQLite程序員

什麼是數據庫? 數據庫(Database)是按照數據結構來組織、存儲和管理數據的倉庫 數據庫能夠分爲2大種類:1. 關係型數據庫(主流) 2. 對象型數據庫github

SQLite是一款輕型的嵌入式數據庫,它佔用資源很是的低,在嵌入式設備中,可能只須要幾百K的內存就夠了,它的處理速度比Mysql、PostgreSQL這兩款著名的數據庫都還快。sql


##2、SQL語句 在程序運行過程當中操做數據庫中的數據,得先學會使用SQL語句數據庫

###1. 什麼是SQL編程

SQL(structured query language):結構化查詢語言 SQL是一種對關係型數據庫中的數據進行定義和操做的語言 SQL語言簡潔,語法簡單,好學好用swift

###2. 什麼是SQL語句安全

使用SQL語言編寫出來的句子\代碼,就是SQL語句 在程序運行過程當中,要想操做(增刪改查,CRUD)數據庫中的數據,必須使用SQL語句bash

###3. SQL語句的特色

不區分大小寫(好比數據庫認爲user和UsEr是同樣的) 每條語句都必須以分號 ; 結尾

###4. SQL中的經常使用關鍵字

select、insert、update、delete、from、create、where、desc、order、by、group、table、alter、view、index等等

###5. SQL語句的種類

  1. 數據定義語句(DDL:Data Definition Language) 包括create和drop等操做 在數據庫中建立新表或刪除表(create table或 drop table)
  2. 數據操做語句(DML:Data Manipulation Language) 包括insert、update、delete等操做 上面的3種操做分別用於添加、修改、刪除表中的數據
  3. 數據查詢語句(DQL:Data Query Language) 能夠用於查詢得到表中的數據 關鍵字select是DQL(也是全部SQL)用得最多的操做 其餘DQL經常使用的關鍵字有where,order by,group by和having

##3、數據庫操做 ###表操做 ####1. 建立表 格式:

create table 表名 (字段名1 字段類型1, 字段名2 字段類型2, …) ;
create table if not exists 表名 (字段名1 字段類型1, 字段名2 字段類型2, …) ;
示例:
create table t_student (id integer, name text, age inetger, score real) ;
複製代碼

字段類型

integer : 整型值 real : 浮點值 text : 文本字符串 blob : 二進制數據(好比文件)

其實是無類型的 就算聲明爲integer類型,仍是能存儲字符串文本(主鍵除外) 建表時聲明啥類型或者不聲明類型均可以,也就意味着創表語句能夠這麼寫: create table t_student(name, age);

爲了保持良好的編程規範、方便程序員之間的交流,編寫建表語句的時候最好加上每一個字段的具體類型

####2. 刪除表 格式:

drop table 表名 ;
drop table if exists 表名 ;

示例:
drop table t_student ;
複製代碼

###數據操做 ####1. 插入數據

格式:

insert into 表名 (字段1, 字段2, …) values (字段1的值, 字段2的值, …) ;

示例
insert into t_student (name, age) values (‘mj’, 10) ;

//注意!
數據庫中的字符串內容應該用一對單引號 ' 包含 複製代碼

####2. 更新數據 格式:

update 表名 set 字段1 = 字段1的值, 字段2 = 字段2的值, … ; 

示例
update t_student set name = ‘jack’, age = 20 ; 

//注意!
上面的示例會將t_student表中全部記錄的name都改成jack,age都改成20
複製代碼

####3. 刪除數據 格式:

delete from 表名 ;

示例
delete from t_student ;

注意
上面的示例會將t_student表中全部記錄都刪掉
複製代碼

####4. 條件語句 若是隻想刪除數據庫中國某些指定的記錄,那麼就必須在DML語句後加上一些條件 條件語句的格式:

where 字段 = 某個值 ;   // 不能用兩個 =
where 字段 is 某個值 ;   // is 至關於 = 
where 字段 != 某個值 ; 
where 字段 is not 某個值 ;   // is not 至關於 != 
where 字段 > 某個值 ; 
where 字段1 = 某個值 and 字段2 > 某個值 ;  // and至關於C語言中的 &&
where 字段1 = 某個值 or 字段2 = 某個值 ;  //  or 至關於C語言中的 ||

示例:
將t_student表中年齡大於10 而且 姓名不等於jack的記錄,年齡都改成 5
update t_student set age = 5 where age > 10 and name != ‘jack’ ;

刪除t_student表中年齡小於等於10 或者 年齡大於30的記錄
delete from t_student where age <= 10 or age > 30 ;

猜猜下面語句的做用
update t_student set score = age where name = ‘jack’ ;
將t_student表中名字等於jack的記錄,score字段的值 都改成 age字段的值
複製代碼

####5. 查詢數據 格式:

select 字段1, 字段2, … from 表名 ;
select * from 表名;   //  查詢全部的字段

示例:
select name, age from t_student ;
select * from t_student ;
select * from t_student where age > 10 ;  //  條件查詢
複製代碼

####6. 起別名(瞭解) 格式:(字段和表均可以起別名)

select 字段1 別名 , 字段2 別名 , … from 表名 別名 ; 
select 字段1 別名, 字段2 as 別名, … from 表名 as 別名 ;
select 別名.字段1, 別名.字段2, … from 表名 別名 ;
示例
select name myname, age myage from t_student ;
給name起個叫作myname的別名,給age起個叫作myage的別名
select s.name, s.age from t_student s ;
給t_student表起個別名叫作s,利用s來引用表中的字段
複製代碼

####7. 計算記錄的數量 格式:

select count (字段) from 表名 ;
select count ( * ) from 表名 ;

示例
select count (age) from t_student ;
select count ( * ) from t_student where score >= 60;
複製代碼

####8. 排序 按照某個字段的值進行排序搜索

select * from t_student order by 字段 ;
Example: select * from t_student order by age ;
//默認是按照升序排序(由小到大),也能夠變爲降序(由大到小)

//指定升降序
select * from t_student order by age desc ;  //降序
select * from t_student order by age asc ;   // 升序(默認)

//也能夠用多個字段進行排序
//以下,先按照年齡排序(升序),年齡相等就按照身高排序(降序)
select * from t_student order by age asc, height desc ;

複製代碼

####9. 精確控制查詢結果 使用limit能夠精確地控制查詢結果的數量,好比每次只查詢10條數據 格式:

select * from 表名 limit 數值1, 數值2 ;

示例:
select * from t_student limit 4, 8 ;
//能夠理解爲:跳過最前面4條語句,而後取8條記錄
複製代碼

limit經常使用來作分頁查詢

好比每頁固定顯示5條數據,那麼應該這樣取數據
第1頁:limit 0, 5
第2頁:limit 5, 5
第3頁:limit 10, 5
…
第n頁:limit 5*(n-1), 5

//注:!省略了limit的第一個參數
select * from t_student limit 7 ;
至關於select * from t_student limit 0, 7 ;
表示取最前面的7條記錄
複製代碼

##約束 ###1. 簡單約束 建表時能夠給特定的字段設置一些約束條件,常見的約束有

not null :規定字段的值不能爲null
unique :規定字段的值必須惟一
default :指定字段的默認值
//建議:儘可能給字段設定嚴格的約束,以保證數據的規範性

示例
create table t_student (id integer, name text not null unique, age integer not null default 1) ;
//name字段不能爲null,而且惟一
//age字段不能爲null,而且默認爲1
複製代碼

###2. 主鍵約束

若是t_student表中就name和age兩個字段,並且有些記錄的name和age字段的值都同樣時,那麼就無法區分這些數據,形成數據庫的記錄不惟一,這樣就不方便管理數據

良好的數據庫編程規範應該要保證每條記錄的惟一性,爲此,增長了主鍵約束。也就是說,每張表都必須有一個主鍵,用來標識記錄的惟一性

####1. 什麼是主鍵 主鍵(Primary Key,簡稱PK)用來惟一地標識某一條記錄 例如:t_student能夠增長一個id字段做爲主鍵,至關於人的身份證 主鍵能夠是一個字段或多個字段

####2. 主鍵的特色

主鍵應當是對用戶沒有意義的
永遠也不要更新主鍵
主鍵不該包含動態變化的數據
主鍵應當由計算機自動生成
複製代碼

####4. 主鍵的聲明 在創表的時候用primary key聲明一個主鍵

create table t_student (id integer primary key, name text, age integer) ;
integer類型的id做爲t_student表的主鍵
複製代碼

主鍵字段 只要聲明爲primary key,就說明是一個主鍵字段 主鍵字段默認就包含了not null 和 unique 兩個約束

若是想要讓主鍵自動增加(必須是integer類型),應該增長autoincrement

create table t_student (id integer primary key autoincrement, name text, age integer) ;
複製代碼

###3. 外鍵約束

利用外鍵約束能夠用來創建表與表之間的聯繫 外鍵的通常狀況是:一張表的某個字段,引用着另外一張表的主鍵字段

####1. 新建一個外鍵

create table t_student (id integer primary key autoincrement, name text, age integer, class_id integer, constraint fk_t_student_class_id_t_class_id foreign key (class_id)  references t_class (id);
複製代碼

t_student表中有一個叫作fk_t_student_class_id_t_class_id的外鍵 這個外鍵的做用是用t_student表中的class_id字段引用t_class表的id字段 ####2. 錶鏈接查詢 什麼是錶鏈接查詢? 須要聯合多張表才能查到想要的數據

錶鏈接的類型 內鏈接:inner join 或者 join (顯示的是左右表都有完整字段值的記錄) 左外鏈接:left outer join (保證左表數據的完整性)

示例
查詢0316iOS班的全部學生
select s.name,s.age from t_student s, t_class c where s.class_id = c.id and c.name = ‘0316iOS’;
複製代碼

##3、iOS基於C語言的數據庫操做 ###1.首先建立並打開數據庫

// 打開數據庫
- (void)openDatabase:(NSString *)SQLiteName {
    //1. 獲取數據庫存儲路徑
    NSString *dbName = [SQLiteName documentDir];
    //2. 打開數據庫
    // 若是數據庫不存在,怎新建並打開一個數據庫,不然直接打開
    if (sqlite3_open(dbName.UTF8String, &_db) != SQLITE_OK) {
        NSLog(@"建立/打開數據庫失敗。");
    }

    //3. 建立表
    if ([self createTable]) {
        NSLog(@"建立表成功");
    } else {
        NSLog(@"建立表失敗");
    }
}

/**
 *  建立數據表
 */
- (BOOL)createTable {
    NSString *sql = @"CREATE TABLE IF NOT EXISTS t_person (id integer PRIMARY KEY AUTOINCREMENT,name text,age integer)";

    return [self execSql:sql];
}
複製代碼

###2. 增刪改 sqlite3_exec() 方法能夠執行任何SQL語句,好比創表、更新、插入和刪除操做。可是通常不用它執行查詢語句,由於它不會返回查詢到的數據。咱們這裏封裝一個方法。

/**
 *  執行除查詢之外的sql語句
 */
- (BOOL)execSql:(NSString *)sql {
    if (sqlite3_exec(_db, sql.UTF8String, nil, nil, nil) != SQLITE_OK) {
        return NO;
    }
    return YES;
}
複製代碼

###3.查詢 由於查詢語句須要返回查詢結果並提供給UI顯示,因此是最麻煩的 查詢用到的主要有如下三個方法:

sqlite3_prepare_v2() : 準備數據庫,並檢查SQL語句是否合法 sqlite3_step() :一條一條獲取數據,直到沒有記錄 sqlite3_coloum_xxx() : 獲取對應類型的內容,從0開始。根據實際查詢字段的屬性,使用sqlite3_column_字段類型 取得對應的內容便可。

查詢完要釋放資源:

sqlite3_finalize() : 釋放stmt

*
 *  返回指定sql查詢語句運行的結果集
 *
 *  @param sql sql
 *
 *  @return 結果集
 */
- (NSArray *)execRecordSql:(NSString *)sql {
    // 1. 評估準備SQL語法是否正確
    sqlite3_stmt *stmt = NULL;

    /**
     *  準備: 理解爲預編譯SQL語句, 檢測裏面是否有錯誤等等, 它能夠提供性能
     *
     *  @param db   已經開打的數據庫對象
     *  @param cSQL 須要執行的SQL語句
     *  @param -1   須要執行的SQL語句的長度, 傳入-1系統自動計算
     *  @param stmt 句柄,靠這個獲取查詢結果,當成緩衝區就行了
     *  @param NULL  通常傳NULL
     *
     *  @return
     */
    if (sqlite3_prepare_v2(_db, sql.UTF8String, -1, &stmt, NULL) != SQLITE_OK) {
        NSLog(@"準備數據失敗");
    }
    NSMutableArray *records = [NSMutableArray array];
    // 2.查詢數據
    // sqlite3_step表明取出一條數據, 若是取到了數據就會返回SQLITE_ROW
    while (sqlite3_step(stmt) == SQLITE_ROW) {
        // 3. 獲取/顯示查詢結果
        // sqlite3_column_xxx方法的第二個參數與sql語句中的字段順尋一一對應(從0開始)
        // 獲取一條記錄的數據
        NSDictionary *dict = [self recordWithStmt:stmt];
        [records addObject:dict];
    }

    // 4. 釋放句柄
    sqlite3_finalize(stmt);

    // 返回查詢到的數據
    return records;
}

/**
 獲取一條記錄的值

 - parameter stmt: 預編譯好的SQL語句

 - returns: 字典
 */
- (NSDictionary *)recordWithStmt:(sqlite3_stmt *)stmt {
    //1.拿到當前這條數據的全部列
    int count = sqlite3_column_count(stmt);
    // 定義字典存儲查詢到的數據
    NSMutableDictionary *record = [[NSMutableDictionary alloc] init];

    for (int index = 0; index < count; index++) {
        // 2. 拿到每一列的名稱
        NSString *name = [NSString stringWithCString:sqlite3_column_name(stmt, index) encoding:NSUTF8StringEncoding];
        NSLog(@"%@",name);
        // 3.拿到每一列的類型 SQLITE_INTEGER
        int type = sqlite3_column_type(stmt, index);

        switch (type) {
            case SQLITE_INTEGER://整形
                [record setObject:@(sqlite3_column_int64(stmt, index)) forKey:name];
                break;
            case SQLITE_FLOAT:
                [record setObject:@(sqlite3_column_double(stmt, index)) forKey:name];
                break;
            case SQLITE3_TEXT:
                // 文本類型
                [record setObject:[NSString stringWithUTF8String:sqlite3_column_text(stmt, index)] forKey:name];
                break;
            case SQLITE_NULL:
                // 空類型
                [record setObject:[[NSNull alloc]init] forKey:name];
                break;
            default:
                // 二進制類型 SQLITE_BLOB
                // 通常狀況下, 不會往數據庫中存儲二進制數據
                break;
        }
    }

    return record;
複製代碼

##4、FMDB框架的使用 這一部分來自:張興業的CSDN ###1.簡介 FMDB是iOS平臺的SQLite數據庫框架,它以OC的方式封裝了SQLite的Cell語言API。相似的封裝庫還有PlausibleDatabase、sqlitepersistentobjects等。FMDB更加簡單易用而且支持線程安全因此更加流行。

FMDB是一個開源的項目:GitHub鏈接

###2.添加FMDB框架 從GitHub下載完成後解壓,導入fmdb文件夾下的11個文件,以下圖:

Screen Shot 2016-04-30 at 21.52.18.png
使用FMDB也必須加入 libsqlite3.dylib 依賴包。 FMDB同時兼容ARC和非ARC工程,會自動根據工程配置來調整相關的內存管理代碼。 FMDB經常使用類:

FMDatabase : 一個單一的SQLite數據庫,用於執行SQL語句。 FMResultSet :執行查詢一個FMDatabase結果集,這個和Android的Cursor相似。 FMDatabaseQueue :在多個線程來執行查詢和更新時會使用這個類。

###3.建立數據庫 經過指定SQLite數據庫文件路徑來建立FMDatabase對象

FMDatabase *db = [FMDatabase databaseWithPath:path];
if (![db open]) {
NSLog(@"數據庫打開失敗!");
}
複製代碼

一、當數據庫文件不存在時,fmdb會本身建立一個。 二、 若是你傳入的參數是空串:@"",則fmdb會在臨時文件目錄下建立這個數據庫,數據庫斷開鏈接時,數據庫文件被刪除。 三、若是你傳入的參數是 NULL,則它會創建一個在內存中的數據庫,數據庫斷開鏈接時,數據庫文件被刪除

###4.打開和關閉數據庫

[db open] ;
[db close];

複製代碼

###5.增刪改 除了查詢操做,FMDB數據庫操做都執行executeUpdate方法,這個方法返回BOOL型。

####建立表

if ([db open]) {  
        NSString *sqlCreateTable =  [NSString stringWithFormat:@"CREATE TABLE IF NOT EXISTS '%@' ('%@' INTEGER PRIMARY KEY AUTOINCREMENT, '%@' TEXT, '%@' INTEGER, '%@' TEXT)",TABLENAME,ID,NAME,AGE,ADDRESS];  
        BOOL res = [db executeUpdate:sqlCreateTable];  
        if (!res) {  
            NSLog(@"error when creating db table");  
        } else {  
            NSLog(@"success to creating db table");  
        }  
        [db close];  
  
    } 
複製代碼

####添加數據

if ([db open]) {  
       NSString *insertSql1= [NSString stringWithFormat:  
                              @"INSERT INTO '%@' ('%@', '%@', '%@') VALUES ('%@', '%@', '%@')",  
                              TABLENAME, NAME, AGE, ADDRESS, @"張三", @"13", @"濟南"];  
       BOOL res = [db executeUpdate:insertSql1];  
       NSString *insertSql2 = [NSString stringWithFormat:  
                               @"INSERT INTO '%@' ('%@', '%@', '%@') VALUES ('%@', '%@', '%@')",  
                               TABLENAME, NAME, AGE, ADDRESS, @"李四", @"12", @"濟南"];  
       BOOL res2 = [db executeUpdate:insertSql2];  
         
       if (!res) {  
           NSLog(@"error when insert db table");  
       } else {  
           NSLog(@"success to insert db table");  
       }  
       [db close];  
  
   }  
複製代碼

####修改數據

if ([db open]) {  
        NSString *updateSql = [NSString stringWithFormat:  
                               @"UPDATE '%@' SET '%@' = '%@' WHERE '%@' = '%@'",  
                               TABLENAME,   AGE,  @"15" ,AGE,  @"13"];  
        BOOL res = [db executeUpdate:updateSql];  
        if (!res) {  
            NSLog(@"error when update db table");  
        } else {  
            NSLog(@"success to update db table");  
        }  
        [db close];  
  
    }  

複製代碼

####刪除數據

if ([db open]) {  
          
        NSString *deleteSql = [NSString stringWithFormat:  
                               @"delete from %@ where %@ = '%@'",  
                               TABLENAME, NAME, @"張三"];  
        BOOL res = [db executeUpdate:deleteSql];  
          
        if (!res) {  
            NSLog(@"error when delete db table");  
        } else {  
            NSLog(@"success to delete db table");  
        }  
        [db close];  
  
    }  
複製代碼

###6.查詢 查詢操做使用了executeQuery,並涉及到FMResultSet。

if ([db open]) {  
        NSString * sql = [NSString stringWithFormat:  
                          @"SELECT * FROM %@",TABLENAME];  
        FMResultSet * rs = [db executeQuery:sql];  
        while ([rs next]) {  
            int Id = [rs intForColumn:ID];  
            NSString * name = [rs stringForColumn:NAME];  
            NSString * age = [rs stringForColumn:AGE];  
            NSString * address = [rs stringForColumn:ADDRESS];  
            NSLog(@"id = %d, name = %@, age = %@ address = %@", Id, name, age, address);  
        }  
        [db close];  
    }  
複製代碼

FMDB的FMResultSet提供了多個方法來獲取不一樣類型的數據:

//FMResultSet has many methods to retrieve data in an 
//appropriate format:
intForColumn:
longForColumn:
longLongIntForColumn:
boolForColumn:
doubleForColumn:
stringForColumn:
dateForColumn:
dataForColumn:
dataNoCopyForColumn:
UTF8StringForColumnName:
objectForColumnName:
複製代碼

####7.多線程操做數據庫

若是應用中使用了多線程操做數據庫,那麼就須要使用FMDatabaseQueue來保證線程安全了。 應用中不可在多個線程中共同使用一個FMDatabase對象操做數據庫,這樣會引發數據庫數據混亂。 爲了多線程操做數據庫安全,FMDB使用了FMDatabaseQueue,使用FMDatabaseQueue很簡單,首先用一個數據庫文件地址來初使化FMDatabaseQueue,而後就能夠將一個閉包(block)傳入inDatabase方法中。 在閉包中操做數據庫,而不直接參與FMDatabase的管理。

FMDatabaseQueue * queue = [FMDatabaseQueue databaseQueueWithPath:database_path];  
   dispatch_queue_t q1 = dispatch_queue_create("queue1", NULL);  
   dispatch_queue_t q2 = dispatch_queue_create("queue2", NULL);  
     
   dispatch_async(q1, ^{  
       for (int i = 0; i < 50; ++i) {  
           [queue inDatabase:^(FMDatabase *db2) {  
                 
               NSString *insertSql1= [NSString stringWithFormat:  
                                      @"INSERT INTO '%@' ('%@', '%@', '%@') VALUES (?, ?, ?)",  
                                      TABLENAME, NAME, AGE, ADDRESS];  
                 
               NSString * name = [NSString stringWithFormat:@"jack %d", i];  
               NSString * age = [NSString stringWithFormat:@"%d", 10+i];  
                 
                 
               BOOL res = [db2 executeUpdate:insertSql1, name, age,@"濟南"];  
               if (!res) {  
                   NSLog(@"error to inster data: %@", name);  
               } else {  
                   NSLog(@"succ to inster data: %@", name);  
               }  
           }];  
       }  
   });  
     
   dispatch_async(q2, ^{  
       for (int i = 0; i < 50; ++i) {  
           [queue inDatabase:^(FMDatabase *db2) {  
               NSString *insertSql2= [NSString stringWithFormat:  
                                      @"INSERT INTO '%@' ('%@', '%@', '%@') VALUES (?, ?, ?)",  
                                      TABLENAME, NAME, AGE, ADDRESS];  
                 
               NSString * name = [NSString stringWithFormat:@"lilei %d", i];  
               NSString * age = [NSString stringWithFormat:@"%d", 10+i];  
                 
               BOOL res = [db2 executeUpdate:insertSql2, name, age,@"北京"];  
               if (!res) {  
                   NSLog(@"error to inster data: %@", name);  
               } else {  
                   NSLog(@"succ to inster data: %@", name);  
               }  
           }];  
       }  
   });  

複製代碼

目前FMDB版本爲v2.6.2。若有須要瞭解swift FMDB的使用或者更深刻了解這個框架請上 GitHub鏈接

好了數據庫就到這裏,基本夠用了。

碼字不易,點個喜歡吧。

相關文章
相關標籤/搜索