sqlite3 數據庫使用

 

參考

  1. SQLite3 使用教學
  2. SQLite 簡單教程

簡介

特色

  • 軟件屬於公共財(public domain),SQLite可說是某種「美德軟件」 (virtueware),做者本人放棄着做權,而給使用SQLite的人如下的「祝福」 (blessing):
    • May you do good and not evil. 願你行善莫行惡
    • May you find forgiveness for yourself and forgive others. 願你原諒 本身寬恕他人
    • May you share freely, never taking more than you give. 願你寬心與 人分享,所取很少於你所施予
  • 支援大多數的SQL指令(下面會簡單介紹)。
  • 一個文件就是一個數據庫。不須要安裝數據庫服務器軟件。
  • 完整的Unicode支援(所以沒有跨語系的問題)。
  • 速度很快。

SQLite顧名思議是以SQL爲基礎的數據庫軟件,SQL是一套強大的數據庫語言,主 要概念是由「數據庫」、「資料表」(table)、「查詢指令」(queries)等單 元組成的「關聯性數據庫」(進一步的概念可參考網絡上各類關於SQL及關聯性數 據庫的文件)。由於SQL的查詢功能強大,語法一致而入門容易,所以成爲現今主 流數據庫的標準語言(微軟、Oracle等大廠的數據庫軟件都提供SQL語法的查詢及 操做)。shell

Linux 下命令行操做

下面的命令都是在 Linux 下運行過,基於 Debian 5.0,其餘 Linux 一般也沒有 問題。其中 SQL 命令一般是不依賴於任何平臺的。數據庫

建立數據庫

建立數據庫就是建立一個 SQLite 文件。編程

# sqlite3 test.db  # 當前目錄下若是有 test.db ,那麼就讀取這個文件,不然建立。
SQLite version 3.5.9
Enter ".help" for instructions
sqlite> create table mytable(name varchar (40), age smallint);
sqlite> insert into mytable values ('Jian Lee',23);
sqlite> select * from mytable;
Jian Lee|23

上面簡單使用幾個 SQL 命令建立一個表,並插入一條記錄,最後查看建立的表內 容。使用 .help 能夠列出幫助信息。 .quit 能夠退出。數組

SQL 指令簡介

全部的 SQL 指令都以 ";" 號結尾。 」- -「 表示註釋。服務器

建立 table,插入記錄

sqlite> create table 數據庫 (名字, 嵌入式);
sqlite> insert into 數據庫 values ('PostgreSQL','否');
sqlite> insert into 數據庫 values ('MySQL','否');
sqlite> insert into 數據庫 values ('SQLite','是');
ssqlite> select * from 數據庫;
PostgreSQL|否
MySQL|否
SQLite|是

insert into 命令中不存在的字段能夠用 NULL 代替。網絡

瞧,優秀的程序設計的多麼好!任何」字符串「都只是一個」標籤「,因此都能完美支持!數據結構

創建索引

建立索引能夠加快訪問速度。

sqlite> create index 數據庫名字索引 on 數據庫 (名字);

查詢

更新或修改/刪除

更新
sqlite> select * from 數據庫;
PostgreSQL|否
MySQL|否
SQLite|是
sqlite> update 數據庫 set 名字='SQLite3' where 名字='SQLite';
sqlite> select * from 數據庫;
PostgreSQL|否
MySQL|否
SQLite3|是
刪除
sqlite> select * from 數據庫;
PostgreSQL|否
MySQL|否
SQLite3|是
sqlite> delete from 數據庫 where 嵌入式='是';
sqlite> select * from 數據庫;
PostgreSQL|否
MySQL|否

SQLite 的特別用法

修改 sqlite3 的默認分隔符(|)

sqlite> .separator "-"

shell 下訪問

# sqlite3 test.db "select * from 數據庫;"
PostgreSQL|否
MySQL|否

若是要執行多條 sql 語句,能夠先生成這樣的sql語句文件,好比 「sql.txt」:

.output Somefile    // 這句話告訴sqlite把sql執行的結果輸出到文件
create tab ...        // 正常的sql語句
insert into ....
.quit                // 告訴sqlite退出
而後, 「cat sql.txt sqlite data.db」。sqlite就會執行sql.txt中的sql語

句,並把結果保存到Somefile中。

輸出 HTML 表格

root@jianlee:~/lab/xml/crepo/test# sqlite3 -html test.db "select * from 數據庫;"
<TR><TD>PostgreSQL</TD>
<TD>否</TD>
</TR>
<TR><TD>MySQL</TD>
<TD>否</TD>
</TR>

將數據庫倒出來(輸出 SQL 指令)

root@jianlee:~/lab/xml/crepo/test# sqlite3 test.db ".dump"  > test.sql
root@jianlee:~/lab/xml/crepo/test# cat test.sql
BEGIN TRANSACTION;
CREATE TABLE mytable(name varchar (40), age smallint);
INSERT INTO "mytable" VALUES('Jian Lee',23);
CREATE TABLE 操做系統 (title, length, year, starring);
INSERT INTO "操做系統" VALUES('Jian Lee',23,33,45);
CREATE TABLE 數據庫 (名字, 嵌入式);
INSERT INTO "數據庫" VALUES('PostgreSQL','否');
INSERT INTO "數據庫" VALUES('MySQL','否');
CREATE INDEX 數據庫名字索引 on 數據庫 (名字);
COMMIT;

重建數據庫(用上面倒出的 SQL 語句)

root@jianlee:~/lab/xml/crepo/test# sqlite3 重建數據庫.db < test.sql
root@jianlee:~/lab/xml/crepo/test# sqlite3 重建數據庫.db "select * from 數據庫;"
PostgreSQL|否
MySQL|否

在大量插入資料時,你可能會須要先打這個指令:

begin;

插入完資料後要記得打這個指令,資料纔會寫進數據庫中:

commit;

begin; 和 commit; 是 SQL 處理一個事務的語法。

數據表結構

SQLite 數據庫的數據結構保存在 "sqlite_master" 表中,下例針對 rpm 的 yum 源裏的 repodata 目錄中的 other.sqlite.bz2 數據庫進行查詢:

# sqlite3 other.sqlite
SQLite version 3.5.9
Enter ".help" for instructions
sqlite> select * from sqlite_master;
table|db_info|db_info|2|CREATE TABLE db_info (dbversion INTEGER, checksum TEXT)
table|packages|packages|3|CREATE TABLE packages (  pkgKey INTEGER PRIMARY KEY,  pkgId TEXT)
table|changelog|changelog|4|CREATE TABLE changelog (  pkgKey INTEGER,  author TEXT,  date INTEGER,  changelog TEXT)
index|keychange|changelog|5|CREATE INDEX keychange ON changelog (pkgKey)
index|pkgId|packages|6|CREATE INDEX pkgId ON packages (pkgId)
trigger|remove_changelogs|packages|0|CREATE TRIGGER remove_changelogs AFTER DELETE ON packages  BEGIN    DELETE FROM changelog WHERE pkgKey = old.pkgKey;  END sqlite>

可是你不可以對sqlite_master 表執行 DROP TABLE, UPDATE, INSERT or DELETE ,當你建立或者刪除表的時候,sqlite_master 表會自動更新。你不能手 工改變 sqlite_master 表。

臨時表的結構不會存貯到 sqlite_master 表中,臨時表是存貯在另外一個特殊的 表,叫作 "sqlite_temp_master"。"sqlite_temp_master" 表自己就是臨時的。

將結果寫到文件中

取自參考資料2的示例,我如今還用不到:

默認狀況下,sqlite3會將結果發送到標準輸出,你可使用 ".output" 來改 變,只是將輸出到的文件名做爲參數傳遞給 .output,全部後面的查詢結果都會 寫到文件裏。開頭使用 ".output stdout" 會再次寫到標準輸出,例如:

    sqlite> .mode list
    sqlite> .separator |
    sqlite> .output test_file_1.txt
    sqlite> select * from tbl1;
    sqlite> .exit
    $ cat test_file_1.txt
    hello|10
    goodbye|20
    $

