13 - SQLite 、 FMDB

1 - 什么是SQlite

  • SQLite是一款輕型的嵌入式數(shù)據(jù)庫(kù)
  • SQLite數(shù)據(jù)庫(kù):可以存儲(chǔ)大批量的數(shù)據(jù)
  • 它占用資源非常的低,在嵌入式設(shè)備中,可能只需要幾百K的內(nèi)存就夠了
  • 它的處理速度比Mysql、PostgreSQL這兩款著名的數(shù)據(jù)庫(kù)都還快
  • 數(shù)據(jù)庫(kù)(Database)是按照數(shù)據(jù)結(jié)構(gòu)來組織、存儲(chǔ)和管理數(shù)據(jù)的倉(cāng)庫(kù).

1.1 - 數(shù)據(jù)庫(kù)可以分為2大種類:

  • 關(guān)系型數(shù)據(jù)庫(kù)(主流)
  • 對(duì)象型數(shù)據(jù)庫(kù)
  • 常用關(guān)系型數(shù)據(jù)庫(kù)
    • PC端:Oracle、MySQL、SQL Server、Access、DB2、Sybase
    • 嵌入式\移動(dòng)客戶端:SQLite

2 - 如何存儲(chǔ)數(shù)據(jù)

  • 數(shù)據(jù)庫(kù)的存儲(chǔ)結(jié)構(gòu)和excel很像,以表(table)為單位來存儲(chǔ)數(shù)據(jù)
2.1 - 數(shù)據(jù)庫(kù)存儲(chǔ)數(shù)據(jù)的步驟
  • 新建一個(gè)數(shù)據(jù)庫(kù)文件
  • 新建一張表(table)
  • 添加多個(gè)字段(column,列,屬性)
  • 添加多行記錄(row,每行存放多個(gè)字段對(duì)應(yīng)的值)

3 - SQL語(yǔ)句 (DDL DML DQL)

演示圖片所用的軟件為綠色版的Navicat.

3.1 - SQL語(yǔ)句的種類

  • 3.1.1 - 數(shù)據(jù)定義語(yǔ)句(DDL:Data Definition Language)
  • 包括create(創(chuàng)表)drop(刪表)等操作
  • 在數(shù)據(jù)庫(kù)中創(chuàng)建新表或刪除表(create tabledrop table
  • 3.1.2 - 數(shù)據(jù)操作語(yǔ)句(DML:Data Manipulation Language)
  • 包括insertupdatedelete等操作
  • 上面的3種操作分別用于添加、修改、刪除表中的數(shù)據(jù)
  • 3.1.3 - 數(shù)據(jù)查詢語(yǔ)句(DQL:Data Query Language)
  • 可以用于查詢獲得表中的數(shù)據(jù)
  • 關(guān)鍵字select是DQL(也是所有SQL)用得最多的操作
  • 其他DQL常用的關(guān)鍵字有whereorder bygroup byhaving

3.1.1 - DDL(數(shù)據(jù)定義語(yǔ)句)

創(chuàng)表格式

  • 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) ;

字段類型

  • SQLite將數(shù)據(jù)劃分為以下幾種存儲(chǔ)類型:

  • integer : 整型值

  • real : 浮點(diǎn)值

  • text : 文本字符串

  • blob : 二進(jìn)制數(shù)據(jù)(比如文件)

  • 實(shí)際上SQLite是無類型的

  • 就算聲明為integer類型,還是能存儲(chǔ)字符串文本(主鍵除外)

  • 建表時(shí)聲明啥類型或者不聲明類型都可以,也就意味著創(chuàng)表語(yǔ)句可以這么寫:
    create table t_student(name, age);

  • 為了保持良好的編程規(guī)范、方便程序員之間的交流,編寫建表語(yǔ)句的時(shí)候最好加上每個(gè)字段的具體類型



  • 由于以上創(chuàng)表后是沒有主鍵和自生長(zhǎng)的 所以創(chuàng)建表最好的方式是:

  • 在創(chuàng)表的時(shí)候附帶主鍵和自生長(zhǎng)



