SQLite使用小結


一、SQLite簡介  

  SQLite是一款輕型的數據庫,是遵守ACID的關聯式數據庫管理系統,它的設計目標是嵌入式的,而且目前已經在很多嵌入式產品中使用了它,它占用資源非常的低,在嵌入式設備中,可能只需要幾百K的內存就夠了。它能夠支持Windows/Linux/Unix等等主流的操作系統,同時能夠跟很多程序語言相結合,比如Tcl、PHP、Java等,還有ODBC接口,同樣比起Mysql、PostgreSQL這兩款開源世界著名的數據庫管理系統來講,它的處理速度比他們都快。

  SQLite雖然很小巧,但是支持的SQL語句不會遜色於其他開源數據庫,它支持的SQL包括:

  ATTACH DATABASE

  BEGIN TRANSACTION

  comment

  COMMIT TRANSACTION

  COPY

  CREATE INDEX

  CREATE TABLE

  CREATE TRIGGER

  CREATE VIEW

  DELETE

  DETACH DATABASE

  DROP INDEX

  DROP TABLE

  DROP TRIGGER

  DROP VIEW

  END TRANSACTION

  EXPLAIN

  expression

  INSERT

  ON CONFLICT clause

  PRAGMA

  REPLACE

  ROLLBACK TRANSACTION

  SELECT

  UPDATE

  同時它還支持事務處理功能等等。也有人說它象Microsoft的Access,有時候真的覺得有點象,但是事實上它們區別很大。比如SQLite 支持跨平台,操作簡單,能夠使用很多語言直接創建數據庫,而不象Access一樣需要Office的支持。如果你是個很小型的應用,或者你想做嵌入式開發,沒有合適的數據庫系統,那么現在你可以考慮使用SQLite。目前它的最新版本是 3.2.2,它的官方網站是:http://www.sqlite.org或者http://www.sqlite.com.cn,能在上面獲得源代碼和文檔。同時因為數據庫結構簡單,系統源代碼也不是很多,也適合想研究數據庫系統開發的專業人士

  下面是訪問SQLite官方網站: http://www.sqlite.org/ 時第一眼看到關於SQLite的特性.

  1. ACID事務

  2. 零配置 – 無需安裝和管理配置

  3. 儲存在單一磁盤文件中的一個完整的數據庫

  4. 數據庫文件可以在不同字節順序的機器間自由的共享

  5. 支持數據庫大小至2TB

  6. 足夠小, 大致3萬行C代碼, 250K

  7. 比一些流行的數據庫在大部分普通數據庫操作要快

  8. 簡單, 輕松的API

  9. 包含TCL綁定, 同時通過Wrapper支持其他語言的綁定

  10. 良好注釋的源代碼, 並且有着90%以上的測試覆蓋率

  11. 獨立: 沒有額外依賴

  12. Source完全的Open, 你可以用於任何用途, 包括出售它

  13. 支持多種開發語言,C, PHP, Perl, Java, ASP.NET,Python

  2、SQLite類型

  SQLite的數據類型

  首先你會接觸到一個讓你驚訝的名詞: Typelessness(無類型). 對! SQLite是無類型的. 這意味着你可以保存任何類型的數據到你所想要保存的任何表的任何列中, 無論這列聲明的數據類型是什么(只有在一種情況下不是, 稍后解釋). 對於SQLite來說對字段不指定類型是完全有效的. 如:

  Create Table ex1(a, b, c);

  誠然SQLite允許忽略數據類型, 但是仍然建議在你的Create Table語句中指定數據類型. 因為數據類型對於你和其他的程序員交流, 或者你准備換掉你的數據庫引擎. SQLite支持常見的數據類型, 如:

  CREATE TABLE ex2(

  a VARCHAR(10),

  b NVARCHAR(15),

  c TEXT,

  d INTEGER,

  e FLOAT,

  f BOOLEAN,

  g CLOB,

  h BLOB,

  i TIMESTAMP,

  j NUMERIC(10,5)

  k VARYING CHARACTER (24),

  l NATIONAL VARYING CHARACTER(16)

  );

  前面提到在某種情況下, SQLite的字段並不是無類型的. 即在字段類型為”Integer Primary Key”時.

 

 

SQLite的簡單使用

SQLite可以到官方站點下載http://www.sqlite.org/download.html
包括:Linux,Mac OS X, Windows下的已編譯文件以及源代碼、幫助文檔。windows版的下載地址是:http://www.sqlite.org/sqlite-3_6_23_1.zip,下載后解壓就可以了。這個文件是工作在命令行下的。雙擊它,就直接進入命令行了。

打開之后是這樣顯示的:

SQLite version 3.6.23.1

Enter ".help" for instructions

Enter SQL statements terminated with a ";"

sqlite>

 

1、創建數據庫

按理說第一步是創建一個數據庫,我是學電子的,對計算機不了解,所以我不知道mysql是如何存儲數據庫的。但sqlite將一個數據庫存儲為一個文件。我們先進下cmd,關掉原來的sqlite3。

在命令提示符下:

sqlite3 newsql.db

然后就會自動跳到sqlite>命令提示符下。我記得在linux下用時候會在當前目錄下出現newsql.db文件。但在我所用版本的windows下沒有出現。然后我做了些嘗試得到如下結果:

sqlite3 newsql.db

.quit

注意:.quit是在sqlite提示符下輸入的,代表退出。看目錄下還是沒有出現數據庫文件。

sqlite3 newsql.db

;

.quit

出現了newsql.db文件。冒號加回車,在sqlite中,代表執行一條語句的意思,雖然我只輸入了一個冒號加回車,沒有輸入任何的語句,但結果已是不同。

2、創建一個表

create table mytable(entry1 varchar(10),entry2 int);