查詢數據庫結構

列出全部數據表 (.tables)
sqlite> .tables
changelog  db_info    packages
sqlite>

".tables" 命令和一下的查詢類似:

    SELECT name FROM sqlite_master
    WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%'
    UNION ALL
    SELECT name FROM sqlite_temp_master
    WHERE type IN ('table','view')
    ORDER BY 1
列出特殊表的索引 (.indices)

".indices" 命令以類似的方法列出一個特殊表的索引。".indices" 命令以一個 數據表的名字做爲參數。

.schema

不帶參數的 .schema 命令會顯示用來建立數據庫的 「CREATE TABLE and CREATE INDEX」 語句,若是你給一個表名爲參數,他會顯示用來建立表和索引(若是有的 話)的 CREATE 語句。

sqlite> .schema
CREATE TABLE changelog (  pkgKey INTEGER,  author TEXT,  date INTEGER,  changelog TEXT);
CREATE TABLE db_info (dbversion INTEGER, checksum TEXT);
CREATE TABLE packages (  pkgKey INTEGER PRIMARY KEY,  pkgId TEXT);
CREATE INDEX keychange ON changelog (pkgKey);
CREATE INDEX pkgId ON packages (pkgId);
CREATE TRIGGER remove_changelogs AFTER DELETE ON packages  BEGIN    DELETE FROM changelog WHERE pkgKey = old.pkgKey;  END;
schema>
sqlite> .schema changelog
CREATE TABLE changelog (  pkgKey INTEGER,  author TEXT,  date INTEGER,  changelog TEXT);
CREATE INDEX keychange ON changelog (pkgKey);

經常使用命令

