關於女神SQLite的疑惑(2)

 還是女神SQLite的話題,繼續討論有關她的種種常見疑惑。

1. 問:女神SQLite是線程安全的嗎?

1. 答:SQLite是線程安全的,這點確鑿無疑。但我要補充的一句話是:線程有時候是惡魔,不要讓女神輕易接近他!


說線程是惡魔可能有點危言聳聽的味道,難道線程不是我們廣大編程羣衆喜聞樂見的基本工具麼?都用了多少年啦沒啥問題!的確如此,但世界上總有頭上長角的牛人,可以在早已被認爲平平無奇的地方硬生生找出普通人發現不了的深層邏輯謬誤,並且能裝訂成冊警示後人,來膜拜下:

www2.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1.pdf

言歸正傳,SQLite本身是線程安全的,但要獲得這個技能,你必須在編譯的時候定義宏 SQLITE_THREADSAFE 爲1。如果你不確定即將鏈接到你程序的 SQLite 庫文件是否擁有了線程安全技能,你可以調用以下函數來確認。

sqlite3_threadsafe() 

SQLite 使用互斥鎖來確保多線程可以順序地訪問普通數據結構,從而確保安全性。然而,頻繁地索取和釋放這些互斥鎖勢必會輕微地降低 SQLite的性能。因此,如果你不需要 SQLite 爲你提供線程安全的保障,你可以用下面的編譯選項來關閉它們以獲得最高性能

-DSQLITE_THREADSAFE=0

另外要額外提醒一句,在 Unix/Linux 系統下,不要將一個打開的 SQLite 數據庫連接,通過調用 fork() 函數傳遞到子進程去使用,謹記。


2. 問:怎麼列出一個數據庫中所有的表和索引

2. 答:這分兩種情況,① 使用SQLite命令行的時候;② 使用C/C++編程API的時候。

第一種情況,你直接使用SQLite的內置命令 ".tables" 即可查看當前數據庫中的所有表,或者使用內置命令 ".schema" 來查看當前數據庫中所有的表和索引的創建語句。

第二種情況,可以在一個特殊的表 "SQLITE_MASTER" 獲得所有的的表和索引。每一個SQLite數據庫都有一個稱爲 SQLITE_MASTER 的表,它統管了數據庫中所有其他的元素,它的內部定義如下:

CREATE TABLE sqlite_master (

  type TEXT,

  name TEXT,

  tbl_name TEXT,

  rootpage INTEGER,

  sql TEXT

);


對於一個表來說, type 域就是 'table' ,name 域就是表的名字。因此可以使用以下 SQL 語句來查詢當前數據庫庫中所有的表:


SELECT name FROM sqlite_master

WHERE type='table';


對於一個索引來說,type 域就是 'index'name 域就是索引的名字,而 tbl_name 域則表示該索引所在的表的名字。


對於表和索引,sql 域都是創建他們的原始 SQL 語句。對於自動創建的索引(比如自動遞增的主鍵)而言,該域爲 NULL。 


表 SQLITE_MASTER 是隻讀的,你無法對其進行諸如 UPDATE、INSERT或者DELETE。當你創建或者銷燬表和索引時,SQLite 系統將自動更新它。 


注意,所有的臨時表都不會出現在 SQLITE_MASTER 中,臨時表及其索引的 schema 將被存儲在另一個被稱爲 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;

3. 問:怎麼在一個表中添加和刪除一個域(列)?

3. 答:抱歉,作爲一個正常的數據庫,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;


哇哇?!搞什麼鬼爲什麼這麼麻煩? 就不能提供一個 DELETE COLUMN 來一鍵刪除麼?

不能!因爲像 刪除 這樣的面目猙獰的可怕命令,對於視安全比生命更爲重要的數據庫而言是不能原生支持的,記錄在數據庫的東西,就像胎記一般,不會因爲你洗個澡就洗沒了,實在不想要不嫌麻煩不怕痛可以動刀子切掉,那大家都沒話說。


4. 問:我在數據庫中刪除了很多數據,但數據庫卻一點兒沒變小,誰出來說句公道話?

4. 答:別急聽我說,當你從 SQLite 數據庫中刪除信息時,SQLite 內部會記錄這個空出來的區域,以便於下次你插入新數據時可以使用。但在你沒有斷開數據庫鏈接(close)之前,這片存儲區域暫時不還給操作系統。

這好像是很多收押金的APP的套路。。。

對於強迫症患者來說,這不是一件好事,他們的理想情況是,我一旦刪除數據,必須要看到實實在在的數據庫變小!並且一定要刪多少小多少,因爲這樣才能感覺整個世界盡在掌握之中,怎麼才能做到呢?也好辦,只要一個 SQL 命令就可以了:

VACUUM;

如果你有更高的要求,你要求每次刪除數據時必須強迫 SQLite 自動釋放相應的存儲空間,那可以使用 auto_vacuum 來達到地。

PRAGMA auto_vacuum = FULL;

但是凡事都是要付出代價的,每次嚴格縮減存儲空間帶來的後果除了使得 SQLite 系統變慢之外,在縮減空間時實際上還會產生最多兩倍於已用空間大小的臨時存儲空間需求。


5. 問:SQLite那麼棒,我能不能偷偷把它用到我的商業項目中,額。。。我指的是不掏任何費用的情況下?

5. 答:雖然問得略顯猥瑣,但答案是肯定的。

SQLite是徹底的開源,你不需要爲他付出任何費用,它的作者在源碼的開頭處僅僅寫下對使用它的人的三個“祝福”:


願你用來行善除惡

願你原諒自己並寬恕他人

願你寬心與人分享,所取不多於所施。


可能你會覺得作者矯情,但請注意,SQLite 不是普通的軟件,世界上所有的安卓手機和蘋果手機全部都使用 SQLite,這還僅僅是手機而已,還有海量電子設備都用到了這款快準狠的數據庫!想想吧!作者爲了開源事業,放棄了多麼大的現實利益!敬佩!


6. 問:怎麼在字符串中包含一個單引號?

6. 答:SQL 標準使用單引號來引用字符串,因此在字符串中包含單引號是需要特殊的寫法:寫兩遍。請看:

INSERT INTO t values('蘋果''香蕉');

注意到插入的字符串中紅色的一堆單引號,它表示一個單引號,因此他相當於插入了這樣的字符串:

蘋果'香蕉

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

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

微店主頁.JPG
微信原文: https://mp.weixin.qq.com/s?__biz=MzAxNzYzMTU0Ng==&mid=2651289233&idx=1&sn=3994dc7fd73c6db5755e6e1496cbe67d&chksm=801146c4b766cfd25445ddf4981fc6e36662e5ee82d11049780722d8f1f7c9e0d9941b2c2b39#rd


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