(轉)sqlite常見問題

sqlite常見問題

(1) 如何建立自動增長字段?

簡短回答:聲明爲 INTEGER PRIMARY KEY 的列將會自動增長。

長一點的答案: 如果你聲明表的一列爲 INTEGER PRIMARY KEY,那麼, 每當你在該列上插入一NULL值時, NULL自動被轉換爲一個比該列中最大值大1的一個整數,如果表是空的, 將會是1。 (如果是最大可能的主鍵 9223372036854775807,那個,將鍵值將是隨機未使用的數。) 如,有下列表:

CREATE TABLE t1(
a INTEGER PRIMARY KEY,
b INTEGER
);

在該表上,下列語句

INSERT INTO t1 VALUES(NULL,123);

在邏輯上等價於:

INSERT INTO t1 VALUES((SELECT max(a) FROM t1)+1,123);

有一個新的API叫做 sqlite3_last_insert_rowid(), 它將返回最近插入的整數值。

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


--------------------------------------------------------------------------------

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

參見 http://www.sqlite.org/datatype3.html.
--------------------------------------------------------------------------------

(3)SQLite允許向一個integer型字段中插入字符串!

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

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


--------------------------------------------------------------------------------

(4)爲什麼SQLite不允許在同一個表不同的兩行上使用0和0.0作主鍵?

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

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


--------------------------------------------------------------------------------

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

多個進程可同時打開同一個數據庫。多個進程可以同時進行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 函數調整這一行爲。
---------------------------------------------------------------------------

(6)SQLite線程安全嗎?

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

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

在 3.3.1 版本之前,一個 sqlite3 結構只能被用於調用 sqlite3_open 創建的同一線程。你不能在一個線程中打開數據庫, 然後將數據庫句柄傳遞給另外一個進程使用。 這主要是由於在好多通用的線程實現(如RedHat9)中的限制引起的(是Bug嗎?)。 特別的,在有問題的系統上,一個進程創建的 fcntl() 鎖無法被其它線程清除或修改。 所以,由於 SQLite 大量使用 fcntl() 鎖做併發控制, 如果你在不同的線程間移動數據庫連接,就可能會出現嚴重的問題。

在3.3.1版本上,關於在線程間移動數據庫連接的限制變得寬鬆了。 在它及以後的版本中,只要連接沒有持有 fcntl() 鎖,在線程間移動句柄是安全的。 如果沒有未決的事務,並且所有的語句都已執行完畢, 你就可以安全的假定不再持有任何鎖。

在UNIX中,在執行 fork() 系統調用時不應攜帶已打開的數據庫進入子進程。 那樣做將會有問題。


--------------------------------------------------------------------------------

(7)在SQLite數據庫中如何列出所有的表和索引?

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

在一個 C/C++ 程序中(或者腳本語言使用 Tcl/Ruby/Perl/Python 等) 你可以在一個特殊的名叫 SQLITE_MASTER 上執行一個SELECT查詢以獲得所有 表的索引。每一個 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


--------------------------------------------------------------------------------

(8) 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 個字符。

對有關SQLite限制的詳細討論,見 limits.html 。


--------------------------------------------------------------------------------

(9) 在SQLite中,VARCHAR字段最長是多少?

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


--------------------------------------------------------------------------------

(10) SQLite支持二進制大對象嗎?

SQLite 3.0 及以後版本允許你在任何列中存儲 BLOB 數據。 即使該列被聲明爲其它類型也可以。


--------------------------------------------------------------------------------

(11) 在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;


--------------------------------------------------------------------------------

(12) 我在數據庫中刪除了很多數據,但數據庫文件沒有變小,是Bug嗎?

不是。當你從SQLite數據庫中刪除數據時, 未用的磁盤空間將會加入一個內部的“自由列表”中。 當你下次插入數據時,這部分空間可以重用。磁盤空間不會丟失, 但也不會返還給操作系統。