不要忘了加冒號。冒號代表一條語句輸入完畢。

mytable是我創建的表名。create 和table都是系統關鍵字。entry1,entry2是兩個表項。

varchar(10) int是類型。根據我讀到的內容,sqlite是不區分類型,但是我們還是要在創建表時,給他一個類型,以便於將這些代碼移植到其他的數據庫里面時更加的方便。

3、向表中插入一條記錄

insert into mytable values("hello world",10);

插入完了之后才發現是不是超出定義的大小了?我定義的entry1項是varchar(10)型的,說實在的,我不知這個類型確切來講是什么意思,我猜應該是10個字符的字符串數組吧。如果那樣的話我是一定超出了。但既然sqlite是不區分類型的,我想應該沒有問題吧。於是我急於看看是不是這樣...

4、查詢表中內容

select * from mytable;

執行這條語句,會列出mytable中的所有內容。

結果為:

sqlite> select * from mytable;

hello world|10

可見還是都插入進去了。

sqlite> insert into mytable values("goodbye cruel world",20);

sqlite> select * from mytable;

hello world|10

goodbye cruel world|20

也就是說,第一個條目的字符串完全不受限制。

5、sqlite3 newsql.db的規則

我們現在退出,然后重新打開數據庫:

sqlite> .quit

F:/sqlite>sqlite3 newsql.db

SQLite version 3.6.23.1

Enter ".help" for instructions

Enter SQL statements terminated with a ";"

sqlite> select * from mytable;

hello world|10

goodbye cruel world|20

可見sqlite3 newsql.db這個命令規則為:打開名為newsql.db這個數據庫,如果不存在則創建一個。

6、查詢一個數據庫中所有的表名

sqlite數據庫中有一個系統建立的表,名為sqlite_master,查詢這個表可以得到所有的表。

sqlite> create table my2ndtable(theonlyentry int);

sqlite> insert into my2ndtable values(30);

sqlite> select * from sqlite_master;

table|mytable|mytable|2|CREATE TABLE mytable(entry1 varchar(10),entry2 int)

table|my2ndtable|my2ndtable|3|CREATE TABLE my2ndtable(theonlyentry int)

對於這個表的定義,官方網站的FAQ中給出如下:

CREATE TABLE sqlite_master ( type TEXT, name TEXT, tbl_name TEXT, rootpage INTEGER, sql TEXT );第一個字段類型顯然會一直是table,第二個字段是名稱分別是mytable和my2ndtable,見上面的結果。第三個字段表名,沒弄懂是什么意,想必是所在的表的名字,但是一個表的名字和所在的表名不是一樣的嗎?第四個字段rootpage,我也不知指什么,這個系統的學過數據庫的人應該能知道,有路過的還望告之。第五個字段是創建表的使用的sql語句吧。

7、sqlite的輸出模式

 默認的輸出格式是“列表”。在列表模式下,每條查詢結果記錄被寫在一行中並且每列之間以一個字符串分割符隔開。默認的分隔符是一個管道符號(“|”)。列表符號在當你輸出查詢結果到另外一個符號處理的程序(如AWK)中去是尤為有用。

sqlite> .mode list

sqlite> select * from mytable;

hello world|10

goodbye cruel world|20

sqlite>

這是正常的模式。

sqlite> .mode csv

sqlite> select * from mytable;

"hello world",10

"goodbye cruel world",20

變化是什么?字符串被加上了引號。

sqlite> .mode column

sqlite> select * from mytable;

hello world  10

goodbye cru  20

哎呀,第二個字符串被截斷了。

.mode MODE ?TABLE?__  Set output mode where MODE is one of:

____________  csv___ Comma-separated values

____________  column_  Left-aligned columns._ (See .width)

____________  html__  HTML <table> code

____________  insert_  SQL insert statements for TABLE

____________  line__  One value per line

____________  list__  Values delimited by .separator string

____________  tabs__  Tab-separated values

____________  tcl___ TCL list elements

這些來自.help命令的輸出結果。

sqlite> .mode html

sqlite> select * from mytable;

<TR><TD>hello world</TD>

<TD>10</TD>

</TR>

<TR><TD>goodbye cruel world</TD>

<TD>20</TD>

</TR>

.mode html是一種較新的輸出方法。

另外,我們也可以把輸出結果輸出到文件:

sqlite> .output output.txt

sqlite> select * from mytable;

sqlite> .exit



F:/sqlite>type output.txt

hello world|10

goodbye cruel world|20

8、查看數據庫中所有的表

sqlite> .tables

my2ndtable  mytable

9、查看所有的表的創建語句

sqlite> .schema

CREATE TABLE my2ndtable(theonlyentry int);

CREATE TABLE mytable(entry1 varchar(10),entry2 int);

sqlite> .schema mytable

CREATE TABLE mytable(entry1 varchar(10),entry2 int);

10、數據庫導出和導入

我們可以利用這個功能做一個簡單的備份,或是說創建一個同樣的數據庫。

第一步,把數據庫倒出來:

cmd命令提示符下:

F:/sqlite>sqlite3 newsql.db ".dump" >a.sql

此語句將數據庫導出成a.sql數據庫語句文件,執行這個文件就可以創建一個一模一樣數據庫:

F:/sqlite>sqlite3 copied.db

SQLite version 3.6.23.1

Enter ".help" for instructions

Enter SQL statements terminated with a ";"

sqlite> select * from mytable;

hello world|10

goodbye cruel world|20

11、刪除記錄
delete from mytable where entry2=10;
可以刪掉mytable中所有entry2項為10的條目。
注意:不是delete * from mytable,而delete from mytable.沒有*.

 

三、SQLite3 C/C++ 開發接口簡介(API函數)