.databases 列出數據庫文件名
.tables ?PATTERN? 列出?PATTERN?匹配的表名
.import FILE TABLE 將文件中的數據導入的文件中
.dump ?TABLE? 生成造成數據庫表的SQL腳本
.output FILENAME 將輸出導入到指定的文件中
.output stdout 將輸出打印到屏幕
.mode MODE ?TABLE?     設置數據輸出模式(csv,html,tcl…
.nullvalue STRING 用指定的串代替輸出的NULL串
.read FILENAME 執行指定文件中的SQL語句
.schema ?TABLE? 打印建立數據庫表的SQL語句
.separator STRING 用指定的字符串代替字段分隔符,這個頗有用!
.show 打印全部SQLite環境變量的設置
.quit 退出命令行接口

SQLite 結構分析

每個 SQLite 數據庫都有一個叫 SQLITE_MASTER 的表, 它定義數據庫的模式。 SQLITE_MASTER 表看起來以下:

CREATE TABLE sqlite_master (
      type TEXT,
      name TEXT,
      tbl_name TEXT,
      rootpage INTEGER,
      sql TEXT
);

對於表來講,type 字段永遠是 'table',name 字段永遠是表的名字。因此,要 得到數據庫中全部表的列表, 使用下列SELECT語句:

SELECT name FROM sqlite_master
WHERE type='table'
ORDER BY name;

對於索引,type 等於 'index', name 則是索引的名字,tbl_name 是該索引所屬 的表的名字。 不論是表仍是索引,sql 字段是原先用 CREATE TABLE 或 CREATE INDEX 語句建立它們時的命令文本。對於自動建立的索引(用來實現 PRIMARY KEY 或 UNIQUE 約束),sql字段爲NULL。

SQLITE_MASTER 表是隻讀的。不能對它使用 UPDATE、INSERT 或 DELETE。 它會 被 CREATE TABLE、CREATE INDEX、DROP TABLE 和 DROP INDEX 命令自動更新。

臨時表不會出如今 SQLITE_MASTER 表中。臨時表及其索引和觸發器存放在另一 個叫 SQLITE_TEMP_MASTER 的表中。SQLITE_TEMP_MASTER 跟 SQLITE_MASTER 差 很少,但它只是對於建立那些臨時表的應用可見。若是要得到全部表的列表, 不 管是永久的仍是臨時的,可使用相似下面的命令:

SELECT name FROM
    (SELECT * FROM sqlite_master UNION ALL
     SELECT * FROM sqlite_temp_master)
WHERE type='table'
    ORDER BY name

觸發器

假設"customers"表存儲了客戶信息,"orders"表存儲了訂單信息,下面的觸發器 確保當用戶改變地址時全部的 關聯訂單地址均進行相應改變:

CREATE TRIGGER update_customer_address UPDATE OF address ON customers
  BEGIN
    UPDATE orders SET address = new.address WHERE customer_name = old.name;
  END;

定義了該觸發器後執行以下語句:

UPDATE customers SET address = '1 Main St.' WHERE name = 'Jack Jones';

會使下面的語句自動執行:

UPDATE orders SET address = '1 Main St.' WHERE customer_name = 'Jack Jones';

注意,目前在有INTEGER PRIMARY KEY域的表上觸發器可能工做不正常。若 BEFORE觸發器修改了一行的 INTEGER PRIMARY KEY域,而該域將由觸發該觸發器 的語句進行修改,則可能根本不會修改該域。 能夠用PRIMARY KEY字段代替 INTEGER PRIMARY KEY字段來解決上述問題。

再舉 1 例,在 primary.sqlite.bz2 數據庫中:

CREATE TRIGGER removals AFTER DELETE ON packages
  BEGIN
    DELETE FROM files WHERE pkgKey = old.pkgKey;
    DELETE FROM requires WHERE pkgKey = old.pkgKey;
    DELETE FROM provides WHERE pkgKey = old.pkgKey;
    DELETE FROM conflicts WHERE pkgKey = old.pkgKey;
    DELETE FROM obsoletes WHERE pkgKey = old.pkgKey;
  END;

SQLite 的 C 語言編程

執行sql語句

int
sqlite3_exec ( sqlite3 *db,  // 使用 sqlite3_open () 打開的數據庫對象。
               const char *sql, // 一條待查詢的 SQL 語句
               sqlite3_callback, // 自定義的回調函數,對查詢結果每一行都執行一次這個函數
               void *,
               char **errmsg
);
<example>

這是最經常使用的執行 sql 語句的調用。簡單的參數含意標在上面函數中,下面對重
要參數含意詳細註釋:

 - 第 4 個參數 "void *" 是調用者所提供的指針,能夠傳遞任何一個指針參數到
   這裏,這個參數最終會傳到回調函數裏面,這個指針比較重要,能夠用來做參
   數的傳遞。若是不須要傳遞指針給回調函數,能夠填NULL。等下咱們再看回調
   函數的寫法,以及這個參數的使用。

 - 第 5 個參數 "char ** errmsg" 是錯誤信息。注意是指針的指針。sqlite3裏
   面有不少固定的錯誤信息。執行 sqlite3_exec 以後,執行失敗時能夠查閱這
   個指針(直接 printf(「%s\n」,errmsg))獲得一串字符串信息,這串信息告訴
   你錯在什麼地方。sqlite3_exec函數經過修改你傳入的指針的指針,把你提供
   的指針指向錯誤提示信息,這樣sqlite3_exec函數外面就能夠經過這個
   char*獲得具體錯誤提示。

說明:一般, sqlite3_callback 和它後面的 void * 這兩個位置均可以填
NULL。填NULL表示你不須要回調。好比你作 insert 操做,作 delete 操做,就
沒有必要使用回調。而當你作 select 時,就要使用回調,由於 sqlite3 把數據
查出來,得經過回調告訴你查出了什麼數據。

** exec 的回調函數
<example>
typedef int
(*sqlite3_callback) (void *, // 這就是上面函數傳遞的 void * 參數,須要強制類型轉換後才能使用。
                    int, // 查詢結果的列數,即有多少個字段數
                    char **, // 保存查詢結果
                    char **  // 各個字段的名字
);

回調函數必須定義成上面這個函數的類型。下面給個簡單的例子:

不使用回調查詢數據庫

上面介紹的 sqlite3_exec 是使用回調來執行 select 操做。還有一個方法能夠 直接查詢而不須要回調。雖然回調顯得代碼整齊,但有時候你仍是想要非回調的 select 查詢。這能夠經過 sqlite3_get_table 函數作到。

int
sqlite3_get_table (sqlite3 *,   // 打開的數據庫對象指針
                   const char * sql, // 要查詢的 sql 語句
                   char *** resultp, // 查詢結果
                   int * nrow,   // 查詢出多少條記錄(即查出多少行)
                   int * ncolumn, // 多少個字段(多少列)
                   char ** errmsg  // 錯誤信息
);

第3個參數是查詢結果,它依然一維數組(不要覺得是二維數組,更不要覺得是三 維數組)。它內存佈局是:第一行是字段名稱,後面是緊接着是每一個字段的值。 下面用例子來講事。

下面給個簡單例子:

int main( int , char ** )
{
          sqlite3 * db;
          int result;
          char * errmsg = NULL;
          char ** dbResult; //是 char ** 類型,兩個*號
          int nRow, nColumn;
          int i , j;
          int index;

          result = sqlite3_open( 「c:\\Dcg_database.db」, &db );

         if( result != SQLITE_OK )
        {
               //數據庫打開失敗
               return -1;
        }

       //數據庫操做代碼
       //假設前面已經建立了 MyTable_1 表
       //開始查詢,傳入的 dbResult 已是 char **,這裏又加了一個 & 取地址符,傳遞進去的就成了 char ***
       result = sqlite3_get_table( db, 「select * from MyTable_1」, &dbResult, &nRow, &nColumn, &errmsg );
       if( SQLITE_OK == result )
       {
           //查詢成功
          index = nColumn; //前面說過 dbResult 前面第一行數據是字段名稱,從 nColumn 索引開始纔是真正的數據
     printf( 「查到%d條記錄\n」, nRow );

     for(  i = 0; i < nRow ; i++ )
     {
         printf( 「第 %d 條記錄\n」, i+1 );
         for( j = 0 ; j < nColumn; j++ )
         {
              printf( 「字段名:%s  ß> 字段值:%s\n」,  dbResult[j], dbResult [index] );
              ++index; // dbResult 的字段值是連續的,從第0索引到第 nColumn - 1索引都是字段名稱,從第 nColumn 索引開始,後面都是字段值,它把一個二維的表(傳統的行列表示法)用一個扁平的形式來表示
         }
         printf( 「-------\n」 );
     }
}

     //到這裏,不論數據庫查詢是否成功,都釋放 char** 查詢結果,使用 sqlite 提供的功能來釋放
     sqlite3_free_table( dbResult );

     //關閉數據庫
     sqlite3_close( db );
     return 0;
}

操做二進制

sqlite 操做二進制數據須要用一個輔助的數據類型:sqlite3_stmt * 。這個數 據類型記錄了一個「sql語句」。爲何我把 「sql語句」 用雙引號引發來?由於你 能夠把 sqlite3_stmt * 所表示的內容當作是 sql語句,可是實際上它不是咱們 所熟知的sql語句。它是一個已經把sql語句解析了的、用sqlite本身標記記錄的 內部數據結構。正由於這個結構已經被解析了,因此你能夠往這個語句裏插入二 進制數據。固然,把二進制數據插到 sqlite3_stmt 結構裏可不能直接 memcpy ,也不能像 std::string 那樣用 + 號。必須用 sqlite 提供的函數來插入。

寫入二進制

要插入二進制,前提是這個表的字段的類型是 blob 類型。假設有這麼一張表:

create table Tbl_2( ID integer, file_content  blob )

首先聲明

sqlite3_stmt * stat;

而後,把一個 sql 語句解析到 stat 結構裏去:

sqlite3_prepare( db, 「insert into Tbl_2( ID, file_content) values( 10, ? )」, -1, &stat, 0 );

上面的函數完成 sql 語句的解析。

  • 第一個參數跟前面同樣,是個 sqlite3 * 類型變量
  • 第二個參數是一個 sql 語句。這個 sql 語句特別之處在於 values 裏面有個 ? 號。在sqlite3_prepare函數裏,?號表示一個未定的值,它的值等下才插入。
  • 第三個參數我寫的是-1,這個參數含義是前面 sql 語句的長度。若是小於 0,sqlite會自動計算它的長度(把sql語句當成以\0結尾的字符串)。
  • 第四個參數是 sqlite3_stmt 的指針的指針。解析之後的sql語句就放在這個結構裏。
  • 第五個參數我也不知道是幹什麼的。爲0就能夠了。

若是這個函數執行成功(返回值是 SQLITE_OK 且 stat 不爲NULL ),那麼下面 就能夠開始插入二進制數據。

sqlite3_bind_blob( stat, 1, pdata, (int)(length_of_data_in_bytes), NULL );
// pdata爲數據緩衝區,length_of_data_in_bytes爲數據大小,以字節爲單位

這個函數一共有5個參數。

  • 第 1 個參數:是前面prepare獲得的 sqlite3_stmt * 類型變量。
  • 第 2 個參數:?號的索引。前面prepare的sql語句裏有一個?號,假若有多個?號 怎麼插入?方法就是改變 bind_blob 函數第2個參數。這個參數我寫1,表示 這裏插入的值要替換 stat 的第一個?號(這裏的索引從1開始計數,而非從0 開始)。若是你有多個?號,就寫多個 bind_blob 語句,並改變它們的第2個 參數就替換到不一樣的?號。若是有?號沒有替換,sqlite爲它取值null。
  • 第3個參數:二進制數據起始指針。
  • 第4個參數:二進制數據的長度,以字節爲單位。
  • 第5個參數:是個析夠回調函數,告訴sqlite當把數據處理完後調用此函數來 析夠你的數據。這個參數我尚未使用過,所以理解也不深入。可是通常都填 NULL,須要釋放的內存本身用代碼來釋放。

bind完了以後,二進制數據就進入了你的「sql語句」裏了。你如今能夠把它保存到數據庫裏:

int result = sqlite3_step( stat );
經過這個語句,stat 表示的sql語句就被寫到了數據庫裏。 最後,要把 sqlite3_stmt 結構給釋放:
sqlite3_finalize( stat ); //把剛纔分配的內容析構掉

讀出二進制

先聲明 sqlite3_stmt * 類型變量:

sqlite3_stmt * stat;

而後,把一個 sql 語句解析到 stat 結構裏去:

sqlite3_prepare( db, 「select * from Tbl_2」, -1, &stat, 0 );

當 prepare 成功以後(返回值是 SQLITE_OK ),開始查詢數據。

int result = sqlite3_step( stat );

這一句的返回值是 SQLITE_ROW 時表示成功(不是 SQLITE_OK )。

你能夠循環執行 sqlite3_step 函數,一次 step 查詢出一條記錄。直到返回值 不爲 SQLITE_ROW 時表示查詢結束。

而後開始獲取第一個字段:ID 的值。ID是個整數,用下面這個語句獲取它的值:

int id = sqlite3_column_int( stat, 0 );
//第2個參數表示獲取第幾個字段內容,從0開始計算,由於個人表的ID字段是第一個字段,所以這裏我填0

下面開始獲取 file_content 的值,由於 file_content 是二進制,所以我須要 獲得它的指針,還有它的長度:

const void * pFileContent = sqlite3_column_blob( stat, 1 );
int len = sqlite3_column_bytes( stat, 1 );

這樣就獲得了二進制的值。把 pFileContent 的內容保存出來以後,不要忘了釋 放 sqlite3_stmt 結構:

sqlite3_finalize( stat ); //把剛纔分配的內容析構掉

重複使用 sqlite3_stmt 結構

若是你須要重複使用 sqlite3_prepare 解析好的 sqlite3_stmt 結構,須要用函 數: sqlite3_reset。

result = sqlite3_reset(stat);

這樣, stat 結構又成爲 sqlite3_prepare 完成時的狀態,你能夠從新爲它 bind 內容。

相關文章
相關標籤/搜索