如果刪除了大量數據,而又想縮小數據庫文件佔用的空間,執行 VACUUM 命令。 VACUUM 將會從頭重新組織數據庫。這將會使用數據庫有一個空的“自由鏈表”, 數據庫文件也會最小。但要注意的是,VACUUM 的執行會需要一些時間 (在SQLite開發時,在Linux上,大約每M字節需要半秒種),並且, 執行過程中需要原數據庫文件至多兩倍的臨時磁盤空間。

對於 SQLite 3.1版本,一個 auto-vacumm 模式可以替代 VACUUM 命令。 可以使用 auto_vacuum pragma 打開。


--------------------------------------------------------------------------------

(13) 我可以在商業產品中使用SQLite而不需支付許可費用嗎?

是的。SQLite 在 public domain。 對代碼的任何部分沒有任何所有權聲明。你可以使用它做任何事。


--------------------------------------------------------------------------------

(14) 如何在字符串中使用單引號(')?

SQL 標準規定,在字符串中,單引號需要使用逃逸字符,即在一行中使用兩個單引號。在這方面 SQL 用起來類似 Pascal 語言。 SQLite 尊循標準。如:

INSERT INTO xyz VALUES('5 O''clock');


--------------------------------------------------------------------------------

(15) SQLITE_SCHEMA error是什麼錯誤?爲什麼會出現該錯誤?

當一個準備好的(prepared)SQL語句不再有效或者無法執行時, 將返回一個 SQLITE_SCHEMA 錯誤。發生該錯誤時,SQL語句必須使用 sqlite3_prepare() API來重新編譯. 在 SQLite 3 中, 一個 SQLITE_SCHEMA 錯誤只會發生在用 sqlite3_prepare()/sqlite3_step()/sqlite3_finalize() API 執行 SQL 時。而不會發生在使用 sqlite3_exec()時。 在版本2中不是這樣。

準備好的語句失效的最通常原因是:在語句準備好後, 數據庫的模式又被修改了。另外的原因會發生在:

數據庫離線:DETACHed.
數據庫被 VACUUMed
一個用戶存儲過程定義被刪除或改變。
一個 collation 序列定義被刪除或改變。
認證函數被改變。
在所有情況下,解決方法是重新編譯並執行該SQL語句。 因爲一個已準備好的語句可以由於其它進程改變數據庫模式而失效, 所有使用 sqlite3_prepare()/sqlite3_step()/sqlite3_finalize() API 的代碼都應準備處理 SQLITE_SCHEMA 錯誤。下面給出一個例子:

int rc;
sqlite3_stmt *pStmt;
char zSql[] = "SELECT .....";

do {
/* Compile the statement from SQL. Assume success. */
sqlite3_prepare(pDb, zSql, -1, &pStmt, 0);

while( SQLITE_ROW==sqlite3_step(pStmt) ){
/* Do something with the row of available data */
}

/* Finalize the statement. If an SQLITE_SCHEMA error has
** occured, then the above call to sqlite3_step() will have
** returned SQLITE_ERROR. sqlite3_finalize() will return
** SQLITE_SCHEMA. In this case the loop will execute again.
*/
rc = sqlite3_finalize(pStmt);
} while( rc==SQLITE_SCHEMA );



--------------------------------------------------------------------------------

(16) 爲什麼 ROUND(9.95,1) 返回 9.9 而不是 10.0? 9.95不應該圓整 (四捨五入)嗎?

SQLite 使用二進制算術,在二進制中, 無法用有限的二進制位數表示 9.95 。使用 64-bit IEEE 浮點 (SQLite就是使用這個)最接近 9.95 的二進制表示是 9.949999999999999289457264239899814128875732421875。 所在,當你輸入 9.95 時,SQLite實際上以爲是上面的數字, 在四捨五入時會捨去。

這種問題在使用二進制浮點數的任何時候都會出現。 通常的規則是記住很多有限的十進制小數都沒有一個對應的二進制表示。 所以,它們只能使用最接近的二進制數。它們通常非常接近, 但也會有些微小的不同,有些時候也會導致你所期望的不同的結果。

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