Sqlite3詳細解讀

Sqlite3詳細解讀



"代碼下載:SQLite3_2013_0402詳細版.zip" http://vdisk.weibo.com/s/Gb9Qi


***數據庫***

嚴格地說,數據庫是按照數據結構來組織、存儲和管理數據的倉庫。在經濟管理的日常工作中,常常需要把某些相關的數據放進這樣的倉庫,並根據管理的需要進行相應的處理。例如,企業或事業單位的人事部門常常要把本單位職工的基本情況(職工號、姓名、年齡、性別、籍貫、工資、簡歷等)存放在表中,這張表就可以看成是一個數據庫。有了這個"數據倉庫"我們就可以根據需要隨時查詢某職工的基本情況,也可以查詢工資在某個範圍內的職工人數等等。


這種數據集合具有如下特點:儘可能不重複,以最優方式爲某個特定組織的多種應用服務,其數據結構獨立於使用它的應用程序,對數據的增、刪、改和檢索由統一軟件進行管理和控制。從發展的歷史看,數據庫是數據管理的高級階段,它是由文件管理系統發展起來的。



***結構語言SQL (Structured Query Language) ***


一種對關係數據庫中的數據進行定義和操作的句法,爲大多數關係數據庫管理系統所支持的工業標準。

結構化查詢語言是一種數據庫查詢和程序設計語言,用於存取數據以及查詢、更新和管理關係數據庫系統。它不要求用戶指定對數據的存放方法,也不需要用戶瞭解具體的數據存放方式,所以具有完全不同底層結構的不同數據庫系統可以使用相同的結構化查詢語言語言作爲數據輸入與管理的接口。結構化查詢語言語句可以嵌套,這使他具有極大的靈活性和強大的功能。


結構化查詢語言包含6個部分:(需掌握一、二、五)


一:數據查詢語言(DQL):其語句,也稱爲數據檢索語句,用以從表中獲得數據,確定數據怎樣在應用程序給出。保留字SELECTDQL(也是所有SQL)用得最多的動詞,其他DQL常用的保留字有WHEREORDER BYGROUP BYHANG。這些DQL保留字常與其他類型的SQL語句一起使用。


二:數據操作語言(DML:其語句包括動詞INSERTUPDATEDELETE。它們分別用於添加,修改和刪除表中的行。也稱爲動作查詢語言。


三:事務處理語言(TPL):它的語句能確保被DML語句影響的表的所有行及時得以更新。TPL語句包括BEGIN TRANSACTIONCOMMITROLLBACK


四:數據控制語言(DCL):它的語句通過GRANTREVOKE獲得許可,確定單個用戶和用戶組對數據庫對象的訪問。某些RDBMS可用GRANTREVOKE控制對錶單個列的訪問。


五:數據定義語言(DDL):其語句包括動詞CREATEDROP。在數據庫中創建新表或刪除表(CREAT TABLE DROP TABLE);爲表加入索引等。DDL包括許多與人數據庫目錄中獲得數據有關的保留字。它也是動作查詢的一部分。


六:指針控制語言(CCL):它的語句,像DECLARE CURSORFETCH INTOUPDATE WHERE CURRENT用於對一個或多個表單獨行的操作。



數據類型

結構化查詢語言中有五種數據類型:字符型,文本型,數值型,邏輯型和日期型。


1. 字符型  VARCHAR VS CHAR

VARCHAR型和CHAR型數據的這個差別是細微的,但是非常重要。他們都是用來儲存字符串長度小於255的字符。VARCHAR型字段的另一個突出的好處是它可以比CHAR型字段佔用更少的內存和硬盤空間。當你的數據庫很大時,這種內存和磁盤空間的節省會變得非常重要。使用VARCHAR型字段時,你不需要爲剪掉你數據中多餘的空格而操心。


2. 文本型   TEXT

使用文本型數據,你可以存放超過二十億個字符的字符串。當你需要存儲大串的字符時,應該使用文本型數據。

注意文本型數據沒有長度,而上一節中所講的字符型數據是有長度的。一個文本型字段中的數據通常要麼爲空,要麼很大。

無論何時,只要你能避免使用文本型字段,你就應該不適用它。文本型字段既大且慢,濫用文本型字段會使服務器速度變慢。文本型字段還會吃掉大量的磁盤空間。一旦你向文本型字段中輸入了任何數據(甚至是空值),就會有2K的空間被自動分配給該數據。除非刪除該記錄,否則你無法收回這部分存儲空間。


3. 數值型整數INT 、小數NUMERIC、錢數MONEY

一個INT型數據佔用四個字節。這看起來似乎差別不大,但是在比較大的表中,字節數的增長是很快的。另一方面,一旦你已經創建了一個字段,要修改它是很困難的。因此,爲安全起見,你應該預測以下,一個字段所需要存儲的數值最大有可能是多大,然後選擇適當的數據類型。


4. 邏輯型   BIT

如果你使用複選框(CHECKBOX)從網頁中搜集信息,你可以把此信息存儲在BIT型字段中。BIT型字段只能取兩個值:01

當心,在你創建好一個表之後,你不能向表中添加 BIT型字段。如果你打算在一個表中包含BIT型字段,你必須在創建表時完成。


5. 日期型  DATETIME VS SMALLDATETIME

一個 DATETIME型的字段可以存儲的日期範圍是從175311日第一毫秒到99991231日最後一毫秒。DATETIME型字段在你輸入日期和時間之前並不包含實際的數據,認識這一點是重要的。



SQL使用方式

簡單的結構化查詢語言查詢只包括SELECT選擇列表、FROM子句和WHERE子句。它們分別說明所查詢列、查詢的表或視圖、以及搜索條件等。


一、選擇列表  選擇列表(select_list)指出所查詢列,它可以是一組列名列表、星號、表達式、變量(包括局部變量和全局變量)等構成。


1、選擇所有列

例如,下面語句顯示testtable表中所有列的數據:

SELECT *FROM testtable


2、選擇部分列並指定它們的顯示次序

查詢結果集合中數據的排列順序與選擇列表中所指定的列名排列順序相同。


3、更改列標題

在選擇列表中,可重新指定列標題。定義格式爲:

列標題=列名 列名列標題

如果指定的列標題不是標準的標識符格式時,應使用引號定界符,例如,下列語句使用漢字顯示列標題:

SELECT 暱稱=nickname,電子郵件=emailFROM testtable


4、刪除重複行

SELECT語句中使用ALLDISTINCT選項來顯示錶中符合條件的所有行或刪除其中重複的數據行,默認爲ALL。使用DISTINCT選項時,對於所有重複的數據行在SELECT返回的結果集合中只保留一行。


5、限制返回的行數

使用TOP n [PERCENT]選項限制返回的數據行數,TOP n說明返回n行,而TOP n PERCENT時,說明n是表示一百分數,指定返回的行數等於總行數的百分之幾。


二、FROM子句

FROM子句指定SELECT語句查詢及與查詢相關的表或視圖。在FROM子句中最多可指定256個表或視圖,它們之間用逗號分隔。

FROM子句同時指定多個表或視圖時,如果選擇列表中存在同名列,這時應使用對象名限定這些列所屬的表或視圖。


三、WHERE子句

WHERE子句設置查詢條件,過濾掉不需要的數據行。

WHERE子句可包括各種條件運算符:

比較運算符(大小比較):>;、>==<;、<=<>;、!>;、!<

範圍運算符(表達式值是否在指定的範圍):BETWEEN…AND…

NOT BETWEEN…AND…

列表運算符(判斷表達式是否爲列表中的指定項):IN (項1,項2……

NOT IN (項1,項2……

模式匹配符(判斷值是否與指定的字符通配格式相符):LIKENOT LIKE

空值判斷符(判斷表達式是否爲空):IS NULLIS NOT NULL

邏輯運算符(用於多條件的邏輯連接):NOTANDOR

1、範圍運算符例:age BETWEEN 10 AND 30相當於age>=10 AND age<=30

2、列表運算符例:country IN ('Germany','China')

3、模式匹配符例:常用於模糊查找,它判斷列值是否與指定的字符串格式相匹配。可用於charvarchartextntextdatetimesmalldatetime等類型查詢。

可使用以下通配字符:

百分號%:可匹配任意類型和長度的字符,如果是中文,請使用兩個百分號即%%

下劃線_:匹配單個任意字符,它常用來限制表達式的字符長度。

方括號[]:指定一個字符、字符串或範圍,要求所匹配對象爲它們中的任一個。[^]:其取值也[] 相同,但它要求所匹配對象爲指定字符以外的任一個字符。


四、查詢結果排序

使用ORDER BY子句對查詢返回的結果按一列或多列排序。ORDER BY子句的語法格式爲:

ORDER BY {column_name [ASC|DESC]} […n]

其中ASC表示升序,爲默認值,DESC爲降序。ORDER BY不能按ntexttextp_w_picpath數據類型進行排序。



*** SQL語句的添加、刪除、修改***


數據記錄篩選注意:單雙引號的用法可能有誤(沒有測式)  

Sql = "Select Distinct 字段名 From 數據表"  

   Distinct函數,查詢數據庫存表內不重複的記錄

Sql = "Select Count(*) From 數據表 where 字段名1>#18:0:0# and 字段名1< #19:00# "  

   count函數,查詢數庫表內有多少條記錄,字段名1”是指同一字段

例:

set rs=conn.execute("select count(id) as idnum from news")  

response.write rs("idnum")  

sql="select * from 數據表 where 字段名 between 1 and 2"  

Sql="select * from 數據表 where 字段名 between #2003-8-10# and #2003-8-12#"  

在日期類數值爲2003-8-10 19:55:08 的字段裏查找2003-8-102003-8-12的所有記錄,而不管是幾點幾分。

select * from tb_name where datetime between #2003-8-10# and #2003-8-12#  

字段裏面的數據格式爲:2003-8-10 19:55:08,通過sql查出2003-8-102003-8-12的所有紀錄,而不管是幾點幾分。

Sql="select * from 數據表 where 字段名=字段值 order by 字段名 [desc]"  

Sql="select * from 數據表 where 字段名 like '%字段值%' order by 字段名 [desc]"  


模糊查詢

Sql="select top 10 * from 數據表 where 字段名 order by 字段名 [desc]"

查找數據庫中前10記錄

Sql="select top n * form 數據表 order by newid()"  

隨機取出數據庫中的若干條記錄的方法  top nn就是要取出的記錄數

Sql="select * from 數據表 where 字段名 in ('1','2','3')"


添加數據記錄

sql="insert into 數據表 (字段1,字段2,字段3 „) valuess (1,2,3 „)"  

sql="insert into 數據表 valuess (1,2,3 „)"  

不指定具體字段名錶示將按照數據表中字段的順序,依次添加

sql="insert into 目標數據表 select * from 源數據表"  

把源數據表的記錄添加到目標數據表


更新數據記錄

Sql="update 數據表 set 字段名=字段值 where 條件表達式"  

Sql="update 數據表 set 字段1=1,字段2=2 „„ 字段n=n where 條件表達式"  

Sql="update 數據表 set 字段1=1,字段2=2 „„ 字段n=n "  

沒有條件則更新整個數據表中的指定字段值


刪除數據記錄

Sql="delete from 數據表 where 條件表達式"  

Sql="delete from 數據表"  沒有條件將刪除數據表中所有記錄)


數據記錄統計函數

AVG(字段名) 得出一個表格欄平均值

COUNT(*|字段名) 對數據行數的統計或對某一欄有值的數據行數統計

MAX(字段名) 取得一個表格欄最大的值

MIN(字段名) 取得一個表格欄最小的值

SUM(字段名) 把數據欄的值相加


引用以上函數的方法:

sql="select sum(字段名) as 別名 from 數據表 where 條件表達式"  

set rs=conn.excute(sql)  

rs("別名") 獲取統的計值,其它函數運用同上。


數據表的建立和刪除

CREATE TABLE 數據表名稱(字段1 類型1(長度),字段2 類型2(長度) „„ )  

例:CREATE TABLE tab01(name varchar(50),datetime default now())  

DROP TABLE 數據表名稱 (永久性刪除一個數據表)


記錄集對象的方法

rs.movenext 將記錄指針從當前的位置向下移一行

rs.moveprevious 將記錄指針從當前的位置向上移一行

rs.movefirst 將記錄指針移到數據表第一行

rs.movelast 將記錄指針移到數據表最後一行

rs.absoluteposition=N 將記錄指針移到數據表第N

rs.absolutepage=N 將記錄指針移到第N頁的第一行

rs.pagesize=N 設置每頁爲N條記錄

rs.pagecount 根據 pagesize 的設置返回總頁數

rs.recordcount 返回記錄總數

rs.bof 返回記錄指針是否超出數據表首端,true表示是,false爲否

rs.eof 返回記錄指針是否超出數據表末端,true表示是,false爲否

rs.delete 刪除當前記錄,但記錄指針不會向下移動

rs.addnew 添加記錄到數據表末端

rs.update 更新數據表記錄




***常見關係型數庫***

Oracle:大型

MySQL:小型

SQLite,是一款輕型的數據庫,是遵守ACID的關聯式數據庫管理系統,它的設計目標是嵌入式的,而且目前已經在很多嵌入式產品中使用了它,它佔用資源非常的低,在嵌入式設備中,可能只需要幾百K的內存就夠了。它能夠支持Windows/Linux/Unix等等主流的操作系統,同時能夠跟很多程序語言相結合,比如 TclC#PHPJava等,還有ODBC接口,同樣比起MysqlPostgreSQL這兩款開源世界著名的數據庫管理系統來講,它的處理速度比他們都快。不像常見的客戶端/服務器結構範例,SQLite引擎不是個程序與之通信的獨立進程,而是連接到程序中成爲它的一個主要部分。所以主要的通信協議是在編程語言內的直接API調用。這在消耗總量、延遲時間和整體簡單性上有積極的作用。整個數據庫(定義、表、索引和數據本身)都在宿主主機上存儲在一個單一的文件中。它的簡單的設計是通過在開始一個事務的時候鎖定整個數據文件而完成的。


SQLite 支持跨平臺,操作簡單,能夠使用很多語言直接創建數據庫,而不象Access一樣需要Office的支持。如果你是個很小型的應用,或者你想做嵌入式開發,沒有合適的數據庫系統,那麼現在你可以考慮使用SQLite。同時因爲數據庫結構簡單,系統源代碼也不是很多,也適合想研究數據庫系統開發的專業人士。



SQLite支持哪些數據類型些?

NULL    值爲NULL

INTEGER 值爲帶符號的整型,根據類別用123468字節存儲

REAL    值爲浮點型,8字節存儲

TEXT    值爲text字符串,使用數據庫編碼(UTF-8, UTF-16BE or UTF-16-LE)存儲

BLOB    值爲二進制數據,具體看實際輸入;比如要在數據庫中存放一張圖片,這張圖片就會以二進制形式存放,在sqlite中對應的數據類型就是BLOB



但實際上,sqlite3也接受如下的數據類型:

smallint  16 位元的整數

interger  32 位元的整數

decimal(p,s)  p 精確值和 s 大小的十進位整數,精確值p是指全部有幾個數(digits)大小值s是指小數點後有幾位數。如果沒有特別指定,則系統會設爲 p=5; s=0

float   32位元的實數。

double   64位元的實數。

char(n)   n 長度的字串,n不能超過 254

varchar(n)  長度不固定且其最大長度爲 n 的字串,n不能超過 4000

graphic(n)   char(n) 一樣,不過其單位是兩個字元 double-bytes n不能超過127這個形態是爲了支援兩個字元長度的字體,例如中文字。

vargraphic(n)  可變長度且其最大長度爲 n 的雙字元字串,n不能超過 2000

date   包含了年份、月份、日期。

time   包含了小時、分鐘、秒。

timestamp  包含了年、月、日、時、分、秒、千分之一秒。



@如果不往數據庫裏面添加任何的表,這個數據庫等於沒有建立,不會在硬盤上產生任何文件,如果數據庫已經存在,則會打開這個數據庫。


@SQL 標準規定,在字符串中,單引號需要使用逃逸字符,即在一行中使用兩個單引號。


@INTEGER PRIMARY KEY屬性,有什麼特性?

如果將聲明表的一列設置爲 INTEGER PRIMARY KEY,則具有:

1.每當你在該列上插入一NULL值時, NULL自動被轉換爲一個比該列中最大值大1的一個整數;

2.如果表是空的,將會是1

注意該整數會比表中該列上的插入之前的最大值大1該鍵值在當前的表中是唯一的。但有可能與已從表中刪除的值重疊。要想建立在整個表的生命週期中唯一的鍵值,需要在 INTEGER PRIMARY KEY 上增加AUTOINCREMENT聲明。那麼,新的鍵值將會比該表中曾能存在過的最大值大1



/* 對於SQlite3,所有的API函數都有一個前綴:sqlite3_。這個前綴表明這些APIsSQlite數據庫產品提供,3代表版本。所有的常量都有一個前綴:SQLITE_SQlite數據庫的源碼是完全開放的,對於提供給客戶應用程序調用的API,函數名全部由小寫字符組成。如sqlite3_create_function_v2就是一個PUBLIC API,而sqlite3CreateFunc就是一個SQlite內部函數。

對於使用SQlite數據庫的客戶來說,提供一套穩定的API非常重要,否則SQlite每出來一個Release版本,之前的API就煥然一新,那客戶應用程序就需要修改自己的APP,付出很大的維護代價。所以SQlite數據庫的API,一旦發佈就不會刪除或者修改,如果某個API確實有必要改進,就會提供一個加了“v2”後綴的新API函數,而保留以前的舊版本,這樣客戶APP無需修改依然可以正常運行。比如:sqlite3_create_functionsqlite3_create_function_v2

  SQliteC APIs包括一定數量的數據結構,接近二百個函數,還有兩三百個常量。雖然API的數量比較多,但用起來並不複雜,其中只有一部分函數是經常使用的,還有很多函數的功能相似。

該例直接使用SQLite提供的C接口API,而且使用新版的帶_v2API.

爲求簡單沒有界面顯示數據庫內容,需要數據庫查看工具輔助。

*/



以下是代碼實現:

#import "ViewController.h"
// 首先添加libsqlite3.0.dylib庫,再導入頭文件
#import <sqlite3.h>
// 數據庫名稱
#define DatabaseName @"database.sqlite3"
@interface ViewController () {
    sqlite3* _pDB; // 指向SQLite數據庫的指針,非OC對象
}
- (const char* )databasePath;
- (IBAction)clickButton:(id)sender;
@end



- (void)viewDidLoad
{
    [super viewDidLoad];
    /* 在執行任何SQL語句之前,必須首先連接到一個數據庫,也就是打開或者新建一個SQlite3數據庫文件。連接數據庫由sqlite3_open函數完成,它一共有上面3個版本。其中 sqlite3_open函數假定SQlite3數據庫文件名爲UTF-8編碼,sqlite3_open_v2是它的加強版。sqlite3_open16函數假定SQlite3數據庫文件名爲UTF-16(Unicode寬字符)編碼。
      */
                                                  
    _pDB = NULL; // 定義指向代表SQLite數據庫結構體的指針
    // SQlite3數據庫文件的擴展名沒有一個標準定義,比較流行的選擇是.sqlite3、.db、.db3。不過在Windows系統平臺上,不推薦使用.sdb作爲 SQlite3數據庫文件的擴展名,據說這會導致IO速度顯著減慢,因爲.sdb擴展名有其特殊用義。
    // 調用SQlite API時,如果成功則會返回SQLITE_OK,如果調用失敗將返回一個錯誤碼(Error code),指明發生了什麼錯誤。對API調用的返回值進行適當檢查,可以提高程序的健壯性。
    NSInteger status = sqlite3_open_v2([self databasePath], &_pDB, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
    /* sqlite3_open_v2(const char *filename, sqlite3 **ppDb, int flags, const char *zVfs)
     參數說明:
     filename:需要被打開的數據庫文件的文件名,在sqlite3_open和sqlite3_open_v2中這個參數採用UTF-8編碼,而在sqlite3_open16中則採用UTF-16編碼。注意該參數是c字符串而不是OC.
                                                   
     ppDb:參數ppDb看起來有點複雜,它是一個指向指針的指針。當調用sqlite3_open_xxx函數時,該函數將分配一個新的SQlite3數據結構,然後初始化,然後將指針ppDb指向它。所以客戶應用程序可以通過sqlite3_open_xxx函數連接到名爲filename的數據庫,並通過參數ppDb返回指向該數據庫數據結構的指針。如果sqlite不能分配內存來存放sqlite對象,ppDb將會被返回一個NULL值。
                                                   
     flags:作爲數據庫連接的額外控制的參數,可以是SQLITE_OPEN_READONLY,SQLITE_OPEN_READWRITE和SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE中的一個,用於控制數據庫的打開方式。
     SQLITE_OPEN_READONLY,則SQlite3數據庫文件以只讀的方式打開,如果該數據庫文件不存在,則sqlite3_open_v2函數執行失敗,返回一個error。
     SQLITE_OPEN_READWRITE,則SQlite3數據庫文件以可讀可寫的方式打開,如果該數據庫文件本身被操作系統設置爲寫保護狀態,則以只讀的方式打開。如果該數據庫文件不存在,則sqlite3_open_v2函數執行失敗,返回一個error。
     SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,則SQlite3數據庫文件以可讀可寫的方式打開,如果該數據庫文件不存在則新建一個。這也是sqlite3_open和sqlite3_open16函數的默認行爲。除此之外,flags還可以設置爲其他標誌,具體可以查看SQlite官方文檔。
                                                   
     zVfs:允許客戶應用程序命名一個虛擬文件系統(Virtual File System)模塊,用來與數據庫連接。VFS作爲SQlite library和底層存儲系統(如某個文件系統)之間的一個抽象層,通常客戶應用程序可以簡單的給該參數傳遞一個NULL指針,以使用默認的VFS模塊。
    */
    if (SQLITE_OK != status) {
        NSLog(@"數據庫打開失敗,%@", NSStringFromSelector(_cmd));
    }
                                                  
    sqlite3_close(_pDB); // 關閉數據庫
    _pDB = NULL;
    //在使用完SQlite數據庫之後,需要調用sqlite3_close函數關閉數據庫連接,釋放數據結構所關聯的內存,刪除所有的臨時數據項。如果在調用sqlite3_close函數關閉數據庫之前,還有某些沒有完成的(nonfinalized)SQL語句,那麼sqlite3_close函數將會返回SQLITE_BUSY錯誤。客戶程序員需要finalize所有的預處理語句(prepared statement)之後再次調用sqlite3_close。
}




// 用於返回沙盒下Document的完整路徑(C字符串)
- (const char* )databasePath
{
    NSString* documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
    NSString* fullPath = [documentPath stringByAppendingPathComponent:DatabaseName];
    return [fullPath cStringUsingEncoding:NSUTF8StringEncoding];
}



- (IBAction)clickButton:(id)sender {
    UIButton* button = (UIButton* )sender;
                                         
    // 建議:執行任何操作前,打開數據庫,操作完成後關閉
    NSInteger status = 0;
    if (_pDB == NULL) {
        status = sqlite3_open_v2([self databasePath], &_pDB, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
        if (SQLITE_OK != status) {
            NSLog(@"數據庫打開失敗,%@", NSStringFromSelector(_cmd));
            sqlite3_close(_pDB);
            _pDB = NULL;
        }
    }
                                         
    switch (button.tag) {
        case 100: { // 創建表
            [self createTableWithDatabase:_pDB];
        } break;
                                                 
        case 200: { // 添加一行數據
            [self addDataWithDatabase:_pDB];
        } break;
                                                 
        case 300: {
            [self deleteFromDatabase:_pDB];
        } break;
                                                 
        case 400: {
            [self selectWithDatabase:_pDB];
        } break;
                                                 
        default:
            break;
    }
                                         
    // 操作完成後關閉數據庫
    sqlite3_close(_pDB);
    _pDB = NULL;   
}






// 這裏創建一個表
- (void)createTableWithDatabase:(sqlite3* )db {
                              
    if (!db) {
        return;
    }
    // 定義一個sqlite3_stmt結構體的指針,用於保存編譯成字節碼的SQL語句
    // 在sqlite中並沒有定義sqlite3_stmt這個結構的具體內容,它只是一個抽象類型,在使用過程中一般以它的指針進行操作
    sqlite3_stmt* stmt = NULL;
                              
    // 需要執行的SQL語句,其中的保留字大小寫無關,如“create”和“CREATE”一樣
    char* szSql = "CREATE TABLE UserInfo(id INTEGER PRIMARY KEY AUTOINCREMENT, date TEXT, money REAL)";
                              
    NSInteger status = sqlite3_prepare_v2(db, szSql, -1, &stmt, NULL);
    /*
           sqlite3_prepare_v2(sqlite3 *db,
                       const char *szSql,
                       int nByte,
                       sqlite3_stmt **ppStmt,
                       const char **pzTail)
            這個函數將sql文本轉換成一個準備語句(prepared statement)對象,同時返回這個對象的指針。這個接口需要一個數據庫連接指針以及一個要準備的包含SQL語句的文本。它實際上並不執行(evaluate)這個SQL語句,它僅僅爲執行準備這個sql語句(編譯SQL語句爲字節碼留給後面的執行函數執行)。
             參數:
             db:數據指針
                               
             szSql:sql語句,使用UTF-8編碼
                               
             nByte:szSql的字節長度。如果nByte爲負值,則prepare函數會自動計算出szSql的字節長度,不過要確保szSql傳入的是以NULL結尾的字符串。如果nByte不是負的,那麼它就是這個函數能從szSql中讀取的字節數的最大值。如果nBytes爲負,szSql在第一次遇見’/000/或’u000’的時候終止。如果SQL命令字符串中只包含一條SQL語句,那麼它沒有必要以“;”結尾。
                               
             ppStmt:一個指向指針的指針,用來傳回一個指向新建的sqlite3_stmt結構體的指針,sqlite3_stmt結構體裏面保存有轉換好的SQL語句。如果SQL命令字符串包含多條SQL語句,同時參數pzTail不爲NULL,那麼它將指向SQL命令字符串中的下一條SQL語句。如果錯誤發生,它被置爲NULL。調用過程必須負責在編譯好的sql語句完成使用後使用sqlite3_finalize()刪除它。
                               
             pzTail:上面提到szSql在遇見終止符或者是達到設定的nByte之後結束,假如szSql還有剩餘的內容,那麼這些剩餘的內容被存放到pZTail中,不包括終止符
                                       
             說明
             如果執行成功,則返回SQLITE_OK,否則返回一個錯誤碼。推薦在現在任何的程序中都使用sqlite3_prepare_v2這個函數,sqlite3_prepare只是用於前向兼容。
             prepared語句可以被重置(調用sqlite3_reset函數),然後可以重新綁定參數之後重新執行。sqlite3_prepare_v2函數代價昂貴,所以通常儘可能的重用prepared語句。最後,這條prepared語句確實不在使用時,調用sqlite3_finalize函數釋放所有的內部資源和sqlite3_stmt數據結構,有效刪除prepared語句。
        */
    if (status == SQLITE_OK) { // 如果該表存在,會返回 SQLITE_ERROR
                                  
        // CREATE TABLE語句沒有返回值,調用sqlite3_step函數執行這條語句
        // 通過調用sqlite3_step一次或多次來執行前面sqlite3_prepare創建的準備語句。這個語句執行到結果的第一行可用的位置,如需繼續前進到結果的第二行的話,只需再次調用sqlite3_setp()
        // 對於不返回結果的語句(如:INSERT,UPDATE,或DELETE),sqlite3_step()只執行一次就返回
        NSInteger result = sqlite3_step(stmt);
        /*
                說明:sqlite3_exec 這個方法可以執行那些沒有返回結果的操作,例如創建、插入、刪除等。
                它屬於早期遺留下來的便捷函數,這些函數存在很多缺點。當然他們依然存在就有理由——使用方便。它們的優點也僅僅是使用方便,而不是具有很好的性能。對於這些便捷函數,它們並沒有什麼特別之處,只是在這些函數內部調用sqlite3_prepare_xxx、sqlite3_step、sqlite3_finalize等API函數來完成一站式功能。在這樣的函數內部往往存在很多額外的類型轉換,所以這些函數很可能會比我們自己去調用sqlite3_prepare_xxx、sqlite3_step、sqlite3_finalize等API執行的更慢一些。
               */
        if (result != SQLITE_DONE) {
            NSLog(@"創建表失敗,%@", NSStringFromSelector(_cmd));
        }
    } else {
        NSLog(@"該表已經存在或創建失敗");
    }
                              
    sqlite3_finalize(stmt); // 摧毀stmt結構體,釋放資源
}








// 插入數據
- (void)addDataWithDatabase:(sqlite3* )db {
    if (!db) {
        return;
    }
                     
    // 準備工作,解釋見 createTableWithDatabase
    sqlite3_stmt* stmt = NULL;
    char* szSql = "INSERT INTO UserInfo(date, money) VALUES(?, ?)";
    /* SQL插入語句
        插入語句有幾種形式,標準的爲:"insert into 數據表 (字段1,字段2,字段3, ...) valuess (值1,值2,值3, ...)"
        其中valuess (值1,值2,值3, ...) 中的值可以使用具體的數值,也可以使用如下的佔位的通配符:
        ?
        ?NNN,NNN代表數字
        :VVV,VVV代表字符
        @VVV
        $VVV
        這裏只介紹 ?,其它的自己查SQL文檔。相同的通配符在同一個SQL聲明中出現多次, 在這種情況下所有相同的通配符都會被替換成相應的值. 沒有被綁定的通配符將自動取NULL值。使用sqlite3_bind_*()來給這些參數綁定值,用sqlite3_clear_bindings重設這些綁定。
        SQL語句字符串可以帶?號,它是SQL語句中的不確定部分,需要對它另外賦值。事實上,SQLite的官方文檔中已經明確指出,在很多時候sqlite3_prepare_v2函數的執行時間要多於sqlite3_step函數的執行時間,因此建議使用者要儘量避免重複調用sqlite3_prepare_v2函數。在我們的實現中,如果想避免此類開銷,只需將待插入的數據以變量的形式綁定到SQL語句中,這樣該SQL語句僅需調用sqlite3_prepare_v2函數編譯一次即可,其後的操作只是替換不同的變量數值。
        sqlite3_reset() 函數可以用來重置一個SQL聲明的狀態(sqlite3_stmt),使得它回到未執行前的狀態,並不改變綁定值。
       */
                     
    NSInteger status = sqlite3_prepare_v2(db, szSql, -1, &stmt, NULL);
    if (SQLITE_OK != status) {
        NSLog(@"插入數據失敗,%@", NSStringFromSelector(_cmd));
        sqlite3_finalize(stmt);
        return;
    }
                     
    /* 綁定參數
         int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
         int sqlite3_bind_double(sqlite3_stmt*, int, double);
         int sqlite3_bind_int(sqlite3_stmt*, int, int);
         int sqlite3_bind_int64(sqlite3_stmt*, int, long long int);
         int sqlite3_bind_null(sqlite3_stmt*, int);
         int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*));
         int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int n, void(*)(void*));
         int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*);
         以上是 sqlite3_bind 所包含的全部接口,它們是用來給SQL聲明中的通配符賦值的. 沒有綁定的通配符則被認爲是空值。在準備SQL語句過程中,綁定是可選的。其中的第二個參數表示該綁定參數對應在SQL語句中?的索引值。第三個參數爲替換的具體值。
        */
    NSDate* now = [NSDate date]; // 獲得當前GMT時間
//    NSLog(@"date = %@", now);
    // 第一個綁定參數
    sqlite3_bind_text(stmt, 1, [[now description] UTF8String], -1, SQLITE_STATIC);
    /* 上面第四個參數爲字符串的長度,該值爲負將返回第一遇到'\0'的位置。
           第五個參數爲一個函數指針,SQLITE3執行完操作後回調此函數,通常用於釋放字符串佔用的內存。此參數有兩個常數,SQLITE_STATIC告訴sqlite3_bind_text函數字符串爲常量,可以放心使用;而SQLITE_TRANSIENT會使得sqlite3_bind_text函數對字符串做一份拷貝。
       */
    // 第二個綁定參數
    double money = arc4random() % 1000 * 0.01; // 隨機數值
    sqlite3_bind_double(stmt, 2, money);
                     
    // 如是,stmt完全準備好了,下面就是執行工作,我們依然使用sqlite3_step
    // 對於不返回結果的語句(如:INSERT,UPDATE,或DELETE),sqlite3_step()只執行一次就返回
    // 返回SQLITE_BUSY表示暫時無法執行操作,SQLITE_DONE表示操作執行完畢,SQLITE_ROW表示執行完畢並且有返回(執行select語句時)。當返回值爲SQLITE_ROW時,我們需要對查詢結果進行處理,SQLITE3提供sqlite3_column_*系列函數。
    NSInteger result = sqlite3_step(stmt);
    if (SQLITE_DONE == result) {
        NSLog(@"插入一條數據");
    } else {
        NSLog(@"插入數據失敗,%@", NSStringFromSelector(_cmd));
    }
    sqlite3_finalize(stmt); // 摧毀stmt結構體,釋放資源
}





// 刪除最後一條數據
- (void)deleteFromDatabase:(sqlite3* )db {
    // 刪除一般伴隨查找,建議先看查找
    // 首先找到最後一條數據的主鍵(id)
    // SQLite中語法的不同,不能使用top 1,應使用LIMIT 0,1表示從第0條記錄開始,往後讀取1條記錄
    char* selectSql = "SELECT * FROM UserInfo ORDER BY id DESC LIMIT 0,1";
    sqlite3_stmt* stmt = NULL;
    NSInteger Status = sqlite3_prepare_v2(db, selectSql, -1, &stmt, NULL);
               
    if (SQLITE_OK != Status) {
        sqlite3_finalize(stmt);
        return;
    }
               
    NSInteger nIndex = -1;
    while (SQLITE_ROW == sqlite3_step(stmt)) {
        nIndex = sqlite3_column_int(stmt, 0);
        NSLog(@"最後一行的id = %i", nIndex);
    }
               
    sqlite3_finalize(stmt); // 摧毀stmt結構體,釋放資源
    if (nIndex > -1) {
        sqlite3_stmt* st = NULL;
        // 刪除該條記錄
        char* deleteSql = "DELETE FROM UserInfo WHERE id = ?";
        NSInteger result = sqlite3_prepare_v2(db, deleteSql, -1, &st, NULL);
                   
        if (SQLITE_OK == result) {
            // 多此一舉,使用綁定
            sqlite3_bind_int(st, 1, nIndex);
                       
            while (SQLITE_ROW == sqlite3_step(st)) {
            }
        }
        sqlite3_finalize(st);
    }  
}






// 查詢數值
- (void)selectWithDatabase:(sqlite3* )db {
    // SQL的篩選語句有非常多的形式,這裏只介紹常見的兩種:
    // "select * from 數據表 where 字段名=字段值 order by 字段名 [desc]"
    // "where"後面接篩選條件;"order by"表示排序方式(可選);ACS表示按正序排序(從小到大排序,默認),DESC 表示按倒序排序(從大到小排序)
    // "select * from 數據表 where 字段名 in ('值1','值2','值3')"
        
    // 查找所有"money"字段值大於2的所有數據
    char* szSql = "SELECT * FROM UserInfo WHERE money>2";
    sqlite3_stmt* stmt = NULL;
    sqlite3_prepare_v2(db, szSql, -1, &stmt, NULL);
    // 如果SQL語句中包含?號,需要使用sqlite3_bind_*()來給這些參數綁定值,這裏沒有。
        
    // 返回SQLITE_BUSY表示暫時無法執行操作,SQLITE_DONE表示操作執行完畢,SQLITE_ROW表示執行完畢並且有返回(執行select語句時)。當返回值爲SQLITE_ROW時,我們需要對查詢結果進行處理,SQLITE3提供sqlite3_column_*系列函數。
    /*
         sqlite3_column()
         這個過程從執行sqlite3_step()執行一個準備語句得到的結果集的當前行中返回一個列。每次sqlite3_step得到一個結果集的列停下後,這個過程就可以被多次調用去查詢這個行的各列的值。對列操作是有多個函數,均以sqlite3_column爲前綴
         const void *sqlite3_column_blob(sqlite3_stmt*, int iCol);
         int sqlite3_column_bytes(sqlite3_stmt*, int iCol);
         int sqlite3_column_bytes16(sqlite3_stmt*, int iCol);
         double sqlite3_column_double(sqlite3_stmt*, int iCol);
         int sqlite3_column_int(sqlite3_stmt*, int iCol);
         sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol);
         const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol);
         const void *sqlite3_column_text16(sqlite3_stmt*, int iCol);
         int sqlite3_column_type(sqlite3_stmt*, int iCol);
         sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol);
         說明
         第一個參數爲從sqlite3_prepare返回來的prepared statement對象的指針,第二參數指定這一行中的想要被返回的列的索引。最左邊的一列的索引號是0,行的列數可以使用sqlite3_colum_count()獲得。這些函數會根據情況去轉換數值的類型。
         單步執行一次將返回一個結果(一條數據),一條數據可能包含多列(每列對應一個字段)。
        */
    while (SQLITE_ROW == sqlite3_step(stmt)) {
        // 第0列對應字段“id”
        NSInteger nId = sqlite3_column_int(stmt, 0);
            
        // 第1列對應字段“date”
        char* szDate = (char* )sqlite3_column_text(stmt, 1);
        NSString* strDate = [NSString stringWithUTF8String:szDate];
            
        // 第2列對應字段“money”
        double dMoney = sqlite3_column_double(stmt, 2);
        NSLog(@"id = %i, date = %@, money = %g", nId, strDate, dMoney);
    }
        
    sqlite3_finalize(stmt); // 摧毀stmt結構體,釋放資源
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章