SQLite 數(shù)據(jù)類(lèi)型,。。 這次選 SQLite 的這個(gè)話(huà)題,。 主要有幾個(gè)原因,,一個(gè)是 SQLite 作為移動(dòng)客戶(hù)端最常用的本地?cái)?shù)據(jù)庫(kù)之一,它的使用范圍其實(shí)很大,,所以有深入了解他的價(jià)值,。 另外一個(gè)是 SQLite 的數(shù)據(jù)類(lèi)型機(jī)制其實(shí)常常會(huì)被大家忽略,因?yàn)槲覀兓蚨嗷蛏僦皩?duì)數(shù)據(jù)庫(kù)的基本操作有過(guò)一定了解,。所以在使用 SQLite 的時(shí)候就比較容易想當(dāng)然的根據(jù)通用的 SQL 知識(shí)來(lái)使用它,。比如要?jiǎng)?chuàng)建一個(gè)數(shù)據(jù)表,我想大家會(huì)很容易的寫(xiě)出類(lèi)似這樣的 SQL 語(yǔ)句來(lái) create table person { id int pirmary key, name varchar(255)}一眼看上去這個(gè) SQL 沒(méi)什么問(wèn)題,, 并且執(zhí)行到 SQLite 中也能正常運(yùn)行,,成功的創(chuàng)建出數(shù)據(jù)表。在很長(zhǎng)的時(shí)間中,,我自己也都是這樣來(lái)寫(xiě)的,。直到有一天我在開(kāi)始搞一個(gè) APP 的 SQLite 環(huán)境搭建的時(shí)候,突然涌出一個(gè)想法 — 這樣在 SQLite 中創(chuàng)建數(shù)據(jù)表的語(yǔ)法雖然符合標(biāo)準(zhǔn) SQL 中的定義,,但這樣寫(xiě)是最優(yōu)的解決方案嗎,? 因?yàn)槲抑溃總€(gè)數(shù)據(jù)庫(kù)的實(shí)現(xiàn)雖然盡可能的符合 SQL 標(biāo)準(zhǔn),,但肯定做不到完全吻合,,SQLite 應(yīng)該也不例外,一定有它自己的特性部分,。帶著這個(gè)想法,,我查了一下相關(guān)文檔,和我想的一樣,,SQLite 關(guān)于數(shù)據(jù)類(lèi)型確實(shí)有它自己的一套機(jī)制,,而且和我們通常熟悉的還不太一樣。 SQLite 的官網(wǎng)上有一篇文檔專(zhuān)門(mén)解釋了這個(gè)事情:Datatypes In SQLite Version 3,。 接下來(lái)我就和大家聊聊我的收獲,。 Storage ClassSQLite 數(shù)據(jù)類(lèi)型中,第一個(gè)重要的概念就是 Storage Class,。 簡(jiǎn)單來(lái)說(shuō)就是,,SQLite 數(shù)據(jù)表中的每個(gè)字段的類(lèi)型是動(dòng)態(tài)的。和我們熟悉的其他關(guān)系數(shù)據(jù)庫(kù)不同,,假如我定義了一個(gè)字段 name 的類(lèi)型是 varchar(255),,如果按照 SQL 標(biāo)準(zhǔn)來(lái)說(shuō),,這張表中每一條記錄的 name 字段都必須是這個(gè)類(lèi)型,然而 SQLite 實(shí)際上不是這樣的,。 如果是在 SQLite 中,,每條記錄的 name 字段的類(lèi)型是動(dòng)態(tài)的,也就是說(shuō)同一張表的不同數(shù)據(jù)條目中,,name 字段的類(lèi)型是可以不同的,。并且 SQLite 中用于表示不同值得類(lèi)型時(shí),用的也不是靜態(tài)類(lèi)型,,而是 Storage Class,。 在 SQLite 中, Storage Class 只有 5 個(gè)類(lèi)型: NULL : 對(duì)應(yīng) NULL 值,。 INTEGER: 表示有符號(hào)整數(shù),,根據(jù)整數(shù)的取值分為 1,2,,3,,4,6,,8 個(gè)字節(jié)尺寸,。 REAL: 用于存儲(chǔ)浮點(diǎn)數(shù)。 TEXT: 用于存儲(chǔ)字符串類(lèi)型數(shù)據(jù),。 BLOB: 二進(jìn)制大數(shù)據(jù),。 如上所示,SQLite 的核心數(shù)據(jù)類(lèi)型只有這 5 個(gè),。 比想象的要簡(jiǎn)單很多,,并且 SQLite 數(shù)據(jù)是動(dòng)態(tài)類(lèi)型。 那么為什么我們可以在 create table 語(yǔ)句中使用 varchar, double 這樣的數(shù)據(jù)類(lèi)型寫(xiě)法呢,?這就涉及到另外一個(gè)概念了,。 Type Affinity上面提到的 Storage Class 是 SQLite 中數(shù)據(jù)的存儲(chǔ)類(lèi)型,它們代表的是數(shù)據(jù)實(shí)際在磁盤(pán)上面的存放類(lèi)型,。如果我們要在 SQL 語(yǔ)句中表示數(shù)據(jù)類(lèi)型,,就需要用到另外一個(gè)概念,也就是 Type Affinity,。 Type Affinity 可以理解為對(duì)這個(gè)數(shù)據(jù)字段的推薦類(lèi)型,。為什么說(shuō)是推薦類(lèi)型呢,因?yàn)槲覀兦懊嫣岬竭^(guò),,SQLite 的數(shù)據(jù)類(lèi)型是動(dòng)態(tài)的,,我們只能給這個(gè)字段推薦一個(gè)類(lèi)型,而不能規(guī)定這個(gè)字段只能是這個(gè)類(lèi)型,。 Type Affinity 也分為 5 個(gè)類(lèi)型: TEXT NUMERIC INTEGER REAL BLOB 雖然和前面的 Storage Class 的 5 個(gè)取值有部分重合,,但它們的含義是不同的,。Type Affinity 是可以直接寫(xiě)到 SQL 語(yǔ)句中的,比如我們開(kāi)始的例子中可以改用更符合 SQLite 自身標(biāo)準(zhǔn)的寫(xiě)法: create table person { id INTEGER pirmary key, name TEXT}那么大家之這時(shí)候可能會(huì)有些疑問(wèn)了,,比如 name 字段,,既可以用 TEXT 也可以用 varchar(255) ,那我到底該用哪個(gè)呢,,它們之間的區(qū)別是什么? 這就要從 Type Affinity 的匹配規(guī)則說(shuō)起,,我們除了這樣明確的寫(xiě)出 INTEGER 和 TEXT 這樣的 Type Affinity 外,,SQLite 還會(huì)對(duì) Type Affinity 進(jìn)行匹配。 比如 INTEGER 類(lèi)型,,只要我們?cè)?SQL 中指定的類(lèi)型名稱(chēng)包含 INT,, 都會(huì)被匹配成 INTEGER,不區(qū)分大小寫(xiě),。 也就是說(shuō)你在 SQL 中寫(xiě)的類(lèi)型名稱(chēng)是下面中的任何一個(gè),,都會(huì)被匹配成 INTEGER: INTINTEGERTINYINTSMALLINTMEDIUMINTBIGINTUNSIGNED BIG INTINT2INT8 只要類(lèi)型名中包含 INT 的,就會(huì)被匹配成 INTEGER,。 這就解釋了為什么我們最開(kāi)始的那個(gè) SQL 語(yǔ)句也可以成功運(yùn)行了,。下面是每個(gè) Type Affinity 的匹配規(guī)則: INTEGER: 類(lèi)型名中包含 INT。 TEXT: 類(lèi)型名中包含 CHAR, CLOB, TEXT,。 REAL: 類(lèi)型名中包含 REAL,FLOA,DOUB,。 BLOB: 類(lèi)型名中包含 BLOB。 NUMERIC: 其他情況都會(huì)匹配到這個(gè),。 這些匹配規(guī)則都是不區(qū)分大小寫(xiě)的,,知道了這個(gè)規(guī)則的存在,再回頭看看我們最開(kāi)始寫(xiě)的 SQL: create table person { id int pirmary key, name varchar(255)}int 符合 INTEGER 的匹配規(guī)則,,所以能執(zhí)行通過(guò),, varchar(255) 符合 TEXT 的匹配規(guī)則,所以也能執(zhí)行通過(guò),。然后他們之間有區(qū)別嗎,? 其實(shí)是沒(méi)有區(qū)別的,只是一個(gè)概念的不同寫(xiě)法,。int,varchar 這些匹配規(guī)則的存在是為了更好的兼容我們熟悉的 SQL 標(biāo)準(zhǔn)的寫(xiě)法,。 無(wú)論怎么寫(xiě),本質(zhì)上都是對(duì)應(yīng)的 5 個(gè) Type Affinity 之一,。 當(dāng)然,,如果你是在為 SQLite 環(huán)境進(jìn)行 SQL 設(shè)計(jì),TEXT,INTEGER 這種寫(xiě)法會(huì)更加合適一些,。 最后,,關(guān)于 Type Affinity 的匹配規(guī)則有兩個(gè)值得一提的事情,,首先匹配是嚴(yán)格按照我們上面寫(xiě)的順序進(jìn)行的,也就是說(shuō)如果你遇到一個(gè) CHARINT 的類(lèi)型名稱(chēng),,它同時(shí)匹配了 TEXT 和 INTEGER,。 但因?yàn)?INTEGER 是最先匹配的,所以他會(huì)被匹配成 INTEGER,。 另外一個(gè)就是,, 雖然 varchar(255) 這種寫(xiě)法,可以匹配到 TEXT 類(lèi)型,,但括號(hào)里面表示長(zhǎng)度的 255,,是會(huì)被忽略的,SQLite 中字符串字段沒(méi)有長(zhǎng)度限制,。 Type Affinity 如何工作我們了解了 Type Affinity 的存在,,以及它如何匹配,生效,。那么還有最后一個(gè)問(wèn)題,,假如我們給一個(gè)字段指定了 Type Affinity, 它在實(shí)際運(yùn)行中都起什么作用呢,? 還是以 name 字段為例,,它的 Type Affinity 類(lèi)型是 TEXT。 我們可以這樣寫(xiě) insert 語(yǔ)句: insert into person (name) values ('mark');這里插入的數(shù)據(jù)類(lèi)型和 Type Affinity 推薦的類(lèi)型是一樣的,,都是字符串,,語(yǔ)句被成功的執(zhí)行。另外一種情況,,如果我們這樣寫(xiě) insert 語(yǔ)句呢: insert into person (name) values (500);這次我們插入的是一個(gè)整數(shù),,這時(shí)候 Type Affinity 的作用就體現(xiàn)了,因?yàn)?Type Affinity 建議的類(lèi)型是 TEXT,, SQLite 就會(huì)把這個(gè)數(shù)字 500,,轉(zhuǎn)換成字符串 `500` 然后在插入數(shù)據(jù)庫(kù)中。 這個(gè)機(jī)制對(duì)于 INTEGER 也一樣,,比如 id 字段: insert into person (id) values ('20');這條數(shù)據(jù)在插入的時(shí)候,,也會(huì)把字符串 `20` 轉(zhuǎn)換成數(shù)字 20。 在 DB Browser 中驗(yàn)證到這里,,我們算是把 SQLite 數(shù)據(jù)類(lèi)型機(jī)制了解了一遍,。 那么在實(shí)際使用時(shí)會(huì)是什么樣呢,我們來(lái)用 DB Browser 試著操作一下真實(shí)的 SQLite 數(shù)據(jù)庫(kù),。 DB Browser 是一個(gè)很好用的 SQLite 數(shù)據(jù)庫(kù)管理工具,,首先,我們?cè)囍鴦?chuàng)建一個(gè)數(shù)據(jù)表: 可以看到,,DB Browser 在創(chuàng)建表的時(shí)候,,使用的是 SQLite 的 Type Affinity 類(lèi)型,。可見(jiàn),,它采用的就是更加符合 SQLite 習(xí)慣的類(lèi)型名稱(chēng),。看完這篇文章后,,下次再使用類(lèi)似 DB Browser 的工具創(chuàng)建表的時(shí)候,,就不要質(zhì)疑為什么沒(méi)有 varchar, float 這樣的字段類(lèi)型了。 Type Affinity 的類(lèi)型命名方式,,才是 SQLite 中更加標(biāo)準(zhǔn)的,。 我們還可以用 DB Browser 驗(yàn)證一下 Type Affinity 和 SQLite 的動(dòng)態(tài)類(lèi)型機(jī)制是如何運(yùn)行的。還是在 DB Browser 中,,執(zhí)行一條這樣的插入語(yǔ)句: id 字段應(yīng)該是 INTEGER 類(lèi)型的,但我們這里給它插入了一條字符串類(lèi)型的值,。執(zhí)行一下,,這條插入語(yǔ)句是能夠成功運(yùn)行的。我們需要驗(yàn)證一下插入到表中的這條數(shù)據(jù)實(shí)際存儲(chǔ)的類(lèi)型是什么,,我們可以運(yùn)行這條語(yǔ)句: typeof 函數(shù)可以獲取到每條數(shù)據(jù)在庫(kù)中實(shí)際的存儲(chǔ)類(lèi)型,,雖然我們 insert 語(yǔ)句中插入的 id 是字符串類(lèi)型,但因?yàn)?id 字段的 Type Affinity 是 INTEGER,。 數(shù)據(jù)在實(shí)際存儲(chǔ)之前,,會(huì)被 SQLite 根據(jù)每個(gè)字段的 Type Affinity 轉(zhuǎn)換成相應(yīng)的類(lèi)型。 這也就是為什么我們給 INTEGER 字段插入一個(gè)字符串值能夠成功,,并且實(shí)際存儲(chǔ)的類(lèi)型也正確的原因了,。 讓我們?cè)龠M(jìn)一步思考一下,看看另外一個(gè) insert 語(yǔ)句: 這次更加奔放,,前面那條 insert 語(yǔ)句我們雖然給 id 插入的是字符串,,但好歹還是 `2` 這個(gè)能有意義的轉(zhuǎn)換成整數(shù)的字符串。 這次我們插入的是 `text`,。 那么這條語(yǔ)句能不能被執(zhí)行成功呢,? 執(zhí)行一下,居然也成功了,。我們?cè)儆?typeof 來(lái)驗(yàn)證一下看看: 可以看到,,這條記錄中 id 字段的實(shí)際類(lèi)型是 TEXT。 盡管 id 的 Type Affinity 是 INTEGER,。 但正如我們前面說(shuō)的,, Type Affinity 只是推薦,不是必須,。每條數(shù)據(jù)最終的 Storage Class 是動(dòng)態(tài)的,。 比如我們這條插入語(yǔ)句,,我們給 id 插入了一個(gè)字符串 text, SQLite 會(huì)根據(jù) id 的 Type Affinity 嘗試將這個(gè)字符串轉(zhuǎn)換成 INTEGER 類(lèi)型,。但很明顯,,轉(zhuǎn)換不能成功,所以這個(gè)數(shù)據(jù)就原樣的存儲(chǔ)進(jìn)來(lái)了,。所以它的類(lèi)型也沒(méi)有被轉(zhuǎn)換,,依然是 TEXT。 這個(gè)例子是對(duì) SQLite 動(dòng)態(tài)類(lèi)型機(jī)制一個(gè)很好的解釋?zhuān)憧梢越o字段指定 Type Affinity,。 但即使你指定了 Type Affinity,,也不能強(qiáng)制這個(gè)字段的存儲(chǔ)類(lèi)型。 結(jié)束這篇文章對(duì) SQLite 數(shù)據(jù)類(lèi)型機(jī)制給大家做了比較明確的解釋?zhuān)⑼ㄟ^(guò) DB Browser 的幾個(gè)實(shí)踐操作驗(yàn)證了 SQLite 類(lèi)型機(jī)制的運(yùn)作過(guò)程,。內(nèi)容不算特別高深,,只求一篇內(nèi)容能幫你解決某一個(gè)具體類(lèi)型的問(wèn)題,日積月累作用就會(huì)明顯,??赐赀@個(gè)后,應(yīng)該會(huì)對(duì)你下次使用 SQLite 的時(shí)候有所幫助,。你在寫(xiě) SQL 或者用工具的時(shí)候,,帶著這個(gè)思維模式一定會(huì)做出更加標(biāo)準(zhǔn)的方案。 如果你覺(jué)得這篇文章有幫助,,還可以關(guān)注微信公眾號(hào) swift-cafe,,會(huì)有更多我的原創(chuàng)內(nèi)容分享給你~ 本站文章均為原創(chuàng)內(nèi)容,如需轉(zhuǎn)載請(qǐng)注明出處,,謝謝,。 關(guān)注微信公眾號(hào),發(fā)現(xiàn)更多精彩 swift-cafe |
|