SQLite3是SQLite一個全新的版本,它雖然是在SQLite 2.8.13的代碼基礎之上開發的,但是使用了和之前的版本不兼容的數據庫格式和API. SQLite3是為了滿足以下的需求而開發的:

  • 支持UTF-16編碼.
  • 用戶自定義的文本排序方法.
  • 可以對BLOBs字段建立索引.

因此為了支持這些特性我改變了數據庫的格式,建立了一個與之前版本不兼容的3.0版. 至於其他的兼容性的改變,例如全新的API等等,都將在理論介紹之后向你說明,這樣可以使你最快的一次性擺脫兼容性問題.

3.0版的和2.X版的API非常相似,但是有一些重要的改變需要注意. 所有API接口函數和數據結構的前綴都由"sqlite_"改為了"sqlite3_". 這是為了避免同時使用SQLite 2.X和SQLite 3.0這兩個版本的時候發生鏈接沖突.

由於對於C語言應該用什么數據類型來存放UTF-16編碼的字符串並沒有一致的規范. 因此SQLite使用了普通的void* 類型來指向UTF-16編碼的字符串. 客戶端使用過程中可以把void*映射成適合他們的系統的任何數據類型.

SQLite 3.0一共有83個API函數,此外還有一些數據結構和預定義(#defines). (完整的API介紹請參看另一份文檔.) 不過你們可以放心,這些接口使用起來不會像它的數量所暗示的那么復雜. 最簡單的程序仍然使用三個函數就可以完成: sqlite3_open(), sqlite3_exec(), 和 sqlite3_close(). 要是想更好的控制數據庫引擎的執行,可以使用提供的sqlite3_prepare()函數把SQL語句編譯成字節碼,然后在使用sqlite3_step()函數來執行編譯后的字節碼. 以sqlite3_column_開頭的一組API函數用來獲取查詢結果集中的信息. 許多接口函數都是成對出現的,同時有UTF-8和UTF-16兩個版本. 並且提供了一組函數用來執行用戶自定義的SQL函數和文本排序函數.

2.1 如何打開關閉數據庫

   typedef struct sqlite3 sqlite3;
   int sqlite3_open(const char*, sqlite3**);
   int sqlite3_open16(const void*, sqlite3**);
   int sqlite3_close(sqlite3*);
   const char *sqlite3_errmsg(sqlite3*);
   const void *sqlite3_errmsg16(sqlite3*);
   int sqlite3_errcode(sqlite3*);

sqlite3_open() 函數返回一個整數錯誤代碼,而不是像第二版中一樣返回一個指向sqlite3結構體的指針. sqlite3_open() 和 sqlite3_open16() 的不同之處在於sqlite3_open16() 使用UTF-16編碼(使用本地主機字節順序)傳遞數據庫文件名. 如果要創建新數據庫, sqlite3_open16() 將內部文本轉換為UTF-16編碼, 反之sqlite3_open() 將文本轉換為UTF-8編碼.

打開或者創建數據庫的命令會被緩存,直到這個數據庫真正被調用的時候才會被執行. 而且允許使用PRAGMA聲明來設置如本地文本編碼或默認內存頁面大小等選項和參數.

sqlite3_errcode() 通常用來獲取最近調用的API接口返回的錯誤代碼. sqlite3_errmsg() 則用來得到這些錯誤代碼所對應的文字說明. 這些錯誤信息將以 UTF-8 的編碼返回,並且在下一次調用任何SQLite API函數的時候被清除. sqlite3_errmsg16() 和 sqlite3_errmsg() 大體上相同,除了返回的錯誤信息將以 UTF-16 本機字節順序編碼.

SQLite3的錯誤代碼相比SQLite2沒有任何的改變,它們分別是:

#define SQLITE_OK           0   /* Successful result */
#define SQLITE_ERROR        1   /* SQL error or missing database */
#define SQLITE_INTERNAL     2   /* An internal logic error in SQLite */
#define SQLITE_PERM         3   /* Access permission denied */
#define SQLITE_ABORT        4   /* Callback routine requested an abort */
#define SQLITE_BUSY         5   /* The database file is locked */
#define SQLITE_LOCKED       6   /* A table in the database is locked */
#define SQLITE_NOMEM        7   /* A malloc() failed */
#define SQLITE_READONLY     8   /* Attempt to write a readonly database */
#define SQLITE_INTERRUPT    9   /* Operation terminated by sqlite_interrupt() */
#define SQLITE_IOERR       10   /* Some kind of disk I/O error occurred */
#define SQLITE_CORRUPT     11   /* The database disk image is malformed */
#define SQLITE_NOTFOUND    12   /* (Internal Only) Table or record not found */
#define SQLITE_FULL        13   /* Insertion failed because database is full */
#define SQLITE_CANTOPEN    14   /* Unable to open the database file */
#define SQLITE_PROTOCOL    15   /* Database lock protocol error */
#define SQLITE_EMPTY       16   /* (Internal Only) Database table is empty */
#define SQLITE_SCHEMA      17   /* The database schema changed */
#define SQLITE_TOOBIG      18   /* Too much data for one row of a table */
#define SQLITE_CONSTRAINT  19   /* Abort due to contraint violation */
#define SQLITE_MISMATCH    20   /* Data type mismatch */
#define SQLITE_MISUSE      21   /* Library used incorrectly */
#define SQLITE_NOLFS       22   /* Uses OS features not supported on host */
#define SQLITE_AUTH        23   /* Authorization denied */
#define SQLITE_ROW         100  /* sqlite_step() has another row ready */
#define SQLITE_DONE        101  /* sqlite_step() has finished executing */

2.2 執行 SQL 語句

       typedef int (*sqlite_callback)(void*,int,char**, char**);
       int sqlite3_exec(sqlite3*, const char *sql, sqlite_callback, void*, char**);

sqlite3_exec 函數依然像它在SQLite2中一樣承擔着很多的工作. 該函數的第二個參數中可以編譯和執行零個或多個SQL語句. 查詢的結果返回給回調函數. 更多地信息可以查看API 參考.

在SQLite3里,sqlite3_exec一般是被准備SQL語句接口封裝起來使用的.

       typedef struct sqlite3_stmt sqlite3_stmt;
       int sqlite3_prepare(sqlite3*, const char*, int, sqlite3_stmt**, const char**);
       int sqlite3_prepare16(sqlite3*, const void*, int, sqlite3_stmt**, const void**);
       int sqlite3_finalize(sqlite3_stmt*);
       int sqlite3_reset(sqlite3_stmt*);

sqlite3_prepare 接口把一條SQL語句編譯成字節碼留給后面的執行函數. 使用該接口訪問數據庫是當前比較好的的一種方法.

sqlite3_prepare() 處理的SQL語句應該是UTF-8編碼的. 而sqlite3_prepare16() 則要求是UTF-16編碼的. 輸入的參數中只有第一個SQL語句會被編譯. 第四個參數則用來指向輸入參數中下一個需要編譯的SQL語句存放的SQLite statement對象的指針, 任何時候如果調用 sqlite3_finalize() 將銷毀一個准備好的SQL聲明. 在數據庫關閉之前,所有准備好的聲明都必須被釋放銷毀. sqlite3_reset() 函數用來重置一個SQL聲明的狀態,使得它可以被再次執行.

SQL聲明可以包含一些型如"?" 或 "?nnn" 或 ":aaa"的標記,其中"nnn" 是一個整數,"aaa" 是一個字符串. 這些標記代表一些不確定的字符值(或者說是通配符),可以在后面用sqlite3_bind 接口來填充這些值. 每一個通配符都被分配了一個編號(由它在SQL聲明中的位置決定,從1開始),此外也可以用 "nnn" 來表示 "?nnn" 這種情況. 允許相同的通配符在同一個SQL聲明中出現多次, 在這種情況下所有相同的通配符都會被替換成相同的值. 沒有被綁定的通配符將自動取NULL值.

       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聲明中的通配符賦值的. 沒有綁定的通配符則被認為是空值. 綁定上的值不會被sqlite3_reset()函數重置. 但是在調用了sqlite3_reset()之后所有的通配符都可以被重新賦值.

在SQL聲明准備好之后(其中綁定的步驟是可選的), 需要調用以下的方法來執行:

       int sqlite3_step(sqlite3_stmt*);

如果SQL返回了一個單行結果集,sqlite3_step() 函數將返回 SQLITE_ROW , 如果SQL語句執行成功或者正常將返回 SQLITE_DONE , 否則將返回錯誤代碼. 如果不能打開數據庫文件則會返回 SQLITE_BUSY . 如果函數的返回值是 SQLITE_ROW, 那么下邊的這些方法可以用來獲得記錄集行中的數據:

       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);
       int sqlite3_column_count(sqlite3_stmt*);
       const char *sqlite3_column_decltype(sqlite3_stmt *, int iCol);
       const void *sqlite3_column_decltype16(sqlite3_stmt *, int iCol);
       double sqlite3_column_double(sqlite3_stmt*, int iCol);
       int sqlite3_column_int(sqlite3_stmt*, int iCol);
       long long int sqlite3_column_int64(sqlite3_stmt*, int iCol);
       const char *sqlite3_column_name(sqlite3_stmt*, int iCol);
       const void *sqlite3_column_name16(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_column_count()函數返回結果集中包含的列數. sqlite3_column_count() 可以在執行了 sqlite3_prepare()之后的任何時刻調用. sqlite3_data_count()除了必需要在sqlite3_step()之后調用之外,其他跟sqlite3_column_count() 大同小異. 如果調用sqlite3_step() 返回值是 SQLITE_DONE 或者一個錯誤代碼, 則此時調用sqlite3_data_count() 將返回 0 ,然而 sqlite3_column_count() 仍然會返回結果集中包含的列數.

返回的記錄集通過使用其它的幾個 sqlite3_column_***() 函數來提取, 所有的這些函數都把列的編號作為第二個參數. 列編號從左到右以零起始. 請注意它和之前那些從1起始的參數的不同.

sqlite3_column_type()函數返回第N列的值的數據類型. 具體的返回值如下:

       #define SQLITE_INTEGER  1
       #define SQLITE_FLOAT    2
       #define SQLITE_TEXT     3
       #define SQLITE_BLOB     4
       #define SQLITE_NULL     5

sqlite3_column_decltype() 則用來返回該列在 CREATE TABLE 語句中聲明的類型. 它可以用在當返回類型是空字符串的時候. sqlite3_column_name() 返回第N列的字段名. sqlite3_column_bytes() 用來返回 UTF-8 編碼的BLOBs列的字節數或者TEXT字符串的字節數. sqlite3_column_bytes16() 對於BLOBs列返回同樣的結果,但是對於TEXT字符串則按 UTF-16 的編碼來計算字節數. sqlite3_column_blob() 返回 BLOB 數據. sqlite3_column_text() 返回 UTF-8 編碼的 TEXT 數據. sqlite3_column_text16() 返回 UTF-16 編碼的 TEXT 數據. sqlite3_column_int() 以本地主機的整數格式返回一個整數值. sqlite3_column_int64() 返回一個64位的整數. 最后, sqlite3_column_double() 返回浮點數.

不一定非要按照sqlite3_column_type()接口返回的數據類型來獲取數據. 數據類型不同時軟件將自動轉換.

2.3核心對象和接口

 

SQL數據庫引擎的最主要任務是解析SQL語句。為了達成這個目的,開發者需要了解兩個對象:

 

* 數據庫連接對象:sqlite3

* 預處理語句對象:sqlite3_stmt

 

嚴格來講,預處理語句對象並不是必須的,因為能夠使用sqlite_exec或者sqlite3_get_table這些便於使用的封裝接口,而這些接口封裝並隱藏了預處理語句對象。盡管如此,對預處理對象的理解有助於我們更充分的使用SQLite。

 

數據庫連接對象和預處理對象是由下列的一組C/C++接口調用操縱的:

 

* sqlite3_open()

* sqlite3_prepare()

* sqlite3_step()

* sqlite3_column()

* sqlite3_finalize()

* sqlite3_close()

 

這6個C/C++接口例程和上述的兩個對象構成了SQLite的核心功能。開發者對於它們的理解能夠更好的使用SQLite。

 

注意,這個接口例程列表更多是概念上的意義而不是實際的接口。許多這些接口都出現在各個版本之中。例如,上述列表中的sqlite3_open() 例程實際上有三個不同的接口以略微不同的方式實現相同的功能:slqite3_open(),sqlite3_open16()和 sqlite3_open_v2()。列表中的實際上並不存在sqlite3_column()這個接口。顯示在列表中的 “sqlite3_column()”僅僅是一個占位,表示一整套用於從表中查詢出各種數據類型的列記錄接口。

 

這里說明下核心接口的主要功能:

 

* sqlite3_open() 該接口打開與一個SQLite數據庫文件的連接並返回一個數據庫連接對象。這通常是應用程序調用的第一個SQLite API接口而且也是調用其他SQLite API接口前需要調用的接口。許多SQLite接口需要一個指向數據庫連接對象的指針作為它們的第一個參數,因而這些接口也可以理解成是數據庫連接對象的操作接口。該接口就是創建了這樣一個數據庫連接對象。

 

* sqlite3_prepare() 該接口把一個SQL語句文本轉換成一個預處理語句對象並返回一個指向該對象的指針。這個接口需要一個由先前調用sqlite3_open()返回的數據庫連接對象指針以及一個預處理的SQL語句文本字符串為參數。這個API並不實際解析SQL語句,僅僅是為后續的解析而對SQL語句進行的預處理。

 

注意新的應用中不建議使用sqlite3_prepare(),而應該使用另一個接口sqlite3_prepare_v2()。

 

* sqlite3_step() 該接口用於解析一個由先前通過sqlite3_prepare()接口創建的預處理語句,直至返回第一列結果為止。通過再次調用 sqlite3_step()可以返回下一列的結果,繼續不斷地調用sqlite3_step()直至整個語句完成為止。對於那些並不返回結果的語句(例如:INSERT,UPDATE,DELETE語句)一次調用sqlite3_step()就完成了語句的處理。

 

* sqlite3_column() 該接口返回一個由sqlite3_step()解析的預處理語句結果集中當前行的某一列數據。每次執行sqlite3_step()都返回一個新的結果集中的一行。可以多次調用sqlite3_column()接口返回那一行中所有列的數據。就像上面所說的那樣,SQLite API中並沒有sqlite3_column()這樣的接口。取而代之的是一組用於從結果集中查詢出各個列項各種數據類型數據的函數接口。在這組函數接口中,有些接口返回結果集的大小,有些返回結果集的列數。

 

*sqlite3_column_blob()

*sqlite3_column_bytes()

*sqlite3_column_bytes16()

*sqlite3_column_count()

*sqlite3_column_double()

*sqlite3_column_int()

*sqlite3_column_int64()

*sqlite3_column_text()

*sqlite3_column_text16()

*sqlite3_column_type()

*sqlite3_column_value()

 

* sqlite3_finalize() 該接口銷毀之前調用sqlite3_prepare()創建的預處理語句。每一個預處理語句都必須調用這個接口進行銷毀以避免內存泄

漏。

* sqlite3_close() 該接口關閉一個由之前調用sqlite3_open()創建的數據庫連接。所有與該連接相關的預處理語句都必須在關閉連接之前銷毀。

2.4擴展SQLite

 

SQLite包含一些可用於擴展其功能的一些其他接口,這些接口包括:

 

* sqlite3_create_collation()

* sqlite3_create_function()

* sqlite3_create_module()

 

sqlite3_create_collation()接口用於為索引文本創建新的對照序列。sqlite3_create_module()接口用於注冊新的續表實現接口。

 

sqlite3_create_function()接口創建新的SQL函數-即可以是單一的也可以是組合的接口。新的函數實現通常利用下列的輔助接口:

 

* sqlite3_aggregate_context()

* sqlite3_result()

* sqlite3_user_data()

* sqlite3_value()

 

SQLite中所有內建的SQL函數接口也是通過這些相同的接口實現的。查看SQLite源代碼,尤其是date.c和func.c兩個文件就有許多這方面的例子。

 

四、在VC下使用SQLite

#include "../sqlite3_lib/sqlite3.h"

 

#pragma comment(lib, "../sqlite3_lib/sqlite3.lib")

 

static int _sql_callback(void * notused, int argc, char ** argv, char ** szColName)

{

    int i;

    for ( i=0; i < argc; i++ )

    {

        printf( "%s = %s/n", szColName[i], argv[i] == 0 ? "NUL" : argv[i] );

    }

      

    return 0;

}

 

int main(int argc, char * argv[])

{

    const char * sSQL1 = "create table users(userid varchar(20) PRIMARY KEY, age int, birthday datetime);";

    const char * sSQL2 = "insert into users values('wang',20,'1989-5-4');";

    const char * sSQL3 = "select * from users;";

 

    sqlite3 * db = 0;

    char * pErrMsg = 0;

    int ret = 0;

   

    // 連接數據庫

    ret = sqlite3_open("./test.db", &db);

      

    if ( ret != SQLITE_OK )

    {

        fprintf(stderr, "無法打開數據庫: %s", sqlite3_errmsg(db));

        return(1);

    }

      

    printf("數據庫連接成功!/n");

      

    // 執行建表SQL

 

    sqlite3_exec( db, sSQL1, 0, 0, &pErrMsg );

    if ( ret != SQLITE_OK )

    {

        fprintf(stderr, "SQL error: %s/n", pErrMsg);

        sqlite3_free(pErrMsg);

    }

      

    // 執行插入記錄SQL

    sqlite3_exec( db, sSQL2, 0, 0, &pErrMsg);

 

    // 查詢數據表

    sqlite3_exec( db, sSQL3, _sql_callback, 0, &pErrMsg);

 

    // 關閉數據庫

    sqlite3_close(db);

    db = 0;

      

    return 0;

}

 

使用事務

 
    在上面的例子中,我們向數據庫里插入了10條數據,然后再從數據庫里讀出來。細心的你不知道有沒有發現,在執行Insert的時候
,並沒有使用同一個事 務。在很多情況下,我們需要使用事務來保證對數據庫操作的原子性。Sqlite是支持事務的,而且對事務的使用非常簡單:
使用sql語句”begin;” 表示事務開始,”rollback;”表示事務的回滾,”commit;”表示事務的提交。下面我們對上面例子中的代碼作一下修改,
給Insert操作添 加事務支持:
 
//... ...


bool is_succed = true;
sqlite3_exec(conn, "begin;", 0, 0, 0);  //

開啟事務
//添加10條記錄
for (int i = 0; i < 10; i++)
{
    // 執行SQL
    sprintf(sql, "INSERT INTO [test_for_cpp] ([id], [name], [age]) /
        VALUES (%d, '%s', %d)", i, "JGood", i);
    if (SQLITE_OK != sqlite3_exec(conn, sql, 0, 0, &err_msg))
    {
        is_succed = false;
        break;
    }
}

if (is_succed)
    sqlite3_exec(conn, "commit;", 0, 0, 0);  // 提交事務
else
    sqlite3_exec(conn, "rollback;", 0, 0, 0);  // 回滾事務

//... ...

 

使用sql參數

 
    基本上,使用sqlite3_open, sqlite3_close, sqlite3_exec這三個函數,
可以完成大大部分的工作。但還不完善。上面的例子中,都是直接以sql語句的形式來操作數據庫,這樣很容易被注入。所以有必要使用sql參數。
 
sqlite3_prepare
 
sqlite3_bind_*
 
sqlite3_step
 
sqlite3_column_*
 
struct sqlite3_stmt
 
sqlite3_finalize
 
    sqlite3_prepare用來編譯sql語句。sql語句被執行之前,
必須先編譯成字節碼。sqlite3_stmt是一個結構體,表示sql語句 編譯后的字節碼。sqlite3_step用來執行編譯后的sql語句。
sqlite3_bind_*用於將sql參數綁定到sql語句。 sqlite3_column_*用於從查詢的結果中獲取數據。sqlite3_finalize用來釋放sqlite3_stmt對象。代碼最能說明函 數的功能,
下面就用一個例子來演示吧~~
 
// ----------------------------------------------
// http://blog.csdn.net/JGood
 
// sqlite3_prepare, sqlite3_bind_*, sqlite3_step, sqlite3_column_*, sqlite3_column_type
// sqlite3_stmt, sqlite3_finalize, sqlite3_reset
// 查詢
// ----------------------------------------------
sqlite3 *conn = NULL;
sqlite3_stmt *stmt = NULL;
const char *err_msg = NULL;
// 列數據類型
char col_types[][10] = { "", "Integer", "Float", "Text", "Blob", "NULL" };

sqlite3_open("test.db", &conn);
sqlite3_prepare(conn, "SELECT * FROM [test_for_cpp] WHERE [id]>?", -1, &stmt, &err_msg);
sqlite3_bind_int(stmt, 1, 5);

while (SQLITE_ROW == sqlite3_step(stmt))
{
    int col_count = sqlite3_column_count(stmt); // 結果集中列的數量

    const char *col_0_name = sqlite3_column_name(stmt, 0); // 獲取列名
    int id = sqlite3_column_int(stmt, 0);
    int id_type = sqlite3_column_type(stmt, 0); // 獲取列數據類型

    const char *col_2_name = sqlite3_column_name(stmt, 2);
    int age = sqlite3_column_int(stmt, 2);
    int age_type = sqlite3_column_type(stmt, 2);

    const char *col_1_name = sqlite3_column_name(stmt, 1);
    char name[80];
    strncpy(name, (const char *)sqlite3_column_text(stmt, 1), 80);
    int name_type = sqlite3_column_type(stmt, 1);

    // 打印結果
    printf("col_count: %d, %s = %d(%s), %s = %s(%s), %s = %d(%s)/n",
        col_count, col_0_name, id, col_types[id_type], col_2_name, name,
        col_types[name_type], col_1_name, age, col_types[age_type]);
}

sqlite3_finalize(stmt); // 釋放sqlite3_stmt
sqlite3_close(conn);
這段代碼查詢id號大於5的所有記錄.
其他函數
 在上面的例子中,還使用了其他的一些函數,
如:sqlite3_column_count用於獲取結果集中列的數 量;sqlite3_column_name用於獲取列的名稱;
sqlite3_column_type用於獲取列的數據類 型;sqlite3_errcode用於獲取最近一次操作出錯的錯誤代碼;
sqlite3_errmsg用於獲取最近一次操作出錯的錯誤說明。 sqlite的api中還有很多的函數,有了上面的基礎,

相信你通過查詢官方的文檔,能迅速掌握本文未介紹的api。

 

 

五、數據庫管理工具

sqlite 管理工具 Sqlite Developer

Sqlite Developer是SharpPlus出品的一款強大數據庫管理軟件。支持對sqlite3數據庫的管理。

下載:http://dl.pconline.com.cn/download/60827.html

 Sqlite Developer 注冊碼:http://download.csdn.net/source/2755962

 

 

常見問題:

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

2、  如何通過sqlite3.dll與sqlite3.def生成sqlite3.lib文件
LIB /DEF:sqlite3.def /machine:IX86

3、  如何查詢當前的編碼的編碼格式
pragma  encoding;

4、  如果刪除了大量數據,而又想縮小數據庫文件占用的空間,執行 VACUUM 命令
vacuum;

5、  sqlite3_open打開一個數據庫時,如果數據庫不存在就會新生成一個數據庫文件。如果接着執行其他查詢語句就會失敗,比如sqlite3_prepare,編程中出現明明指定了數據庫而且里面也有數據,為什么查詢失敗了,主要是數據庫名路徑不對引起的。一般的做法是先檢查數據庫文件是否存在,如果存在就使用sqlite3_open打開數據庫;否則創建一個新的數據庫。

6、  如何建立自動增長字段

聲明為 INTEGER PRIMARY KEY 的列將會自動增長。

7、SQLite3支持何種數據類型?

NULL

INTEGER

REAL

TEXT

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 包含了 年、月、日、時、分、秒、千分之一秒。

8、SQLite允許向一個integer型字段中插入字符串

這是一個特性,而不是一個bug。SQLite不強制數據類型約束。任何數據都可以插入任何列。你可以向一個整型列中插入任意長度的字符串,向布爾型列中插入浮點數,或者向字符型列中插入日期型值。 在 CREATE TABLE 中所指定的數據類型不會限制在該列中插入任何數據。任何列均可接受任意長度的字符串(只有一種情況除外: 標志為INTEGER PRIMARY KEY的列只能存儲64位整數,當向這種列中插數據除整數以外的數據時,將會產生錯誤。

 

但SQLite確實使用聲明的列類型來指示你所期望的格式。所以,例如你向一個整型列中插入字符串時,SQLite會試圖將該字符串轉換成一個整數。如果可以轉換,它將插入該整數;否則,將插入字符串。這種特性有時被稱為 類型或列親和性(type or column affinity).

9、為什么SQLite不允許在同一個表不同的兩行上使用0和0.0作主鍵?

主鍵必須是數值類型,將主鍵改為TEXT型將不起作用。

每一行必須有一個唯一的主鍵。對於一個數值型列, SQLite認為 '0' 和 '0.0' 是相同的, 因為他們在作為整數比較時是相等的(參見上一問題)。 所以,這樣值就不唯一了。

10、多個應用程序或一個應用程序的多個實例可以同時訪問同一個數據庫文件嗎?

 

多個進程可同時打開同一個數據庫。多個進程可以同時進行SELECT 操作,但在任一時刻,只能有一個進程對數據庫進行更改。

 

SQLite 使用讀、寫鎖控制對數據庫的訪問。(在Win95/98/ME等不支持讀、寫鎖的系統下,使用一個概率性的模擬來代替。)但使用時要注意:如果數據庫文件存放於一個NFS文件系統上,這種鎖機制可能不能正常工作。 這是因為 fcntl() 文件鎖在很多NFS上沒有正確的實現。在可能有多個進程同時訪問數據庫的時候,應該避免將數據庫文件放到NFS上。在Windows上,Microsoft的文檔中說:如果使用 FAT 文件系統而沒有運行 share.exe 守護進程,那么鎖可能是不能正常使用的。那些在Windows上有很多經驗的人告訴我:對於網絡文件,文件鎖的實現有好多Bug,是靠不住的。如果他們說的是對的,那么在兩台或多台Windows機器間共享數據庫可能會引起不期望的問題。

 

我們意識到,沒有其它嵌入式的 SQL 數據庫引擎能象 SQLite 這樣處理如此多的並發。SQLite允許多個進程同時打開一個數據庫,同時讀一個數據庫。當有任何進程想要寫時,它必須在更新過程中鎖住數據庫文件。但那通常只是幾毫秒的時間。其它進程只需等待寫進程干完活結束。典型地,其它嵌入式的SQL數據庫引擎同時只允許一個進程連接到數據庫。

 

但是,Client/Server數據庫引擎(如 PostgreSQL, MySQL, 或 Oracle)通常支持更高級別的並發,並且允許多個進程同時寫同一個數據庫。這種機制在Client/Server結構的數據庫上是可能的,因為總是有一個單一的服務器進程很好地控制、協調對數據庫的訪問。如果你的應用程序需要很多的並發,那么你應該考慮使用一個Client/Server 結構的數據庫。但經驗表明,很多應用程序需要的並發,往往比其設計者所想象的少得多。

 

當SQLite試圖訪問一個被其它進程鎖住的文件時,缺省的行為是返回 SQLITE_BUSY。 可以在C代碼中使用 sqlite3_busy_handler() 或 sqlite3_busy_timeout() API 函數調整這一行為。

11、SQLite線程安全嗎?

線程是魔鬼(Threads are evil)。 避免使用它們。

SQLite 是線程安全的。由於很多用戶會忽略我們在上一段中給出的建議, 我們做出了這種讓步。但是,為了達到線程安全,SQLite在編譯時必須將 SQLITE_THREADSAFE 預處理宏置為1。在Windows和Linux上,已編譯的好的二進制發行版中都是這樣設置的。如果不確定你所使用的庫是否是線程安全的,可以調用 sqlite3_threadsafe() 接口找出。

12、在SQLite數據庫中如何列出所有的表和索引?

如果你運行 sqlite3 命令行來訪問你的數據庫,可以鍵入 “.tables”來獲得所有表的列表。或者,你可以輸入 “.schema” 來看整個數據庫模式,包括所有的表的索引。 輸入這些命令,后面跟一個LIKE模式匹配可以限制顯示的表。

13、SQLite數據庫有已知的大小限制嗎?

在 Windows 和 Unix 下,版本 2.7.4的 SQLite 可以達到 2的41次方字節 (2T 字節)。老版本的為 2的31 次方字節(2G 字節)。

SQLite 版本 2.8 限制一個記錄的容量為 1M。SQLite 版本 3.0 則對單個記錄容量沒有限制。

表名、索引表名、視圖名、觸發器名和字段名沒有長度限制。但 SQL 函數的名稱 (由 sqlite3_create_function() API 函數創建) 不得超過 255 個字符。

14、在SQLite中,VARCHAR字段最長是多少?

SQLite 不強制 VARCHAR 的長度。 你可以在 SQLITE 中聲明一個 VARCHAR(10),SQLite還是可以很高興地允許你放入500個字符。 並且這500個字符是原封不動的,它永遠不會被截斷。

15、在SQLite中,如何在一個表上添加或刪除一列?

SQLite 有有限地 ALTER TABLE 支持。你可以使用它來在表的末尾增加一列,可更改表的名稱。 如果需要對表結構做更復雜的改變,則必須重新建表。 重建時可以先將已存在的數據放到一個臨時表中,刪除原表, 創建新表,然后將數據從臨時表中復制回來。

 

如,假設有一個 t1 表,其中有 "a", "b", "c" 三列, 如果要刪除列 c ,以下過程描述如何做:

 

BEGIN TRANSACTION;

CREATE TEMPORARY TABLE t1_backup(a,b);

INSERT INTO t1_backup SELECT a,b FROM t1;

DROP TABLE t1;

CREATE TABLE t1(a,b);

INSERT INTO t1 SELECT a,b FROM t1_backup;

DROP TABLE t1_backup;

COMMIT;

 

16、如何在Windows下編譯SQLite3
1)先從官網獲取sqlite-amalgamation-3_6_22.zip;

2)接着從官網獲取sqlitedll-3_6_22.zip

下載編譯好的DLL,這里有我們需要的sqlite3.def該文件

3)打開VC新建一個“Win32 Dynamic-Link Library”工程,命名為:sqlite3;

