標籤(空格分隔): iOS數據庫 數據庫總結 SQLite數據庫ios
ios中的數據存儲方式及其特色git
##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語句的種類
- 數據定義語句(DDL:Data Definition Language) 包括create和drop等操做 在數據庫中建立新表或刪除表(create table或 drop table)
- 數據操做語句(DML:Data Manipulation Language) 包括insert、update、delete等操做 上面的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個文件,以下圖:
使用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鏈接
好了數據庫就到這裏,基本夠用了。
碼字不易,點個喜歡吧。