刪表格式

  • drop table 表名 ;
  • drop table if exists 表名 ;
drop table t_student ;


3.1.2 - DML(數(shù)據(jù)操作語(yǔ)句)

  • 插入數(shù)據(jù)(insert
  • insert into 表名 (字段1, 字段2, …) values (字段1的值, 字段2的值, …) ;

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


  • 注意
  • 數(shù)據(jù)庫(kù)中的字符串內(nèi)容應(yīng)該用單引號(hào) ’ 括住;

  • 更新數(shù)據(jù)(update)
update 表名 set 字段1 = 字段1的值, 字段2 = 字段2的值, … ;
update t_student set name = ‘jack’, age = 20 ;

  • 注意
  • 上面的示例會(huì)將t_student表中所有記錄的name都改為jack,age都改為20

  • 刪除數(shù)據(jù)(delete)
delete from 表名 ;
delete from t_student ;

  • 注意
  • 上面的示例會(huì)將t_student表中所有記錄都刪掉

3.1.3 - DQL (條件語(yǔ)句)

  • 如果只想更新或者刪除某些固定的記錄,那就必須在DML語(yǔ)句后加上一些條件
  • 條件語(yǔ)句的常見格式
where 字段 = 某個(gè)值 ;   // 不能用兩個(gè) =
where 字段 is 某個(gè)值 ;   // is 相當(dāng)于 =
where 字段 != 某個(gè)值 ;
where 字段 is not 某個(gè)值 ;   // is not 相當(dāng)于 !=
where 字段 > 某個(gè)值 ;
where 字段1 = 某個(gè)值 and 字段2 > 某個(gè)值 ;  // and相當(dāng)于C語(yǔ)言中的 &&
where 字段1 = 某個(gè)值 or 字段2 = 某個(gè)值 ;  //  or 相當(dāng)于C語(yǔ)言中的 ||
// 將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 ;

// 將t_student表中名字等于jack的記錄,score字段的值 都改為 age字段的值
update t_student set score = age where name = ‘jack’ ;

  • 查詢數(shù)據(jù)
select 字段1, 字段2, … from 表名 ;
select * from 表名;   //  查詢所有的字段
select name, age from t_student ;
select * from t_student ;
select * from t_student where age > 10 ;  //  條件查詢

  • 起別名(字段和表都可以起別名)
select 字段1 別名 , 字段2 別名 , … from 表名 別名 ;
select 字段1 別名, 字段2 as 別名, … from 表名 as 別名 ;
select 別名.字段1, 別名.字段2, … from 表名 別名 ;
// 給name起個(gè)叫做myname的別名,給age起個(gè)叫做myage的別名
select name myname, age myage from t_student ;

// 給t_student表起個(gè)別名叫做s,利用s來引用表中的字段
select s.name, s.age from t_student s ;

  • 計(jì)算記錄的數(shù)量
select count (字段) from 表名 ;
select count ( * ) from 表名 ;
select count (age) from t_student ;
select count ( * ) from t_student where score >= 60

  • 排序
  • 查詢出來的結(jié)果可以用order by進(jìn)行排序
select * from t_student order by 字段 ;
select * from t_student order by age ;
  • 默認(rèn)是按照升序排序(由小到大),也可以變?yōu)榻敌颍ㄓ纱蟮叫。?/li>
select * from t_student order by age desc ;  //降序
select * from t_student order by age asc ;   // 升序(默認(rèn))
  • 也可以用多個(gè)字段進(jìn)行排序
// 先按照年齡排序(升序),年齡相等就按照身高排序(降序)
select * from t_student order by age asc, height desc ;

  • 分頁(yè) - limit常用來做分頁(yè)查詢,比如每頁(yè)固定顯示5條數(shù)據(jù),那么應(yīng)該這樣取數(shù)據(jù)
第1頁(yè):limit 0, 5
第2頁(yè):limit 5, 5
第3頁(yè):limit 10, 5
第n頁(yè):limit 5*(n-1), 5
  • 猜猜下面語(yǔ)句的作用
select * from t_student limit 7 ;
相當(dāng)于select * from t_student limit 0, 7 ;
表示取最前面的7條記錄

主鍵和約束

  • 簡(jiǎn)單約束
  • 建表時(shí)可以給特定的字段設(shè)置一些約束條件,常見的約束有
  • not null :規(guī)定字段的值不能為null
  • unique :規(guī)定字段的值必須唯一
  • default :指定字段的默認(rèn)值
    (建議:盡量給字段設(shè)定嚴(yán)格的約束,以保證數(shù)據(jù)的規(guī)范性)
create table t_student (id integer, name text not null unique, age integer not null default 1) ;
name字段不能為null,并且唯一
age字段不能為null,并且默認(rèn)為1
主鍵約束
  • 如果t_student表中就name和age兩個(gè)字段,而且有些記錄的name和age字段的值都一樣時(shí),那么就沒法區(qū)分這些數(shù)據(jù),造成數(shù)據(jù)庫(kù)的記錄不唯一,這樣就不方便管理數(shù)據(jù)
  • 良好的數(shù)據(jù)庫(kù)編程規(guī)范應(yīng)該要保證每條記錄的唯一性,為此,增加了主鍵約束
  • 也就是說,每張表都必須有一個(gè)主鍵,用來標(biāo)識(shí)記錄的唯一性
什么是主鍵
  • 主鍵(Primary Key,簡(jiǎn)稱PK)用來唯一地標(biāo)識(shí)某一條記錄
  • 例如t_student可以增加一個(gè)id字段作為主鍵,相當(dāng)于人的身份證
  • 主鍵可以是一個(gè)字段或多個(gè)字段
主鍵的設(shè)計(jì)原則
  • 主鍵應(yīng)當(dāng)是對(duì)用戶沒有意義的
  • 永遠(yuǎn)也不要更新主鍵
  • 主鍵不應(yīng)包含動(dòng)態(tài)變化的數(shù)據(jù)
  • 主鍵應(yīng)當(dāng)由計(jì)算機(jī)自動(dòng)生成
主鍵的聲明
  • 在創(chuàng)表的時(shí)候用primary key聲明一個(gè)主鍵
    create table t_student (id integer primary key, name text, age integer) ;
    pinteger類型的id作為t_student表的主鍵
主鍵字段
  • 只要聲明為primary key,就說明是一個(gè)主鍵字段

  • 主鍵字段默認(rèn)就包含了not null 和 unique 兩個(gè)約束

  • 如果想要讓主鍵自動(dòng)增長(zhǎng)(必須是integer類型),應(yīng)該增加autoincrement
    create table t_student (id integer primary key autoincrement, name text, age integer) ;

外鍵約束
  • 利用外鍵約束可以用來建立表與表之間的聯(lián)系
  • 外鍵的一般情況是:一張表的某個(gè)字段,引用著另一張表的主鍵字段
新建一個(gè)外鍵
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) (id)) ; references t_class
  • t_student表中有一個(gè)叫做fk_t_student_class_id_t_class_id的外鍵
  • 這個(gè)外鍵的作用是用t_student表中的class_id字段引用t_class表的id字段

表鏈接

什么是表連接查詢

  • 需要聯(lián)合多張表才能查到想要的數(shù)據(jù)

表連接的類型

  • 內(nèi)連接:inner join 或者 join (顯示的是左右表都有完整字段值的記錄)

  • 左外連接:left outer join (保證左表數(shù)據(jù)的完整性)

  • 查詢1020班的所有學(xué)生

select s.name,s.age from t_student s, t_class c where s.class_id = c.id and c.name = ‘1020’;

FMDB

  • FMDB是iOS平臺(tái)的SQLite數(shù)據(jù)庫(kù)框架
  • FMDB以O(shè)C的方式封裝了SQLite的C語(yǔ)言API
  • 使用起來更加面向?qū)ο螅∪チ撕芏嗦闊⑷哂嗟腃語(yǔ)言代碼
  • 對(duì)比蘋果自帶的Core Data框架,更加輕量級(jí)和靈活
  • 提供了多線程安全的數(shù)據(jù)庫(kù)操作方法,有效地防止數(shù)據(jù)混亂

FMDB有三個(gè)主要的核心類

1 - FMDatabase
  • 一個(gè)FMDatabase對(duì)象就代表一個(gè)單獨(dú)的SQLite數(shù)據(jù)庫(kù)
  • 用來執(zhí)行SQL語(yǔ)句
2 - FMResultSet
  • 使用FMDatabase執(zhí)行查詢后的結(jié)果集
3 - FMDatabaseQueue
  • 用于在多線程中執(zhí)行多個(gè)查詢或更新,它是線程安全的

打開數(shù)據(jù)庫(kù)

  • 通過指定SQLite數(shù)據(jù)庫(kù)文件路徑來創(chuàng)建FMDatabase對(duì)象
FMDatabase *db = [FMDatabase databaseWithPath:path];
if (![db open]) {
    NSLog(@"數(shù)據(jù)庫(kù)打開失敗!");
}

文件路徑有三種情況

  • 1 - 具體文件路徑
  • 如果不存在會(huì)自動(dòng)創(chuàng)建
  • 2 - 空字符串@""
  • 會(huì)在臨時(shí)目錄創(chuàng)建一個(gè)空的數(shù)據(jù)庫(kù)
  • 當(dāng)FMDatabase連接關(guān)閉時(shí),數(shù)據(jù)庫(kù)文件也被刪除
  • 3 - nil
  • 會(huì)創(chuàng)建一個(gè)內(nèi)存中臨時(shí)數(shù)據(jù)庫(kù),當(dāng)FMDatabase連接關(guān)閉時(shí),數(shù)據(jù)庫(kù)會(huì)被銷毀

執(zhí)行更新

  • 在FMDB中,除查詢以外的所有操作,都稱為“更新”
  • create、drop、insert、update、delete等

使用executeUpdate:方法執(zhí)行更新

- (BOOL)executeUpdate:(NSString*)sql, ...
- (BOOL)executeUpdateWithFormat:(NSString*)format, ...
- (BOOL)executeUpdate:(NSString*)sql withArgumentsInArray:(NSArray *)arguments

[db executeUpdate:@"UPDATE t_student SET age = ? WHERE name = ?;", @20, @"Jack"];

執(zhí)行查詢

  • 查詢方法
- (FMResultSet *)executeQuery:(NSString*)sql, ...
- (FMResultSet *)executeQueryWithFormat:(NSString*)format, ...
- (FMResultSet *)executeQuery:(NSString *)sql withArgumentsInArray:(NSArray *)arguments
// 查詢數(shù)據(jù)
FMResultSet *rs = [db executeQuery:@"SELECT * FROM t_student"];
// 遍歷結(jié)果集
while ([rs next]) {
    NSString *name = [rs stringForColumn:@"name"];
    int age = [rs intForColumn:@"age"];
    double score = [rs doubleForColumn:@"score"];
}

FMDatabaseQueue

  • FMDatabase這個(gè)類是線程不安全的,如果在多個(gè)線程中同時(shí)使用一個(gè)FMDatabase實(shí)例,會(huì)造成數(shù)據(jù)混亂等問題
  • 為了保證線程安全,F(xiàn)MDB提供方便快捷的FMDatabaseQueue類

FMDatabaseQueue的創(chuàng)建

FMDatabaseQueue *queue = [FMDatabaseQueue databaseQueueWithPath:path];

簡(jiǎn)單使用

[queue inDatabase:^(FMDatabase *db) {
    [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jack"];
    [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Rose"];
    [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jim"];
   
    FMResultSet *rs = [db executeQuery:@"select * from t_student"];
    while ([rs next]) {
        // …
    }
}];

使用事務(wù)

[queue inTransaction:^(FMDatabase *db, BOOL *rollback) {
    [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jack"];
    [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Rose"];
    [db executeUpdate:@"INSERT INTO t_student(name) VALUES (?)", @"Jim"];
   
    FMResultSet *rs = [db executeQuery:@"select * from t_student"];
    while ([rs next]) {
        // …
    }
}];

事務(wù)回滾

*rollback = YES;
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容