4)在接下來的對話框中選擇"An empty DLL project",點 FINISH->OK;

5)將sqlite-amalgamation-3_6_22文件夾下的sqlite3.h以及sqlite3.c兩個文件復制到工程文件夾下;

       將sqlitedll-3_6_22文件夾下的sqlite3.def該文件復制到工程文件夾下;

6)在工程的Source Files中添加sqlite3.c文件;

7)在工程的Include Files中添加sqlite3.h文件;

8)在工程的Resource Files中添加sqlite3.def文件;

9)針對如下問題:

sqlite3.def : error LNK2001: unresolved external symbol sqlite3_column_database_name

sqlite3.def : error LNK2001: unresolved external symbol sqlite3_column_database_name16

sqlite3.def : error LNK2001: unresolved external symbol sqlite3_column_origin_name

sqlite3.def : error LNK2001: unresolved external symbol sqlite3_column_origin_name16

sqlite3.def : error LNK2001: unresolved external symbol sqlite3_column_table_name

sqlite3.def : error LNK2001: unresolved external symbol sqlite3_column_table_name16

sqlite3.def : error LNK2001: unresolved external symbol sqlite3_table_column_metadata

Debug/sqlite3.lib : fatal error LNK1120: 7 unresolved externals

在菜單【Project】-【Settings…】-【C/C++】標簽下的Category【General】下

