文章目錄:
- sqlite3 基礎(chǔ)語(yǔ)句
- sqlite3 API
- sqlite3 線程安全
- FMDB
基礎(chǔ)語(yǔ)句:
- 創(chuàng)建數(shù)據(jù)庫(kù)文件
學(xué)習(xí)sqlite3的基礎(chǔ)在于SQL語(yǔ)句质欲,開(kāi)始前請(qǐng)輸入$ sqlite3 驗(yàn)證你的電腦是否已經(jīng)安裝了sqlite3
首先我們需要?jiǎng)?chuàng)建一個(gè)數(shù)據(jù)庫(kù)文件,打開(kāi)終端驶兜,在合適的目錄下舞丛,輸入:
$ sqlite3 studyDB.db
.databases
- 創(chuàng)建表
以id為主鍵并自動(dòng)增加,創(chuàng)建一個(gè)名為book的表:
create table if not exists book (id integer primary key autoincrement, bookNumber integer, bookName text, authorID integer, pressName text);
輸入如下命令查看數(shù)據(jù)表是否創(chuàng)建成功:
.tables
- insert
有了數(shù)據(jù)表以后,就可以愉快地寫(xiě)SQL語(yǔ)句來(lái)測(cè)試了走贪,先從基礎(chǔ)的增刪改查開(kāi)始:
輸入如下指令,向數(shù)據(jù)表中插入一條記錄:
insert into book (bookNumber, bookName, authorID, pressName) values (1001, '三國(guó)演義', 10, '長(zhǎng)江出版社');
- select
然后做查詢(xún)操作惑芭,看上一條記錄是否插入成功:
select * from book;
然而在終端練習(xí)SQL語(yǔ)句坠狡,看起來(lái)并不那么清晰,所以接下來(lái)我們用一個(gè)可視化工具MesaSQLite來(lái)練習(xí)SQL語(yǔ)句强衡,解壓之后請(qǐng)閱讀Serial.txt擦秽,如圖:
接下來(lái)我們多插入幾條記錄,以便演示操作:
insert into book (bookNumber, bookName, authorID, pressName) values (1002, '水滸傳', 11, '黃河出版社');
insert into book (bookNumber, bookName, authorID, pressName) values (1003, '西游記', 12, '長(zhǎng)沙出版社');
insert into book (bookNumber, bookName, authorID, pressName) values (1004, '紅樓夢(mèng)', 13, '武漢出版社');
insert into book (bookNumber, bookName, authorID, pressName) values (1005, '瑯琊榜', 14, '黃河出版社');
insert into book (bookNumber, bookName, authorID, pressName) values (1006, '偽裝者', 15, '長(zhǎng)江出版社');
insert into book (bookNumber, bookName, authorID, pressName) values (1007, '簡(jiǎn)愛(ài)', 16, '長(zhǎng)江出版社');
insert into book (bookNumber, bookName, authorID, pressName) values (1008, '大主宰', 14, '武漢出版社');
執(zhí)行以上SQL語(yǔ)句以后漩勤,我們的數(shù)據(jù)表中的數(shù)據(jù)應(yīng)該是這樣的:
- where
條件語(yǔ)句where感挥,查詢(xún)bookNumber為1003的記錄:
select * from book where bookNumber = 1003;
- update
修改bookNumber為1002的記錄,然后查詢(xún)所有記錄:
update book set pressName = '清華大學(xué)出版社' where bookNumber = 1002;
select * from book;
- delete
刪除紅樓夢(mèng)越败,然后查詢(xún):
delete from book where bookName = '紅樓夢(mèng)';
select *from book;
- and
邏輯運(yùn)算符and触幼,查詢(xún)pressName為黃河出版社,并且authorID為14的記錄:
select * from book where pressName = '黃河出版社' and authorID = 14;
- or
邏輯運(yùn)算符or究飞,查詢(xún)authorID為14置谦,或者pressName為長(zhǎng)江出版社的記錄:
select * from book where pressName= '長(zhǎng)江出版社' or authorID = 14;
- like
模糊查詢(xún)指令like:
select * from book where pressName like '長(zhǎng)%';
select * from book where authorID like '_4';
- in
查詢(xún)authorID為14或16的記錄:
select * from book where authorID in (14, 16);
- not in
查詢(xún)pressName不為“長(zhǎng)江出版社”的記錄:
select * from book where pressName not in ('長(zhǎng)江出版社');
- between
查詢(xún)authorID在14到20之間的記錄:
select * from book where authorID between 14 and 20;
- count
查詢(xún)pressName為“長(zhǎng)江出版社”的記錄條數(shù):
select count(pressName) from book where pressName = '長(zhǎng)江出版社';
為了方便演示,我們創(chuàng)建另一個(gè)數(shù)據(jù)表author:
create table if not exists author (id integer primary key autoincrement, authorName text, authorID integer, age integer);
執(zhí)行如下SQL語(yǔ)句:
insert into author (authorName, authorID, age) values ('jack', 21, 45);
insert into author (authorName, authorID, age) values ('dave', 10, 33);
insert into author (authorName, authorID, age) values ('rose', 14, 24);
insert into author (authorName, authorID, age) values ('jim', 16, 56);
insert into author (authorName, authorID, age) values ('ivan', 13, 22);
最后我們的author表中的數(shù)據(jù)應(yīng)該是這樣的:
- sum
查詢(xún)所有作者的年齡總和:
select sum(age) from author;
- avg
查詢(xún)作者的平均年齡:
select avg(age) from author;
- max
查詢(xún)最大的作者年齡:
select max(age) from author;
- min
查詢(xún)最小的作者年齡:
select min(age) from author;
- order by
查詢(xún)所有的記錄亿傅,并按年齡升序排列:
select * from author order by age asc;
查詢(xún)所有的記錄媒峡,并按年齡降序排列:
select * from author order by age desc;
- 語(yǔ)句嵌套
查詢(xún)年齡小于平均年齡的記錄:
select * from author where age < (select avg(age) from author);
- 多表聯(lián)合查詢(xún)
查詢(xún)年齡小于平均年齡的作者姓名、圖書(shū)名葵擎、出版社:
select author.authorName, book.bookName, book.pressName from author, book where author.authorID = book.authorID and age< (select avg(age) from author);
sqlite3 API:
要使用sqlite3 API谅阿,需要?導(dǎo)入libsqlite3.tbd,然后#import 就可以使用sqlite了酬滤。
使用的過(guò)程根據(jù)使用的函數(shù)大致分為如下幾個(gè)過(guò)程:
- sqlite3_open()
- sqlite3_prepare()
- sqlite3_step()
- sqlite3_column()
- sqlite3_finalize()
- sqlite3_close()
這幾個(gè)過(guò)程是概念上的說(shuō)法签餐,而不完全是程序運(yùn)行的過(guò)程,如sqlite3_column()表示的是對(duì)查詢(xún)獲得一行里面的數(shù)據(jù)的列的各個(gè)操作統(tǒng)稱(chēng)盯串,實(shí)際上在sqlite中并不存在這個(gè)函數(shù)氯檐。
- sqlite3_open
函數(shù)定義:
SQLITE_API int SQLITE_STDCALL sqlite3_open(
const char *filename, /* Database filename (UTF-8) */
sqlite3 **ppDb /* OUT: SQLite db handle */
);
在操作數(shù)據(jù)庫(kù)之前,首先要打開(kāi)數(shù)據(jù)庫(kù)体捏。這個(gè)函數(shù)打開(kāi)一個(gè)sqlite數(shù)據(jù)庫(kù)文件冠摄,并且返回一個(gè)數(shù)據(jù)庫(kù)連接對(duì)象糯崎。假如這個(gè)要被打開(kāi)的數(shù)據(jù)文件不存在,則一個(gè)同名的數(shù)據(jù)庫(kù)文件將被創(chuàng)建耗拓。如果使用sqlite3_open和sqlite3_open_v2的話拇颅,數(shù)據(jù)庫(kù)將采用UTF-8的編碼方式,sqlite3_open16采用UTF-16的編碼方式乔询。如果sqlite數(shù)據(jù)庫(kù)被成功打開(kāi)(或創(chuàng)建)樟插,將會(huì)返回SQLITE_OK,否則將會(huì)返回錯(cuò)誤碼竿刁。
filename:需要被打開(kāi)的數(shù)據(jù)庫(kù)文件的文件名黄锤,在sqlite3_open和sqlite3_open_v2中這個(gè)參數(shù)采用UTF-8編碼,而在sqlite3_open16中則采用UTF-16編碼食拜。
ppDb:一個(gè)數(shù)據(jù)庫(kù)連接句柄被返回到這個(gè)參數(shù)鸵熟,即使發(fā)生錯(cuò)誤。唯一的異常是如果sqlite不能分配內(nèi)存來(lái)存放sqlite對(duì)象负甸,ppDb將會(huì)被返回一個(gè)NULL值流强。
- sqlite3_prepare
函數(shù)定義:
SQLITE_API int SQLITE_STDCALL sqlite3_prepare(
sqlite3 *db, /* Database handle */
const char *zSql, /* SQL statement, UTF-8 encoded */
int nByte, /* Maximum length of zSql in bytes. */
sqlite3_stmt **ppStmt, /* OUT: Statement handle */
const char **pzTail /* OUT: Pointer to unused portion of zSql */
);
這個(gè)函數(shù)將sql文本轉(zhuǎn)換成一個(gè)準(zhǔn)備語(yǔ)句(prepared statement)對(duì)象,同時(shí)返回語(yǔ)句對(duì)象的句柄呻待。這個(gè)接口需要一個(gè)數(shù)據(jù)庫(kù)連接指針以及一個(gè)要準(zhǔn)備的包含SQL語(yǔ)句的文本打月。它實(shí)際上并不執(zhí)行(evaluate)這個(gè)SQL語(yǔ)句,它僅僅為執(zhí)行準(zhǔn)備這個(gè)sql語(yǔ)句蚕捉。
db:數(shù)據(jù)庫(kù)連接指針奏篙。
zSql:sql語(yǔ)句,使用UTF-8編碼迫淹。
nByte:如果nByte小于0秘通,則函數(shù)取出zSql中從開(kāi)始到第一個(gè)0終止符的內(nèi)容;如果nByte不是負(fù)的敛熬,那么它就是這個(gè)函數(shù)能從zSql中讀取的字節(jié)數(shù)的最大值肺稀。如果nBytes非負(fù),zSql在第一次遇見(jiàn)‘/000/’或‘u000’的時(shí)候終止应民。
pzTail:上面提到zSql在遇見(jiàn)終止符或者是達(dá)到設(shè)定的nByte之后結(jié)束盹靴,假如zSql還有剩余的內(nèi)容,那么這些剩余的內(nèi)容被存放到pZTail中瑞妇,不包括終止符。
ppStmt:能夠使用sqlite3_step()執(zhí)行的編譯好的準(zhǔn)備語(yǔ)句的指針梭冠,如果錯(cuò)誤發(fā)生辕狰,它被置為NULL,如假如輸入的文本不包括sql語(yǔ)句控漠。調(diào)用過(guò)程必須負(fù)責(zé)在編譯好的sql語(yǔ)句完成使用后使用sqlite3_finalize()刪除它蔓倍。
- sqlite3_step
函數(shù)定義:
SQLITE_API int SQLITE_STDCALL sqlite3_step(sqlite3_stmt*);
這個(gè)過(guò)程用于執(zhí)行有前面sqlite3_prepare創(chuàng)建的準(zhǔn)備語(yǔ)句悬钳。這個(gè)語(yǔ)句執(zhí)行到結(jié)果的第一行可用的位置。繼續(xù)前進(jìn)到結(jié)果的第二行的話偶翅,只需再次調(diào)用sqlite3_setp()默勾。繼續(xù)調(diào)用sqlite3_setp()直到這個(gè)語(yǔ)句完成,那些不返回結(jié)果的語(yǔ)句(如:INSERT聚谁,UPDATE母剥,或DELETE),sqlite3_step()只執(zhí)行一次就返回形导。
- sqlite3_column
函數(shù)定義:
SQLITE_API const void* SQLITE_STDCALL sqlite3_column_blob(sqlite3_stmt*,intiCol);
SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes(sqlite3_stmt*,intiCol);
SQLITE_API int SQLITE_STDCALL sqlite3_column_bytes16(sqlite3_stmt*,intiCol);
SQLITE_API double SQLITE_STDCALL sqlite3_column_double(sqlite3_stmt*,intiCol);
SQLITE_API int SQLITE_STDCALL sqlite3_column_int(sqlite3_stmt*,intiCol);
SQLITE_API sqlite3_int64 SQLITE_STDCALL sqlite3_column_int64(sqlite3_stmt*,intiCol);
SQLITE_API const unsigned char* SQLITE_STDCALL sqlite3_column_text(sqlite3_stmt*,intiCol);
SQLITE_API const void* SQLITE_STDCALL sqlite3_column_text16(sqlite3_stmt*,intiCol);
SQLITE_API int SQLITE_STDCALL sqlite3_column_type(sqlite3_stmt*,intiCol);
SQLITE_API sqlite3_value* SQLITE_STDCALL sqlite3_column_value(sqlite3_stmt*,intiCol);
這個(gè)過(guò)程從執(zhí)行sqlite3_step()執(zhí)行一個(gè)準(zhǔn)備語(yǔ)句得到的結(jié)果集的當(dāng)前行中返回一個(gè)列环疼。每次sqlite3_step得到一個(gè)結(jié)果集的列停下后,這個(gè)過(guò)程就可以被多次調(diào)用去查詢(xún)這個(gè)行的各列的值朵耕。對(duì)列操作是有多個(gè)函數(shù)炫隶,均以sqlite3_column為前綴。
第一個(gè)參數(shù)為從sqlite3_prepare返回來(lái)的prepared statement對(duì)象的指針阎曹。
第二參數(shù)指定這一行中的想要被返回的列的索引伪阶。最左邊的一列的索引號(hào)是0,行的列數(shù)可以使用sqlite3_colum_count()獲得处嫌。
- sqlite3_finalize
函數(shù)定義:
SQLITE_API int SQLITE_STDCALL sqlite3_finalize(sqlite3_stmt *pStmt);
這個(gè)過(guò)程銷(xiāo)毀前面被sqlite3_prepare創(chuàng)建的準(zhǔn)備語(yǔ)句栅贴,每個(gè)準(zhǔn)備語(yǔ)句都必須使用這個(gè)函數(shù)去銷(xiāo)毀以防止內(nèi)存泄露。
- sqlite3_exec
函數(shù)定義:
SQLITE_API int SQLITE_STDCALL sqlite3_exec(
sqlite3*, /* An open database */
const char*sql, /* SQL to be evaluated */
int(*callback)(void*,int,char**,char**), /* Callback function */
void*, /* 1st argument to callback */
char **errmsg /* Error msg written here */
);
sqlite3_step 和 sqlite3_exec 都可以用于執(zhí)行SQL語(yǔ)句锰霜,他們的區(qū)別在于后者是sqlite3_prepare()筹误、sqlite3_step() 和 sqlite3_finalize() 的封裝,能讓程序多次執(zhí)行sql語(yǔ)句而不要寫(xiě)許多重復(fù)的代碼癣缅,然后提供一個(gè)回調(diào)函數(shù)進(jìn)行結(jié)果的處理厨剪。
第1個(gè)參數(shù):數(shù)據(jù)庫(kù)連接指針。
第2個(gè)參數(shù):是一條sql語(yǔ)句友存。
第3個(gè)參數(shù):是一個(gè)函數(shù)指針祷膳,當(dāng)這條語(yǔ)句執(zhí)行之后,sqlite3會(huì)去調(diào)用你提供的這個(gè)函數(shù)屡立。
第4個(gè)參數(shù):是你所提供的指針直晨,你可以傳遞任何一個(gè)指針參數(shù)到這里,這個(gè)參數(shù)最終會(huì)傳到回調(diào)函數(shù)里面膨俐,如果不需要傳遞指針給回調(diào)函數(shù)勇皇,可以填NULL。等下我們?cè)倏椿卣{(diào)函數(shù)的寫(xiě)法焚刺,以及這個(gè)參數(shù)的使用敛摘。
第5個(gè)參數(shù):是錯(cuò)誤信息。注意是指針的指針乳愉。sqlite3里面有很多固定的錯(cuò)誤信息兄淫。執(zhí)行sqlite3_exec 之后屯远,執(zhí)行失敗時(shí)可以查閱這個(gè)指針。
- sqlite3_close
函數(shù)定義
SQLITE_API int SQLITE_STDCALL sqlite3_close(sqlite3*);
這個(gè)過(guò)程用于關(guān)閉數(shù)據(jù)庫(kù)
代碼演示:
NSString *select_stmt = [NSString stringWithFormat:
@"select * from people where name = \"%@\"",
self.name.text
];
sqlite3_stmt *stmt;
sqlite3_prepare(db,select_stmt.UTF8String,-1,&stmt,NULL);
while(sqlite3_step(stmt) ==SQLITE_ROW) {
NSString*name = [NSString stringWithUTF8String:(const char*)sqlite3_column_text(stmt,1)];
NSString*address = [NSString stringWithUTF8String:(const char*)sqlite3_column_text(stmt,2)];
NSString*age = [NSString stringWithUTF8String:(const char*)sqlite3_column_text(stmt,3)];
self.name.text= name;
self.address.text= address;
self.age.text= age;
}
sqlite3_finalize(stmt);
sqlite3_close(db);
完整的SQLite3 API 代碼戳這里
線程安全
在iOS開(kāi)發(fā)時(shí)捕虽,為了不阻塞主線程慨丐,數(shù)據(jù)庫(kù)訪問(wèn)必須移到子線程中。從3.3.1版本開(kāi)始泄私,SQLite就是線程安全的了房揭。而iOS的SQLite版本沒(méi)有低于這個(gè)版本的:
3.4.0 - iPhone OS 2.2.1
3.6.12 - iPhone OS 3.0 / 3.1
3.6.22 - iPhone OS 4.0
3.6.23.2 - iPhone OS 4.1 / 4.2
3.7.2 - iPhone OS 4.3
3.7.7 - iPhone OS 5.0
SQLite支持3種線程模式:
單線程:禁用所有的mutex鎖,并發(fā)使用時(shí)會(huì)出錯(cuò)挖滤。當(dāng)SQLite編譯時(shí)加了SQLITE_THREADSAFE=0參數(shù)崩溪,或者在初始化SQLite前調(diào)用sqlite3_config(SQLITE_CONFIG_SINGLETHREAD)時(shí)啟用。
多線程:只要一個(gè)數(shù)據(jù)庫(kù)連接不被多個(gè)線程同時(shí)使用就是安全的斩松。源碼中是啟用bCoreMutex伶唯,禁用bFullMutex。實(shí)際上就是禁用數(shù)據(jù)庫(kù)連接和prepared statement(準(zhǔn)備好的語(yǔ)句)上的鎖惧盹,因此不能在多個(gè)線程中并發(fā)使用同一個(gè)數(shù)據(jù)庫(kù)連接或prepared statement乳幸。當(dāng)SQLite編譯時(shí)加了SQLITE_THREADSAFE=2參數(shù)時(shí)默認(rèn)啟用。若SQLITE_THREADSAFE不為0钧椰,可以在初始化SQLite前粹断,調(diào)用sqlite3_config(SQLITE_CONFIG_MULTITHREAD)啟用;或者在創(chuàng)建數(shù)據(jù)庫(kù)連接時(shí)嫡霞,設(shè)置SQLITE_OPEN_NOMUTEX flag瓶埋。
串行:?jiǎn)⒂盟械逆i,包括bCoreMutex和bFullMutex诊沪。因?yàn)閿?shù)據(jù)庫(kù)連接和prepared statement都已加鎖养筒,所以多線程使用這些對(duì)象時(shí)沒(méi)法并發(fā),也就變成串行了端姚。當(dāng)SQLite編譯時(shí)加了SQLITE_THREADSAFE=1參數(shù)時(shí)默認(rèn)啟用晕粪。若SQLITE_THREADSAFE不為0,可以在初始化SQLite前渐裸,調(diào)用sqlite3_config(SQLITE_CONFIG_SERIALIZED)啟用巫湘;或者在創(chuàng)建數(shù)據(jù)庫(kù)連接時(shí),設(shè)置SQLITE_OPEN_FULLMUTEX flag昏鹃。
而這里所說(shuō)的初始化是指調(diào)用sqlite3_initialize()函數(shù)尚氛,這個(gè)函數(shù)在調(diào)用sqlite3_open()時(shí)會(huì)自動(dòng)調(diào)用,且只有第一次調(diào)用是有效的洞渤。
另一個(gè)要說(shuō)明的是prepared statement怠褐,它是由數(shù)據(jù)庫(kù)連接(的pager)來(lái)管理的,使用它也可看成使用這個(gè)數(shù)據(jù)庫(kù)連接您宪。因此在多線程模式下奈懒,并發(fā)對(duì)同一個(gè)數(shù)據(jù)庫(kù)連接調(diào)用sqlite3_prepare_v2()來(lái)創(chuàng)建prepared statement,或者對(duì)同一個(gè)數(shù)據(jù)庫(kù)連接的任何prepared statement并發(fā)調(diào)用sqlite3_bind_*()和sqlite3_step()等函數(shù)都會(huì)出錯(cuò)(在iOS上宪巨,該線程會(huì)出現(xiàn)EXC_BAD_ACCESS而中止)磷杏。這種錯(cuò)誤無(wú)關(guān)讀寫(xiě),就是只讀也會(huì)出錯(cuò)捏卓。安全使用規(guī)則是:沒(méi)有事務(wù)正在等待執(zhí)行的話极祸,所有prepared statement都要被finalized。
調(diào)用sqlite3_threadsafe()可以獲得編譯期的SQLITE_THREADSAFE參數(shù)怠晴。標(biāo)準(zhǔn)發(fā)行版是1遥金,也就是串行模式;而iOS上是2蒜田,也就是多線程模式稿械。
一段存在線程安全隱患的代碼:
iOS上的SQLite默認(rèn)是多線程模式,多個(gè)線程同時(shí)使用同一個(gè)數(shù)據(jù)庫(kù)連接對(duì)象冲粤,將會(huì)產(chǎn)生異常美莫,解決辦法的一種就是在sqlite3_open前加上sqlite3_config(SQLITE_CONFIG_SERIALIZED)。
完整的線程安全代碼戳這里
MFDB
一個(gè)使用FMDB的代碼示例戳這里
需要說(shuō)明的是 FMDatabaseQueue 解決了線程安全問(wèn)題