大多數 sql 數據庫引擎 (據我們所知,除 sqlite 之外的所有 sql 數據庫引擎)都使用嚴格的靜態類型。使用靜態類型,值的類型便由它的容器 -- 存儲值的特定的列 -- 來決定。
sqlite 使用更通用的動態類型系統。在 sqlit 中,值的數據類型與值本身相關,而不是與它的容器。sqlite 的動態類型系統與其它數據庫引擎的常用靜態類型系統是向后兼容的,在這個意義上,工作在靜態類型數據庫上的 sql 語句應該以同樣的方式工作在 sqlite 中。然而,sqlite 中的動態類型允許它做傳統的嚴格類型的數據庫所不能做的事。
1.0 存儲類型與數據類型
存儲在 sqlite 數據庫中的每個值(或是由數據庫引擎所操作的值)都有一個以下的存儲類型:
- null. 值是空值。
- integer. 值是有符號整數,根據值的大小以1,2,3,4,6 或8字節存儲。
- real. 值是浮點數,以8字節 ieee 浮點數存儲。
- text. 值是文本字符串,使用數據庫編碼(utf-8, utf-16be 或 utf-16le)進行存儲。
- blob. 值是一個數據塊,按它的輸入原樣存儲。
注意,存儲類型比數據類型更籠統。以 integer 存儲類型為例,它包括6種不同的長度不等的整數類型,這在磁盤上是不同的。但是只要 integer 值從磁盤讀取到內存進行處理,它們就被轉換為更為一般的數據類型(8字節有符號整型)。因此在一般情況下,“存儲類型” 與 “數據類型” 沒什么差別,這兩個術語可以互換使用。
sqlite 版本3數據庫中的任何列,除了整型主鍵列,都可用于存儲任何存儲類型的值。
sql 語句中的任何值,無論它們是嵌入到 sql 語句中的字面量還是綁定到預編譯 sql 語句中的參數,都有一個隱含的存儲類型。在下述情況下,數據庫引擎會在執行查詢時在數值存儲類型(integer 和 real)和 text 之間進行轉換。
1.1 布爾類型
sqlite 并沒有單獨的布爾存儲類型,而是將布爾值存儲為整數 0 (false) 和 1 (true)。
1.2 日期和時間類型
sqlite 沒有另外的存儲類型來存儲日期和時間。sqlite 的內置的日期和時間函數能夠將日期和時間存為 text、real 或 integer 值:
- text iso8601 字符串 ("yyyy-mm-dd hh:mm:ss.sss")。
- real 儒略日數 (julian day numbers),按照前公歷,自格林威治時間公元前4714年11月24日中午以來的天數。
- integer unix 時間,自 1970-01-01 00:00:00 utc 以來的秒數。
應用可以選擇這些格式中的任一種存儲日期和時間,并使用內置的日期和時間函數在這些格式間自由轉換。
2.0 類型親和性
為了最大限度地提高 sqlite 和其它數據庫引擎之間的兼容性,sqlite 支持列的“類型親和性”的概念。列的類型親和性是指數據存儲于該列的推薦類型。這里重要的思想是類型是推薦的,而不是必須的。任何列仍可以存儲任何類型的數據。這只是讓一些列有選擇性地優先使用某種存儲類型。一個列的首選存儲類型被稱為它的“親和性”。
每個 sqlite 3 數據庫中的列都歸于以下的類型親和性中的一種:
- text
- numeric
- integer
- real
- none
一個具有 text 親和性的列使用存儲類型 null、 text 或 blob 存儲所有數據。如果數值數據被插入到一個具有 text 親和性的列,則數據在存儲前被轉換為文本形式。
數值親和性的列可能包含了使用所有五個存儲類的值。當插入文本數據到數值列時,該文本的存儲類型被轉換成整型或實數(按優先級排序)如果這種轉換是無損或可逆的的話。對于文本與實數類型之間的轉換,如果前15個重要十進制數字被保留的話,sqlite認為這種轉換是無損并可逆的。如果文本不能無損地轉換成整型或實數,那這個值將以文本類型存儲。不要試圖轉換null或blob值。
一個字符串可能看上去像帶有小數點和/或指數符的浮點文字,但只要這個值可以用一個整型表示,數值親和性就會把它轉換成一個整型。因此,字符串‘3.0e+5'以整型300000,而不是浮點值30000.0的形式存儲在一個數值親和性的列里。
一個使用整型親和性的列與具有數值親和性的列表現一致。只是在cast表達式里,它們之間的區別體現得明顯。
除了強制將整型值轉換成浮點表示外,一個具有實數親和性的列與具有數值親和性的列表現一致(作為一個內部的優化,為了少占用空間,無小數部分且存儲在實數親和性列上的小浮點值以整型形式寫到磁盤,讀出時自動轉換回浮點值。在sql級別,這種優化是完全不可見的,并且只能通過檢查數據庫文件的原始比特檢測到)。
一個具有none親和性的列不能從一種存儲類型轉換成另一種,也不要試圖強制對它進行轉換。
2.1 列親和性測定
列的親和性是由它的聲明類型決定的,按照以下順序所示的規則:
1. 如果聲明類型包含字符串“int”,那它被指定為整型親和性;
2. 如果列的聲明類型包含任何“char”、“clob”或“text”字符串,那么該列具有文本親和性。注意:varchar類型包含“char”并且被指定為文本親和性;
3. 如果列的聲明類型包含“blob”或者沒有指定類型,那這列具有none親和性;
4. 如果列的聲明類型包含任何“real”、“floa”或“doub”字符串,則該列具有實數親和性;
5. 否則,它將具有數值親和性。
注意:判定列親和性規則的順序是很重要的。一個具有“charint”聲明類型的列將匹配規則1和2,但是規則1優先所有該列具有整型親和性。
2.2 親和性名字實例
下表顯示了有多少從更傳統的sql實現的常用數據類型名,通過上一節介紹的五個規則被轉換成各種親和性類型。這張表只顯示了sqlite可接受的一小部分數據類型名。注意:跟在類型名后,括號內數值參數(如:varchar(255))將被sqlite忽略 - sqlite不對字符串、blobs或數值的長度強加任何限制(除了大型全局sqlite_max_length限制)。
注意: 因為在“point”末尾的“int”,一個“ floating point”聲明類型 會被賦予整型親和性,而不是實數親和性。而且“string”聲明類型具有數值親和性,而不是文本親和性。
2.3 列親和性行為實例
以下sql演示當有值插入到一張表時,sqlite如何使用列親和性實現類型轉換的:
1
2
3
4
5
6
7
|
create table t1( t text, -- text affinity by rule 2 nu numeric , -- numeric affinity by rule 5 i integer , -- integer affinity by rule 1 r real , -- real affinity by rule 4 no blob -- no affinity by rule 3 ); |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
-- values stored as text, integer, integer, real, text.(值分別以文本、整型、整型、實數、文本形式存儲) insert into t1 values ( '500.0' , '500.0' , '500.0' , '500.0' , '500.0' ); select typeof(t), typeof(nu), typeof(i), typeof(r), typeof( no ) from t1; text| integer | integer | real |text -- values stored as text, integer, integer, real, real. delete from t1; insert into t1 values (500.0, 500.0, 500.0, 500.0, 500.0); select typeof(t), typeof(nu), typeof(i), typeof(r), typeof( no ) from t1; text| integer | integer | real | real -- values stored as text, integer, integer, real, integer. delete from t1; insert into t1 values (500, 500, 500, 500, 500); select typeof(t), typeof(nu), typeof(i), typeof(r), typeof( no ) from t1; text| integer | integer | real | integer -- blobs are always stored as blobs regardless of column affinity. delete from t1; insert into t1 values (x '0500' , x '0500' , x '0500' , x '0500' , x '0500' ); select typeof(t), typeof(nu), typeof(i), typeof(r), typeof( no ) from t1; blob|blob|blob|blob|blob -- nulls are also unaffected by affinity delete from t1; insert into t1 values ( null , null , null , null , null ); select typeof(t), typeof(nu), typeof(i), typeof(r), typeof( no ) from t1; null | null | null | null | null |
3.0 比較表達式
同標準sql一樣,sqlite 3支持如下的比較操作符:"=", "==", "<", "<=", ">", ">=", "!=", "<>", "in", "not in", "between", "is", 以及 "is not"。
3.1 排序規則
比較的結果與操作數的存儲類型有關,同時依據以下的規則:
- null值小于其他任何值(包括另外一個null)
- integer或real小于text,blob值;若兩個integer(或者real)比較,則按照實際的數值進行。
- text小于blob,若兩個text比較,結果則由適當的整理順序決定
- 若兩個blod比較,與memcmp()的結果一致
3.2 操作數進行比較時的相似性
在進行值的比較之前,sqlite會嘗試在存儲類integer、real和/或text之間進行值的轉換。在比較之前嘗不嘗試進行轉換完全取決于操作數的相似性。操作數相似性的判定規則如下:
- 只是對一個列中的值進行引用的表達式同被引用的列具有完全相同的相似性。注意,如果x、y.z代表的是列的名稱,那么+x和+y.z可以認為是為了判定其相似性的表達式。
- "cast(expr as type)"所表示的表達式同類型定義為"type"的列具有完全相同的相似性。
- 其它情況下的表達式具有none相似性。
3.3 比較前的類型轉換
“應用相似性”("apply affinity")的意思是,當且僅當所涉及的轉換是無損且可逆的情況下,將一個操作數轉換為某特定的存儲類型。在進行比較之前對比較運算符的操作數應用相似性的規則如下按順序所示:
- 如果其中的一個操作數具有integer、real或者numeric相似性而另外一個操作數具有text或者none相似性,那么就要對這另外一個操作數應用numeric 相似性。
- 如果其中的一個操作數具有text相似性而另外一個具有none相似性,那么就要對這另外一個操作數應用text相似性。
- 其它情況下不會應用任何相似性,兩個操作數按照各自的原樣進行比較。
將表達式"a between b and c"看作兩個單獨的二元比較運算"a >= b and a <= c",即使這么一來,可能會造成其中的a在兩次比較中會被應用不同的相似性,也要這么處理。datatype conversions in comparisons of the form 在"x in (select y ...)"這種形式的比較中,數據類型的轉換完全同"x=y"一樣進行處理。表達式"a in (x, y, z, ...)" 同"a = +x or a = +y or a = +z or ..."等價。換句話說,in運算符右側的值(本例中就是"x", "y", and "z")被看作是無相似性的,即使它們湊巧是某列的值或者是cast表達式。
3.4 比較示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
create table t1( a text, -- text affinity b numeric , -- numeric affinity c blob, -- no affinity d -- no affinity ); -- values will be stored as text, integer, text, and integer respectively insert into t1 values ( '500' , '500' , '500' , 500); select typeof(a), typeof(b), typeof(c), typeof(d) from t1; text| integer |text| integer -- because column "a" has text affinity, numeric values on the -- right-hand side of the comparisons are converted to text before -- the comparison occurs. select a < 40, a < 60, a < 600 from t1; 0|1|1 -- text affinity is applied to the right-hand operands but since -- they are already text this is a no-op; no conversions occur. select a < '40' , a < '60' , a < '600' from t1; 0|1|1 -- column "b" has numeric affinity and so numeric affinity is applied -- to the operands on the right. since the operands are already numeric, -- the application of affinity is a no-op; no conversions occur. all -- values are compared numerically. select b < 40, b < 60, b < 600 from t1; 0|0|1 -- numeric affinity is applied to operands on the right, converting them -- from text to integers. then a numeric comparison occurs. select b < '40' , b < '60' , b < '600' from t1; 0|0|1 -- no affinity conversions occur. right-hand side values all have -- storage class integer which are always less than the text values -- on the left. select c < 40, c < 60, c < 600 from t1; 0|0|0 -- no affinity conversions occur. values are compared as text. select c < '40' , c < '60' , c < '600' from t1; 0|1|1 -- no affinity conversions occur. right-hand side values all have -- storage class integer which compare numerically with the integer -- values on the left. select d < 40, d < 60, d < 600 from t1; 0|0|1 -- no affinity conversions occur. integer values on the left are -- always less than text values on the right. select d < '40' , d < '60' , d < '600' from t1; 1|1|1 |
若示例中的比較被替換——例如"a<40"被寫作"40>a"——所有的結果依然相同相同。
4.0 操作符
所有的數學運算符(+, -, *, /, %, <<, >>, &, and |)在展開前會將兩個操作數放入 numeric 儲存類。即使這個過程是有損和不可逆轉的。一個 null 操作數在數學運算符上產生一個 null 結果。在數算運算符上的操作數不被視為數字,null 并不會被轉為0或0.0。
5.0 排序, 分組 和 組合查詢
當查詢結果使用 order by 子句排序時, 存儲類型的null空值是排在第一位的, 其次是integer和散布在數字順序的real數據, 其次是按照核對序列順序的text值, 最后為memcmp() order 的blob值. 排序之前不會出現任何存儲類型轉換.
當使用group by 子句分組時不同類型的值被認為是不同的數據, 除了integer 和 real 值如果他們數值相等則被認為是相同的的數據. 沒有任何親和性適用于group by 子句結果的任意值.
組合查詢使用 union, intersect 和 except 在數據之間執行隱式的比較. 沒有任何親和性適用于與union, intersect, 或者 except關聯的隱式比較的運算數 - 數據的比較就像這樣.
6.0 整理序列
當 sqlite 比較兩個字符串時,它使用一個整理序列或整理函數(一物兩表)來決定當兩個字符串相同時,哪個字符串值更高。sqlite 擁有三個內建整理函數:binary, nocase, 和 rtrim。
- binary - 使用 memcmp() 比較字符串,無視文本編碼。
- nocase - 與二進制比較相同,除了 ascii 的26個大寫字母在比較前將會轉為其小寫形勢。注意,只有 ascii 字符會大小寫轉化。 由于表大小的需求,sqlite 并不會嘗試 utf 大小寫轉化。
- rtrim - 與二進制比較相同,除了尾部空格符將被忽略。
應用可以通過 sqlite3_create_collation() 接口注冊額外的整理函數。
6.1 設定sql中的排列順序
每個表中的每一個列都具有一個相關的排序函數。如果沒有顯式地定義排序函數,那么,就會缺省使用binary作為排序函數。列定義中的collate子句可為列定義一個可選的排序函數。
對于二元比較運算符(=, <, >, <=, >=, !=, is, and is not)來說,判定到底使用哪個排序函數的規則按順序如下所列:
- 如果兩個操作數中有任意一個操作數具有使用后綴collate運算符顯式定義的排序函數,那么就會用該函數進行比較,如果兩個操作數都有的情況下,優先使用左操作數的排序函數。
- 如果兩個操作數中任意一個操作數是一個列,那么就會使用該列的排序函數進行比較,但在兩個操作數都是列的情況下,優先使用左操作數對應的列的排序函數。為了達到這句話的目的,列名前帶有1個或多個一元運算符"+"的,仍然按原列名處理。
- 其它情況下,采用binary排序函數進行比較。
比較運算中的操作數,如果在它的任何子表達式中使用了后綴 collate運算符,就可以認為是具有顯式的排序函數(上文中的規則1)。 再者,如果在比較表達式中的任何地方使用了 collate運算符,那么該運算符所定義的排序函數就會用于字符串的比較,而無論在表達式中出現了表中的哪一列。如果在比較中的任何地方出現了兩個或多個 collate運算符子表達式,無論在表達式中嵌入得多深,也無論表達式是怎么使用括號的,都會使用出現在最左側的顯式排序函數。
表達式"x between y and z"從邏輯上講,同"x >= y and x <= z"這兩個比較運算完全等價,在使用排序函數時它們倆要象兩個本來就是獨立的比較運算一樣進行處理。在判定排列順序時,表達式"x in (select y ...)"處理方式完全同表達式"x = y"一樣,形如"x in (y, z, ...)"的表達式,排列順序完全同x的排列順序一樣。
作為 select語句的一個部分,order by子句中排序條件也可以通過使用collate運算符設定排列順序,如果設定了排序時就要按照設定的排序函數進行排序。否則,如果order by子句使用的排序表達式是一個列,那么該列的排列順序就用于判定排列順序。如果該排序表達式不是列并且也無collate子句,就會使用binary排列順序。
6.2 整理序列示例
下面的示例將識別整理序列,決定 sql 語句的文本比較結果。注意,在文本比較時,如果是數字,二進制或null值,整理序列可能并沒有被使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
create table t1( x integer primary key , a, /* collating sequence binary */ b collate binary , /* collating sequence binary */ c collate rtrim, /* collating sequence rtrim */ d collate nocase /* collating sequence nocase */ ); /* x a b c d */ insert into t1 values (1, 'abc' , 'abc' , 'abc ' , 'abc' ); insert into t1 values (2, 'abc' , 'abc' , 'abc' , 'abc' ); insert into t1 values (3, 'abc' , 'abc' , 'abc ' , 'abc' ); insert into t1 values (4, 'abc' , 'abc ' , 'abc' , 'abc' ); /* a=b 的文本比較表現為使用 binary (二進制)整理序列。 */ select x from t1 where a = b order by x; --結果 1 2 3 /* a=b 的文本比較表現為使用 rtrim 整理序列。 */ select x from t1 where a = b collate rtrim order by x; --結果 1 2 3 4 /* d=a 的文本比較表現為使用 nocase 整理序列。 */ select x from t1 where d = a order by x; --結果 1 2 3 4 /* a=d 的文本比較表現為使用 binary (二進制)整理序列。 */ select x from t1 where a = d order by x; --結果 1 4 /* 'abc' =c 的文本比較表現為使用 rtrim (二進制)整理序列。 */ select x from t1 where 'abc' = c order by x; --結果 1 2 3 /* c= 'abc' 的文本比較表現為使用 rtrim 整理序列。 */ select x from t1 where c = 'abc' order by x; --結果 1 2 3 /* 分組表現為使用 nocase 整理序列(值 'abc' , 'abc' 和 'abc' ** 被分為同一組)。*/ select count (*) from t1 group by d order by 1; --結果 4 /* 分組表現為使用 binary 整理序列(值 'abc' , 'abc' 和 'abc' ** 被分為不同的組)。*/ select count (*) from t1 group by (d || '' ) order by 1; --結果 1 1 2 /* 列c排序表現為使用 rtrim 整理序列。*/(譯注:sorting or column c 疑為 sorting of ...誤寫) select x from t1 order by c, x; --結果 4 1 2 3 /* (c|| '' )排序表現為使用 binary 整理序列。*/ select x from t1 order by (c|| '' ), x; --結果 4 2 3 1 /* 列c排序表現為使用 nocase 整理序列。*/ select x from t1 order by c collate nocase, x; --結果 2 4 3 1 |