Preprocessor definitions下:

新增2個編譯選項,分別是:

THREADSAFE

SQLITE_ENABLE_COLUMN_METADATA

10)往工程中添加sqlite3.def文件就是為生成sqlite3.lib文件;

sqlite3.lib是與sqlite3.dll動態鏈接庫文件一起提供給軟件開發者的;

11)解決Windows下的編譯警告
e:/sqlite/code/sqlite3/sqlite3.c(15385) : warning C4047: 'function' : 'void ** ' differs in levels of indirection from 'long *'

e:/sqlite/code/sqlite3/sqlite3.c(15385) : warning C4022: 'InterlockedCompareExchange' : pointer mismatch for actual parameter 1

e:/sqlite/code/sqlite3/sqlite3.c(15385) : warning C4022: 'InterlockedCompareExchange' : pointer mismatch for actual parameter 2

e:/sqlite/code/sqlite3/sqlite3.c(15403) : warning C4047: 'function' : 'void ** ' differs in levels of indirection from 'long *'

e:/sqlite/code/sqlite3/sqlite3.c(15403) : warning C4022: 'InterlockedCompareExchange' : pointer mismatch for actual parameter 1

e:/sqlite/code/sqlite3/sqlite3.c(15403) : warning C4022: 'InterlockedCompareExchange' : pointer mismatch for actual parameter 3

e:/sqlite/code/sqlite3/sqlite3.c(15403) : warning C4047: '==' : 'void *' differs in levels of indirection from 'const int '

e:/sqlite/code/sqlite3/sqlite3.c(65536) : warning C4049: compiler limit : terminating line number emission

warning C4761: integral size mismatch in argument; conversion supplied

warning C4761: integral size mismatch in argument; conversion supplied

warning C4761: integral size mismatch in argument; conversion supplied

 

VC2005 + SQLite 3.6.3 編譯、測試開發

http://blog.csdn.net/Dreamcode/archive/2009/08/21/4467277.aspx

 

參考資料:

sqlite 中文社區:http://www.sqlite.com.cn/

sqlite 官方網站:http://www.sqlite.org/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM