關於女神SQLite的疑惑(1)

SQLite的大名就不做廣告了,反正嵌入式設備、手機等領域中,凡是用到數據庫的地方她幾乎都是主角。這個系列的推文,就來總結日常使用時我們都可能會遇到一些問題。



1. 問:怎麼創建一個自動遞增的域?

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


函數 sqlite3_last_insert_rowid() 可以返回最近一次插入操作的整數主鍵的值。


還有一點要注意,新建的主鍵的值等於原先存在的最大的主鍵的值+1,這個新的主鍵當然是當前全表唯一的,但卻有可能跟之前已經被刪除的記錄的鍵值相等,如此一來可能會導致查詢時不必要的誤會。如果要創建一個表全生命週期唯一的鍵值,就要在聲明中再加上這個約束關鍵字: AUTOINCREMENT。這樣一來,新建的主鍵鍵值就不僅是當前全表唯一,並且在表的全生命週期內也具備唯一性,即:是所有創建過的最大的鍵值+1。另外,如果最大的鍵值已經被使用過了無法在遞增,那麼此時的 INSERT 操作將會失敗,並且返回錯誤碼 SQLITE_FULL 。


2. 問:SQLite究竟支持什麼數據類型?

2. 答:

SQLite有所謂動態類型匹配機制,數據庫中的數據可以被儲存爲 INTEGER(整數),REAL(實數), TEXT(文本字符串), BLOB(二進制數據), 或者 NULL


3. 問:我剛剛將一個文本字符串插入了一個整型(INTEGER)域中!怎麼回事?

3. 答:別緊張,相信我這絕對是一個特色,而不是一個BUG。


SQLite 支持所謂動態類型匹配。這意味着它並不會對數據類型做強制性約束,一般而言,任意類型的數據,都可以被插入到任意一個域中,例如你可以將任意長度的字符串插入到一個整數域中,將一個浮點實數插入到一個文本域,或者將一個日期插入到字符域中。


在你使用命令 CREATE TABLE 來創建表時對域的類型的定義,並不成爲日後插入數據的約束條件。所有的域都可以儲存任意長度的文本字符串。


這種情況只有一個例外:被聲明爲 INTEGER PRIMARY KEY 的域只能存儲一個 64-bit 的有符號整數。如果你試圖將一個非整數強行插入到這樣的整數主鍵域中,恭喜你,你將收穫一個關於類型不匹配的大大的 error 


這麼說來,創建 table 時指定的數據類型還有什麼鳥用呢?嚴格說來還是有用的,SQLite會將你聲明時指定的類型,作爲該域的“傾向性”類型的依據。比如,如果一個域的類型被聲明爲 INTEGER 但是你正試圖插入一串文本,那麼SQLite會傾向於將此文本轉換爲整數,如果成功了,那麼實際存儲的就是一個整數,否則就存儲這串文本。


4. 問:爲什麼SQLite不准我使用 '0' 和 '0.0' 作爲兩個不同記錄的主鍵?

4. 答:是的,'0' 和 '0.0' 的確是兩個完全不同的文本字符串,但是當表的主鍵是一個數字類型的時候,SQLite不允許你這麼做。非要這麼幹的話,可以將主鍵的類型修改爲 TEXT 。


這個疑惑,實際上可以從上面的第3個問題得到指引和解答。


對數據庫而言,每一個行記錄必須有一個唯一的主鍵是,這是最基本的要求。但當一個域的類型是一個數字型(包括整數、實數),而你要插入 '0' 和 '0.0' 時,SQLite將會傾向於把它們視爲數字型數據,因此他們都將被記錄成無法區分的零值,這,顯然違反了主鍵的基本定義。


5. 問:可不可以讓多個程序同時訪問同一個數據庫文件?

5. 答:這沒什麼不可以。


多個程序可以安全地同時執行 SELECT 的動作。但是,任何時候都只能有一個程序可以對數據庫做出修改性的行爲。


實際上,SQLite使用了讀寫鎖來控制對數據庫的訪問。但這裏必須給出警告:這個機制在NFS(網絡文件系統)中工作得並不理想。


因此,你需要避免在NFS中使用多任務同時併發訪問 SQLite 數據庫。在 Windows 的FAT文件系統中,據說,運行一個叫Share.exe的後臺精靈進程可以解決這個問題,否則鎖機制將不穩定。而據我的經驗,以上場景是一個貨真價實的大坑,你有一萬個理由不要碰它。關於這個話題,早已有無數的 Windows 磚家們給出過警告,任何想用鎖機制來鎖住網絡文件的人都必定會被無數的莫名其妙的錯誤、崩潰、異常折磨成精神病,陷入噩夢般的抑鬱之中。簡而言之吧,避免在多端 Windows 中共享 SQLite 數據庫是你先要繞過去的火坑


而在嵌入式當中,據我所知還沒有任何一款 SQL 數據庫引擎在併發性上可以和 SQLite 匹敵。SQLite 允許多任務同時連接到同一個數據庫文件,並且允許多任務併發操作。當任意一個任務試圖進行寫操作時,它必須將整個數據庫鎖起來直到操作完畢,這聽起來貌似不是很屌炸天,但一般而言這僅需幾個毫秒而已,其他的任務只需要等待這麼一小段時間即可做它們該做的事情。其他的嵌入式 SQL 數據庫引擎,一般都只能做到每次讓一個任務連接到一個數據庫文件。


當然,基於 C/S 模型的大型數據庫引擎(例如 PostgreSQL、MySQL或者Oracle)一般能支持更大程度上的併發性,支持多任務同時併發寫操作。這對於 C/S 模型而言是可以辦到的,因爲它們有一個強大的 Server 來協調所有的訪問。


如果你有如此高併發的需求,那麼你應該考慮使用這樣的 C/S 模型的數據庫引擎,但一般而言,也許項目的真正併發需求比你想象的要低得多得多。


當 SQLite 試圖對一個已經被其他任務加了鎖的數據庫訪問時,將會得到一個 SQLITE_BUSY 的錯誤,你可以使用以下兩個函數來控制此時你的程序的下一步行爲。

sqlite3_busy_handler( )

sqlite3_busy_timeout( )


今天就聊到這,後續關於SQLite的常見問題會陸續更新。歡迎小夥伴關注、轉發、點贊、收藏、吐槽、扔雞蛋……



噓!聽說識別下面二維碼進入 微店祕籍酷 能上天遁地,不信你試試!




微信公衆號原文:https://mp.weixin.qq.com/s?__biz=MzAxNzYzMTU0Ng==&mid=2651289221&idx=1&sn=6e060e12eea1029020a426966041dcb0&chksm=801146d0b766cfc606888d4fc9d0bcb4e4b948e99b3d93978e9b6e9250c4a6388be9dc0ffe76#rd

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