聲明:本文copy自博客園-殘星的博客,博客地址:http://www.cnblogs.com/mingforyou/archive/2013/02/19/2917122.html
存儲類(Storage Classes)
如前文所述,SQLite在處理數據類型時與其它的數據庫不同。區別在於它所支持的類型以及這些類型是如何存儲、比較、強化(enforc)和指派(assign)。下面各節介紹SQLite處理數據類型的獨特方法和它與域完整性的關係。
對於數據類型,SQLite的域完整性被稱爲域親和性(affinity)更合適。在SQLite中,它被稱爲類型親和性(type affinity)。爲了理解類型親和性,你必須先要理解存儲類和弱類型(manifesttyping)。
SQLite有5個原始的數據類型,被稱爲存儲類。存儲類這個詞表明瞭一個值在磁盤上存儲的格式,其實就是類型或數據類型的同義詞。這5個存儲類在表4-6中描述。
表 4-6 SQLite存儲類
名稱 |
說明 |
INTEGER |
整數值是全數字(包括正和負)。整數可以是1, 2, 3, 4, 6或 8字節。整數的最大範圍(8 bytes)是{-9223372036854775808, 0, +9223372036854775807}。SQLite根據數字的值自動控制整數所佔的字節數。 空注:參可變長整數的概念。 |
REAL |
實數是10進制的數值。SQLite使用8字節的符點數來存儲實數。 |
TEXT |
文本(TEXT)是字符數據。SQLite支持幾種字符編碼,包括UTF-8和UTF-16。字符串的大小沒有限制。 |
BLOB |
二進制大對象(BLOB)是任意類型的數據。BLOB的大小沒有限制。 |
NULL |
NULL表示沒有值。SQLite具有對NULL的完全支持。 |
SQLite通過值的表示法來判斷其類型,下面就是SQLite的推理方法:
l SQL語句中用單引號或雙引號括起來的文字被指派爲TEXT。
l 如果文字是未用引號括起來的數據,並且沒有小數點和指數,被指派爲INTEGER。
l 如果文字是未用引號括起來的數據,並且帶有小數點或指數,被指派爲REAL。
l 用NULL說明的值被指派爲NULL存儲類。
l 如果一個值的格式爲X'ABCD',其中ABCD爲16進制數字,則該值被指派爲BLOB。X前綴大小寫皆可。
SQL函數typeof()根據值的表示法返回其存儲類。使用這個函數,下面SQL語句返回的結果爲:
sqlite> select typeof(3.14),typeof('3.14'), typeof(314), typeof(x'3142'), typeof(NULL);
typeof(3.14) typeof('3.14') typeof(314) typeof(x'3142') typeof(NULL)
real text integer blob null
SQLite單獨的一個字段可能包含不同存儲類的值。請看下面的示例:
sqlite> DROP TABLE domain;
sqlite> CREATE TABLE domain(x);
sqlite> INSERT INTO domain VALUES(3.142);
sqlite> INSERT INTO domain VALUES('3.142');
sqlite> INSERT INTO domain VALUES(3142);
sqlite> INSERT INTO domain VALUES(x'3142');
sqlite> INSERT INTO domain VALUES(NULL);
sqlite> SELECT ROWID, x, typeof(x) FROMdomain;
返回結果爲:
rowid x typeof(x)
1 3.142 real
2 3.142 text
3 3142 integer
4 1B blob
5 NULL null
這帶來一些問題。這種字段中的值如何存儲和比較?如何對一個包含了INTEGER、REAL、TEXT、BLOB和NULL值的字段排序?一個整數和一個BLOB如何比較?哪個更大?它們能相等嗎?
答案是:具有不同存儲類的值可以存儲在同一個字段中。可以被排序,因爲這些值可以相互比較。有完善定義的規則來做這件事。不同存儲類的值可以通過它們各自類的“類值”進行排序,定義如下:
1. NULL存儲類具有最低的類值。一個具有NULL存儲類的值比所有其它值都小(包括其它具有NULL存儲類的值)。在NULL值之間,沒有特別的可排序值。
2. INTEGER或REAL存儲類值高於NULL,它們的類值相等。INTEGER值和REAL值通過其數值進行比較。
3. TEXT存儲類的值比INTEGER和REAL高。數值永遠比字符串的值低。當兩個TEXT值進行比較時,其值大小由“排序法”決定。
4. BLOB存儲類具有最高的類值。具有BLOB類的值大於其它所有類的值。BLOB值之間在比較時使用C函數memcmp()。
所以,當SQLite對一個字段進行排序時,首先按存儲類排序,然後再進行類內的排序 (NULL類內部各值不必排序) 。下面的SQL說明了存儲類值的不同:
sqlite> SELECT 3 < 3.142, 3.142 <'3.142', '3.142' < x'3000', x'3000' < x'3001';
返回:
3 < 3.142 3.142 < '3.142' '3.142'< x'3000' x'3000' < x'3001'
1 1 1 1
弱類型(manifest typing)
SQLite使用弱類型。
看下面的表:
CREATE TABLE foo( x integer,
y text, z real );
向該表插入一條記錄:
INSERT INTO foo VALUES ('1', '1', '1');
當SQLite創建這條記錄時,x、y和z這3個字段中存儲的是什麼類型呢?答案是INTEGER, TEXT和REAL。
再看下面例子:
CREATE TABLE foo(x, y, z);
現在執行同樣的插入語句:
INSERT INTO foo VALUES ('1', '1', '1');
現在,x、y和z中存儲的是什麼類型呢?答案是TEXT、TEXT和TEXT。
那麼,是否SQLite的字段類型默認爲TEXT呢?再看,還是第2個表,執行如下插入語句:
INSERT INTO foo VALUES (1, 1.0, x'10');
現在,x、y和z中存儲的是什麼類型呢?答案是INTEGER、REAL和BLOB。
如果你願意,可以爲SQLite的字段定義類型,這看起來跟其它數據庫一樣。但這不是必須的,你可以儘管違反類型定義。這是因爲在任何情況下,SQLite都可以接受一個值並推斷它的類型。
總之,SQLite的弱類型可表示爲:1)字段可以有類型,2)類型可以通過值來推斷。類型親和性介紹這兩個規定如何相互關聯。所謂類型親和性就是在強類型(strict typing)和動態類型(dynamic typing)之間的平衡藝術。
類型親和性(Type Affinity)
在SQLite中,字段沒有類型或域。當給一個字段聲明瞭類型,該字段實際上僅僅具有了該類型的新和性。聲明類型和類型親和性是兩回事。類型親和性預定 SQLite用什麼存儲類在字段中存儲值。在存儲一個給定的值時到底SQLite會在該字段中用什麼存儲類決定於值的存儲類和字段親和性的結合。我們先來介紹一下字段如何獲得它的親和性。
字段類型和親和性
首先,每個字段都具有一種親和性。共有四種親和性:NUMERIC、INTEGER、TEXT和NONE。一個字段的親和性由它預聲明的類型決定。所以,當你爲字段聲明瞭類型,從根本上說是爲字段指定了親和性。SQLite按下面的規則爲字段指派親和性:
l 默認的,一個字段默認的親和性是NUMERIC。如果一個字段不是INTEGER、TEXT或NONE的,那它自動地被指派爲NUMERIC親和性。
l 如果爲字段聲明的類型中包含了'INT'(無論大小寫),該字段被指派爲INTEGER親和性。
l 如果爲字段聲明的類型中包含了'CHAR'、'CLOB'或'TEXT'(無論大小寫),該字段被指派爲TEXT親和性。如'VARCHAR'包含了'CHAR',所以被指派爲TEXT親和性。
l 如果爲字段聲明的類型中包含了'BLOB'(無論大小寫),或者沒有爲該字段聲明類型,該字段被指派爲NONE親和性。
注意:如果沒有爲字段聲明類型,該字段的親和性爲NONE,在這種情況下,所有的值都將以它們本身的(或從它們的表示法中推斷的)存儲類存儲。如果你暫時還不確定要往一個字段裏放什麼內容,或準備將來修改,用NONE親和性是一個好的選擇。但SQLite默認的親和性是NUMERIC。例如,如果爲一定字段聲明瞭類型JUJYFRUIT,該字段的親和性不是NONE,因爲SQLite不認識這種類型,會給它指派默認的NUMERIC親和性。所以,與其用一個不認識的類型最終得到NUMERIC親和性,還不如不爲它指定類型,從而使它得到NONE親和性。
親和性和存儲
親和性對值如何存儲到字段有影響,規則如下:
l 一個NUMERIC字段可能包括所有5種存儲類。一個NUMERIC字段具有數字存儲類的偏好(INTEGER和REAL)。當一個TEXT值被插入到一個NUMERIC字段,將會試圖將其轉化爲INTEGER存儲類;如果轉化失敗,將會試圖將其轉化爲REAL存儲類;如果還是失敗,將會用TEXT存儲類來存儲。
l 一個INTEGER字段的處理很像NUMERIC字段。一個INTEGER字段會將REAL值按REAL存儲類存儲。也就是說,如果這個REAL值沒有小數部分,就會被轉化爲INTEGER存儲類。INTEGER字段將會試着將TEXT值按REAL存儲;如果轉化失敗,將會試圖將其轉化爲INTEGER存儲類;如果還是失敗,將會用TEXT存儲類來存儲。
l 一個TEXT字段將會把所有的INTEGER或REAL值轉化爲TEXT。
l 一個NONE字段不試圖做任何類型轉化。所有值按它們本身的存儲類存儲。
l 沒有字段試圖向NULL或BLOB值轉化——如無論用什麼親和性。NULL和BLOB值永遠都按本來的方式存儲在所有字段。
這些規則初看起來比較複雜,但總的設計目標很簡單,就是:如果你需要,SQLite會盡量模仿其它的關係型數據庫。也就是說,如果你將SQLite看成是一個傳統數據庫,類型親和性將會按你的期望來存儲值。如果你聲明瞭一個INTEGER字段,並向裏面放一個整數,就會按整數來存儲。如果你聲明瞭一個具有 TEXT, CHAR或VARCHAR類型的字段並向裏放一個整數,整數將會轉化爲TEXT。可是,如果你不遵守這些規定,SQLite也會找到辦法來存儲你的值。
親和性的運行
讓我們看一些例子來了解親和性是如何工作的:
sqlite> CREATE TABLE domain(i int, nnumeric, t text, b blob);
sqlite> INSERT INTO domain VALUES(3.142,3.142,3.142,3.142);
sqlite> INSERT INTO domain VALUES('3.142','3.142','3.142','3.142');
sqlite> INSERT INTO domain VALUES(3142,3142,3142,3142);
sqlite> INSERT INTO domain VALUES(x'3142',x'3142',x'3142',x'3142');
sqlite> INSERT INTO domain VALUES(null,null,null,null);
sqlite> SELECT ROWID,typeof(i),typeof(n),typeof(t),typeof(b)FROM domain;
返回:
rowid typeof(i) typeof(n) typeof(t) typeof(b)
1 real real text real
2 real real text text
3 integer integer text integer
4 blob blob blob blob
5 null null null null
下面的SQL說明存儲類的排序情況:
sqlite> SELECT ROWID, b, typeof(b) FROMdomain ORDER BY b;
返回:
rowid b typeof(b)
5 NULL null
1 3.142 real
3 3142 integer
2 3.142 text
4 1B blob
sqlite> SELECT ROWID, b, typeof(b),b<1000 FROM domain ORDER BY b;
返回:
rowid b typeof(b) b<1000
NULL null NULL
1 3.142 real 1
3 3142 integer 1
2 3.142 text 0
4 1B blob 0
存儲類和類型轉換
關於存儲類,需要關注的另一件事是:存儲類有時會影響到值如何進行比較。特別是SQLite有時在進行比較之前,會將值在數字存儲類(INTEGER和REAL)和TEXT之間進行轉換。爲進行二進制的比較,遵循如下規則:
l 當一個字段值與一個表達式的結果進行比較,字段的親和性會在比較之前應用於表達式的結果。
l 當兩個字段值進行比較,如果一個字段擁有INTEGER或NUMERIC親和性而另一個沒有,NUMERIC親和性會應用於非NUMERIC字段的TEXT值。
l 當兩個表達式進行比較,SQLite不做任何轉換。如果兩個表達式有相似的存儲類,則直接按它們的值進行比較;否則按類值進行比較。
請看下面例子:
sqlite> selectROWID,b,typeof(i),i>'2.9' from domain ORDER BY b;
rowid b typeof(i i>'2.9'
5 NULL null NULL
1 3.142 real 1
3 3142 integer 1
2 3.142 real 1
4 1B blob 1
也算是“強類型(STRICT TYPING)”
如果你需要比類型親和性更強的域完整性,可以使用CHECK約束。你可以使用一個單獨的內置函數和一個CHECK約束來實現一個“假的”強類型。