SQLite(快速上手版)筆記之注意和高級篇

       相信看了我博客的上篇的博友或路過者,都會覺得,sqlite小巧,易學,簡單。下面是我再把我的筆記上傳博客中,以幫助一些人和我自己,今後翻閱方便。因爲我知道有這回事。  

  前幾天看到有人回覆帖子問了sqlite數據庫用在哪些方面,其實這樣的問題,都可以維基或是百度下的,什麼都會出來,不過sqlite是小型的數據庫,主要用在嵌入式設備中如手機等。下面是對這sqlite3的一些注意和高級部分的筆記:

  1、類型自動分類:具體的值比如sql語句部分的帶雙引號或單引號的文字被定爲Text;如果文字沒帶引號和小數點或指數則被定義爲INTEGER;如果文字沒帶引號但有小數點或指數則被定義爲REAL;如果值是空則被定義爲空值;BLOB數據使用符號X'ABCD'標識。

  2、sqlite3.0中,值被定義爲什麼類型只和值自身有關,和列、變量都沒關係,我們稱其其爲“弱類型”。所有其他的數據庫引擎都收靜態類型系統的限制,其中的所有值的類型是由其所屬列的屬性決定的,而和值無關。不過爲了最大限度的增加sqlite和其他數據庫的兼容性,sqlite提出了一個“類型親和性”,意思就是說它支持列的“類型親和性”。列的“類型親和性”是爲該列所存儲的數據建議一個類型。我們還要注意是建議不是強迫。在理論上講,任何列上可以存儲任何類型的數據。只是針對某些列,如果給建議類型的話,數據庫將按所建議的類型存儲,而這個被優先使用的數數據類型則被稱爲“親和類型”。

  (1)、sqlite3.0中,數據庫的每一列都被定義爲以下親和類型的一種:文本、數字(實數)、整數、無。(這點的理解是這樣的,就是說我給一個整數類型的值填充到文本字段上,此時整數類型的數據被當作文本存儲。)

    規則:

    (1)、 如果數據類型包括字符串"INT"那麼它被定義成具有整數親和性.

        (2)、 如果列中的數據類型包括以下任何的字符串 "CHAR", "CLOB", or "TEXT"    那麼這個列則具有文本親和性.要注意VARCHAR 類型包括字符串"CHAR"因此也具有文本類型親和性.

            (3)、如果一個列的數據類型包括字符串"BLOB"或者如果數據類型被具體化了,那麼這個列具有無類型親和性.

            (4)、否則就具有數字(實數)類型親和性.

  3、 運算符

  所有的數學運算符(所有的運算符而不是連鎖作用標記符"||")運算對象首先具有數字親和性, 如果一個或是兩個都不能被轉換爲數字那麼操作的結果將是空值。 對於連接作用操作符,所有操作符將首先具有文本親和性。如果其中任何一個操作符不能被轉換爲文本(因爲它是空值或是 BLOB)連接作用操作符將是空值。

  4、 分類,排序,混合挑選

  當用子句 ORDER挑選值時,空值首先被挑選出來, 然後是整數和實數按順序被挑選出來, 然後是文本值按 memcmp()順序被挑選出來, 最後是BLOB值按memcmp()順序被挑選出來.在挑選之前, 沒有存儲類型的值都被轉換了。

  5、用戶定義的校對順序

  BINARY -使用memcmp()比較字符串數據,  不考慮文本編碼。(默認比較順序)  

  REVERSE -用倒序比較二進制文本。  

  NOCASE -  和二進制一樣,但在比較之前,26 位的大寫字母要被轉換成小寫字母。【這個改變可以通過編寫sqlite的自定義函數自己實現一些數據庫函數就可以,而且這些自定義函數,在sqlite api中都有。寫好了後,進行編譯,生成.exe文件就可以。】

  對於二進制比較符(=, <, >, <= and >=),如果操作數是一列的話,那麼該列的默認比較類型決定於所使用的比較順序. 如果兩個操作數都是列的話,那麼左邊的操作數的默認比較類型決定了所要使用的比較順序.如果兩個操作數都不是一列,將使用二進制來比較。

  6、SQLite  不支持的  SQL92  特性

  這個列表的順序關係到何時一個特性可能被加入到 SQLite。接近列表頂部的特性更可能在不遠的將來加入。接近列表底部的特性尚且沒有直接的計劃。 

 

  7、sqlite不支持成羣的索引(簡單來說,就是索引使數據庫中的數據存入時索引的順序是什麼樣,數據怎樣放置),這意味着,如果你的索引的是整數順序,記錄就會把數據庫中的數據按整數順序安排,先1後2後3。(設置sqlite頁面緩存: pragma page_size=大小;)

      看個例子:create table wibble2 as select * from wibble;

          Delete from wibble;

          Insert into wibble select * from wibble2 order by key;(這行的寫法,在其他數據庫試過,可行(Oracle/SQLServer2012),sqlite3中也支持)

          Drop table wibble2;

   8、sqlite中如何使用觸發器執行取消和重做邏輯:思路是創建一個特殊表格, 保存數據庫撤銷和重做變化所需的信息。因爲數據庫中的每個表格都需要參與撤銷和重做,每個delete、insert、update都生成了觸發器,而且它們可以在撤銷日記表格中生成登記項,這個登記項將撤銷操作。撤銷表格中的登記項由一般的sql語句組成,爲了完成撤銷,sql語句可以重新運行。

CREATE TRIGGER 觸發器名稱

[BEFORE|AFTER] 數據庫事件 ON [數據庫名稱].表名

[FOR EACH ROW][ WHEN expression]

BEGIN

觸發器執行動作

END

數據庫事件:

DELETE INSERT

UPDATE

UPDATE OF 字段列表

    下面寫個腳本:

      Create table ex1(a INTEGER, b TEXT, c REAL);

      Create trigger trigger_ex1_it

      after insert on ex1

      Begin

        Insert into undolog values(NULL, 'delete from ex1 where rowid='||new.rowid);

      End;

       Create trigger trigger_ex1_ut

      after update on ex1

      Begin

        Insert into undolog values(NULL,'update ex1 set   a='||quote(old.a)||',b='||quote(old.b)||', c='||quote(old.c)||' where rowid='||old.rowid);

      End;

      Create trigger trigger_ex1_dt

      Before delete on ex1

      Begin

        Insert into undolog values(NULL, 'insert into ex1(rowid, a, b, c) values('||old.rowid||', '||quote(old.a)||', '||quote(old.b)||','||quote(old.c)                            ||')');

      End;

  在ex1 表格中執行每個INSERT 後,the _ex1_it 觸發器生成 DELETE語句的文本,它將撤銷 INSERT操作。The _ex1_ut觸發器生成 UPDATE語句,這語句將取消一個 UPDATE所產生的作用。trigger_ex1_dt觸發器生成一個語句,這語句將取消一個 DELETE所具有的作用。 

要注意 quote()函數在這些觸發器中的使用。quote()函數在 SQLite中是標準的。它把它的參數轉換成一種適合被包含在 SQL 語句中的形式。數字值不改變。單個的 quotes 被加在字符串之前或之後,任何內在的單個quotes 都被逃逸。quote()函數被加入 SQLite是 爲了執行撤銷和重做操作。

  9、SQLite3 C/C++ 開發接口(API 函數)

  Sqlite3是建立在sqlite2.8之上開發的,是爲了支持UTF-16編碼、自定義的文本排序方法、對blobs字段建立索引。3.0版和2.x版的api非常相似,只不過前綴改成sqlite3。

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函數和文本排序函數。【附加體外話:其實有關數據庫的操作,java的api或是c/c++庫中都供普通操作數據庫的api或是方法。因爲我對這幾門語言熟悉點,所以就順便說下,這語言之間的一些共性。】

  9.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 */

  9.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 參考。

  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—_exec()。 使用該接口訪問數據庫是當前比較好的的一種方法。

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);

  nt 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_***() 函數來提取, 所有的這些函數都把列的編號作爲第二個參數。列編號從左到右以零起始。

    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() 返回浮點數。

  10、SQLite 常見問題解答

  (1)、如何建立自動增長的字段:可以在字段上聲明爲INTEGER PRIMARY KEY AUTOINCREATE。

  (2)、SQLite 支持何種數據類型:參見 http://www.sqlite.org/datatype3.html。

  (3)、Sqlite允許一個列中插入任何類型的字段,(前面我們說過sqlite是種弱類型,不想其他的語言,如java),但任何類型的字段會被自動轉換該列所需的類型,轉換不了的則按任何類型對應的類型插入。(這就是我在前面說的類型親和性)有一種除外,就是標誌爲INTEGER PRIMARY KEY 的列只能存儲 64位整數, 當向這種列中插數據除整數以外的數據時,將會產生錯誤。

  (4)、SQLite 不允許在同一個表不同的兩行上使用 0 和0.0 作主鍵,因爲這兩者sqlite認爲是相等,既然相等就是不唯一。

  (5)、多個進程可同時打開同一個數據庫。多個進程可以同時進行 SELECT (讀)操作,但在任一時刻,只能有一個進程對數據庫進行更改(寫)。看到這麼一句話這樣說的,我覺得很有借鑑性,所以摘入到此,雖不是原話,大致意思是這樣的, 對於網絡文件,文件鎖的實現有好多 Bug,是靠不住的。如果他們說的是對的, 那麼在兩臺或多臺 Windows機器間共享數據庫可能會引起不期望的問題(關係數據庫兩外算)。SQLite允許多個進程同時打開一個數據庫, 同時讀一個數據庫。當有任何進程想要寫時,它必須在更新過程中鎖住數據庫文件。 但那通常只是幾毫秒的時間。其它進程只需等待寫進程幹完活結束。 當SQLite 試圖訪問一個被其它進程鎖住的文件時,缺省的行爲是返回 SQLITE_BUSY。(sqlite3是線程安全的)

  (6)、在 SQLite 數據庫中如何列出所有的表和索引:.tables和.schema和數據字典sqlite_master; 字典結構: type TEXT, name TEXT, tbl_name TEXT, rootpage INTEGER, sql TEXT 。

  (7)、SQLite 數據庫有已知的大小限制嗎? 見 limits.html 。

  (8)、在 SQLite 中,如何在一個表上添加或刪除一列?Sqlite沒有提供完整的alter支持,可以使用它來在表的末尾增加一列,可更改表的名稱。 如果需要對錶結構做更復雜的改變,則必須重新建表。 重建時可以先將已存在的數據放到一個臨時表中,刪除原表, 創建新表,然後將數據從臨時表中複製回來。  如,假設有一個 t1 表,其中有 "a", "b", "c" 三列, 如果要刪除列 c ,以下過程描述如何做: 

  BEGIN TRANSACTION;

  CREATE TEMPORARY TABLE t1_backup(a,b); 臨時表

  INSERT INTO t1_backup SELECT a,b FROM t1; 臨時表拷貝表t1數據

  DROP TABLE t1;

  CREATE TABLE t1(a,b);

  INSERT INTO t1 SELECT a,b FROM t1_backup;

  DROP TABLE t1_backup;

  END TRANSACTION;

  COMMIT;

發佈了139 篇原創文章 · 獲贊 21 · 訪問量 82萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章