一、約束 Constraints
在上一篇隨筆的結(jié)尾,我提到了約束, 但是在那里我把它翻譯成了限定符,不太準(zhǔn)確,這里先更正一下,應(yīng)該翻譯成約束更貼切一點(diǎn)。 那么什么是約束呢?
我們?cè)跀?shù)據(jù)庫(kù)中存儲(chǔ)數(shù)據(jù)的時(shí)候,有一些數(shù)據(jù)有明顯的約束條件。 比如一所學(xué)校關(guān)于教師的數(shù)據(jù)表,其中的字段列可能有如下約束:
年齡 - 至少大于20歲。如果你想錄入一個(gè)小于20歲的教師,系統(tǒng)會(huì)報(bào)錯(cuò)
國(guó)籍 - 默認(rèn)中國(guó)。所謂默認(rèn),就是如果你不填寫,系統(tǒng)自動(dòng)填上默認(rèn)值
姓名 - 不能為空。每個(gè)人都有名字嘛
員工號(hào) - 唯一。這個(gè)可不能亂,工資發(fā)錯(cuò)了就麻煩了
上面提到的大于、默認(rèn)、不能為空、唯一等等,就是數(shù)據(jù)的約束條件。 我們?cè)谟?CREATE TABLE 創(chuàng)建表的時(shí)候,就應(yīng)該將每個(gè)字段列的約束條件事先說(shuō)明(如果有的話), 以后再往表里輸入數(shù)據(jù)的時(shí)候,系統(tǒng)會(huì)自動(dòng)為我們檢查是否滿足約束條件,如果不滿足系統(tǒng)會(huì)報(bào)錯(cuò)。
SQLite 常用約束如下
NOT NULL - 非空
UNIQUE - 唯一
PRIMARY KEY - 主鍵
FOREIGN KEY - 外鍵
CHECK - 條件檢查
DEFAULT - 默認(rèn)
二、主鍵 PRIMARY KEY
我們還是進(jìn)入 SQLite 命令行環(huán)境,建立一個(gè) test.db 數(shù)據(jù)庫(kù)用來(lái)做實(shí)驗(yàn),如下
復(fù)制代碼 代碼如下:
myqiao@ubuntu:~/My Documents/db$ sqlite3 test.db
-- Loading resources from /home/myqiao/.sqliterc
SQLite version 3.7.4
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .tables
sqlite>
運(yùn)行 .tables 命令沒(méi)有返回,說(shuō)明數(shù)據(jù)庫(kù)是空的。如果你的數(shù)據(jù)庫(kù)里面有內(nèi)容并影響到下面的實(shí)驗(yàn), 你可以用我們上一篇學(xué)的 DROP TABLE 來(lái)刪除造成影響的表, 或者用 ALTER TABLE ... RENAME TO ... 來(lái)改名。
--------------------------------------------------------------------------------
下面言歸正轉(zhuǎn),我們來(lái)說(shuō)說(shuō)主鍵 PRIMARY KEY 。
首先,數(shù)據(jù)表中每一條記錄都有一個(gè)主鍵, 這就像我們每的身份證號(hào)碼、員工號(hào)、銀行帳號(hào); 反過(guò)來(lái)也可以說(shuō),每一個(gè)主鍵對(duì)應(yīng)著一條數(shù)據(jù)記錄。 所以,主鍵必須是唯一的。
其次,一般情況下主鍵同時(shí)也是一個(gè)索引,所以通過(guò)主鍵查找記錄速度比較快。
第三,在關(guān)系型數(shù)據(jù)庫(kù)中,一個(gè)表的主鍵可以作為另外一個(gè)表的外鍵, 這樣,這兩個(gè)表之間就通過(guò)這個(gè)鍵建立了關(guān)系。
最后,主鍵一般是整數(shù)或者字符串,只要保證唯一就行。 在 SQLite 中,主鍵如果是整數(shù)類型,該列的值可以自動(dòng)增長(zhǎng)。
--------------------------------------------------------------------------------
下面我們來(lái)做實(shí)驗(yàn)
復(fù)制代碼 代碼如下:
sqlite>
sqlite> CREATE TABLE Teachers(Id integer PRIMARY KEY,Name text);
sqlite> .tables
Teachers
sqlite> INSERT INTO Teachers(Name) Values('張三');
sqlite> INSERT INTO Teachers(Name) Values('李四');
sqlite> INSERT INTO Teachers(Name) Values('王二麻子');
sqlite> SELECT * FROM Teachers;
Id Name
---------- ----------
1 張三
2 李四
3 王二麻
sqlite> INSERT INTO Teachers(Id,Name) Values(2,'孫悟空');
Error: PRIMARY KEY must be unique
sqlite>
--------------------------------------------------------------------------------
我們先新建了一個(gè) Teachers 表,并設(shè)置了兩個(gè)字段列,其中 Id 字段列為主鍵列。 然后,我們向其中插入三條數(shù)據(jù)并查詢,反饋一切正常。
注意:在插入前三條數(shù)據(jù)的時(shí)候,命令中并沒(méi)有明確指明 Id 的值,系統(tǒng)自動(dòng)賦值,并且數(shù)值自動(dòng)增長(zhǎng)。
插入第四條數(shù)據(jù)的時(shí)候,我給了一個(gè)明確的 Id 編號(hào)為 2,因?yàn)槔钏牡木幪?hào)已經(jīng)是 2 了, 所以系統(tǒng)提示我錯(cuò)誤:主鍵必須唯一。
三、默認(rèn)值 DEFAULT
有一些特別的字段列,在每一條記錄中,他的值基本上都是一樣的。只是在個(gè)別情況下才改為別的值,這樣的字段列我們可以給他設(shè)一個(gè)默認(rèn)值。
下面我們來(lái)做實(shí)驗(yàn)
復(fù)制代碼 代碼如下:
sqlite>
sqlite> DROP TABLE Teachers;
sqlite> .tables
sqlite>
sqlite> CREATE TABLE Teachers(Id integer PRIMARY KEY,Name text,Country text DEFAULT '中國(guó)');
sqlite> .tables
Teachers
sqlite> INSERT INTO Teachers(Name) Values('張三');
sqlite> INSERT INTO Teachers(Name) Values('李四');
sqlite> INSERT INTO Teachers(Name) Values('王二麻子');
sqlite> INSERT INTO Teachers(Name,Country) Values('孫悟空','天庭');
sqlite> SELECT * FROM Teachers;
Id Name Country
---- --------------- ---------------
1 張三 中國(guó)
2 李四 中國(guó)
3 王二麻子 中國(guó)
4 孫悟空 天庭
sqlite>
先把之前的 Teachers 表刪除,然后重新創(chuàng)建。這回 Teachers 表多了一個(gè) Country 字段, 并且設(shè)置默認(rèn)值為“中國(guó)”,然后我們插入四條數(shù)據(jù)到 Teachers 表。
前三條數(shù)據(jù)都沒(méi)有明確指明 Country 字段的值,只有第四條數(shù)據(jù)指明了“孫悟空”的 Country 為“天庭”。
查詢數(shù)據(jù),發(fā)現(xiàn)前三條數(shù)據(jù)都填上了默認(rèn)值,實(shí)驗(yàn)成功。
--------------------------------------------------------------------------------
數(shù)據(jù)顯示有點(diǎn)走樣,命令 .width 4 15 15 設(shè)置的列寬,可以通過(guò) .show 查看, 可能是因?yàn)橹形牡脑?,所以沒(méi)有對(duì)齊。
四、非空 NOT NULL
有一些字段我們可能一時(shí)不知到該填些什么,同時(shí)它也沒(méi)設(shè)定默認(rèn)值, 當(dāng)添加數(shù)據(jù)時(shí),我們把這樣的字段空著不填,系統(tǒng)認(rèn)為他是 NULL 值。
但是還有另外一類字段,必須被填上數(shù)據(jù),如果不填,系統(tǒng)就會(huì)報(bào)錯(cuò)。 這樣的字段被稱為 NOT NULL 非空字段,需要在定義表的時(shí)候事先聲明。
下面我們來(lái)做實(shí)驗(yàn)
復(fù)制代碼 代碼如下:
sqlite>
sqlite> DROP TABLE Teachers;
sqlite> .tables
sqlite>
sqlite> CREATE TABLE Teachers(Id integer PRIMARY KEY,Name text,Age integer NOT NULL,City text);
sqlite> .tables
Teachers
sqlite> INSERT INTO Teachers(Name,Age) Values('Alice',23);
sqlite> INSERT INTO Teachers(Name,Age) Values('Bob',29);
sqlite> INSERT INTO Teachers(id,Name,Age) Values(6,'Jhon',36);
sqlite> SELECT * FROM Teachers;
Id Name Age City
---- --------------- --------------- ---------------
1 Alice 23 NULL
2 Bob 29 NULL
6 Jhon 36 NULL
sqlite> INSERT INTO Teachers(Name) Values('Mary');
Error: Teachers.Age may not be NULL
sqlite>
還是先刪除舊表,創(chuàng)建新表。
這回 Teachers 表聲明了一個(gè) NOT NULL 字段 Age,同時(shí)還有一個(gè)可以為 NULL 的字段 City
插入前三條數(shù)據(jù)都沒(méi)有指定 City 的值,查詢可以看到 City 字段全部為空
注意:這里的 NULL 只是對(duì)“什么都沒(méi)有”的一種顯示形式, 可以通過(guò) .nullvalue 命令改為別的形式,具體見(jiàn)第一篇
插入第四條數(shù)據(jù)時(shí)沒(méi)有指定 Age 的值,系統(tǒng)就報(bào)錯(cuò)了: Teachers.Age 不能為空
五、 唯一 UNIQUE
這一約束很好理解,除了主列以為,還有一些列也不能有重復(fù)值。不多說(shuō),直接看代碼
復(fù)制代碼 代碼如下:
sqlite>
sqlite> DROP TABLE Teachers;
sqlite> .tables
sqlite>
sqlite> CREATE TABLE Teachers(Id integer PRIMARY KEY,Name text UNIQUE);
sqlite> .tables
Teachers
sqlite> INSERT INTO Teachers(Name) VALUES('Alice');
sqlite> INSERT INTO Teachers(Name) VALUES('Bob');
sqlite> INSERT INTO Teachers(Name) VALUES('Jane');
sqlite> INSERT INTO Teachers(Name) VALUES('Bob');
Error: column Name is not unique
sqlite>
這次的 Teachers 表只有 Name 這一列,但是 Name 列不能有重復(fù)值??梢钥吹?,到我們第二次插入 Bob 時(shí),系統(tǒng)就報(bào)錯(cuò)了。
六、 條件檢查 CHECK
某些值必須符合一定的條件才允許存入,這是就需要用到這個(gè) CHECK 約束。
復(fù)制代碼 代碼如下:
sqlite>
sqlite> DROP TABLE Teachers;
sqlite> .tables
sqlite>
sqlite> CREATE TABLE Teachers(Id integer PRIMARY KEY,Age integer CHECK(Age>22));
sqlite> .tables
Teachers
sqlite> INSERT INTO Teachers(Age) VALUES(45);
sqlite> INSERT INTO Teachers(Age) VALUES(33);
sqlite> INSERT INTO Teachers(Age) VALUES(23);
sqlite> INSERT INTO Teachers(Age) VALUES(21);
Error: constraint failed
sqlite>
Age 字段要求必須大于 22,當(dāng)插入的數(shù)據(jù)小于22時(shí),系統(tǒng)報(bào)錯(cuò)。
七、外鍵 FOREIGN KEY
現(xiàn)在,我們的數(shù)據(jù)庫(kù)中已經(jīng)有 Teachers 表了,假如我們?cè)俳⒁粋€(gè) Students 表, 要求 Students 表中的每一個(gè)學(xué)生都對(duì)應(yīng)一個(gè) Teachers 表中的教師。
很簡(jiǎn)單,只需要在 Students 表中建立一個(gè) TeacherId 字段,保存對(duì)應(yīng)教師的 Id 號(hào), 這樣,學(xué)生和教師之間就建立了關(guān)系。
--------------------------------------------------------------------------------
問(wèn)題是:我們有可能給學(xué)生存入一個(gè)不在 Teachers 表中的 TeacherId 值, 而且發(fā)現(xiàn)不了這個(gè)錯(cuò)誤。
這種情況下,可以把 Students 表中 TeacherId 字段聲明為一個(gè)外鍵, 讓它的值對(duì)應(yīng)到 Teachers 表中的 Id 字段上。
這樣,一旦在 Students 表中存入一個(gè)不存在的教師 Id ,系統(tǒng)就會(huì)報(bào)錯(cuò)。
復(fù)制代碼 代碼如下:
sqlite>
sqlite> .tables
Teachers
sqlite> CREATE TABLE Students (Id integer PRIMARY KEY, TeacherId integer, FOREIGN KEY(TeacherId) REFERENCES Teachers(id) );
sqlite> .tables
Students Teachers
sqlite> SELECT * FROM Teachers;
Id Age
---- ---------------
1 40
2 33
3 23
sqlite> INSERT INTO Students(TeacherId) VALUES(1);
sqlite> INSERT INTO Students(TeacherId) VALUES(3);
sqlite> INSERT INTO Students(TeacherId) VALUES(9);
sqlite> SELECT * FROM Students;
Id TeacherId
---- ---------------
1 1
2 3
3 9
sqlite>
這里建立了 Students 表,并且把 TeacherId 作為外鍵與 Teachers 表的 Id 列相對(duì)應(yīng)。
問(wèn)題來(lái)了:插入的前兩條數(shù)據(jù)沒(méi)問(wèn)題,因?yàn)?Id 編號(hào) 1、3 都在 Teachers 表中; 但是數(shù)字 9 并不在 Teachers 表中,不但沒(méi)有報(bào)錯(cuò),系統(tǒng)還保存進(jìn)去了,這是為什么呢?
據(jù)說(shuō) SQLite 的外鍵約束默認(rèn)情況下并不是開(kāi)啟的,如果你需要這個(gè)功能,你可能需要下載源代碼版本,設(shè)置每個(gè)編譯參數(shù),然后重新編譯,這樣你就得到支持外鍵的 SQLite 了。
您可能感興趣的文章:- 讓Sqlite脫離VC++ Runtime獨(dú)立運(yùn)行的方法
- C++操作SQLite簡(jiǎn)明教程
- Sqlite 常用函數(shù)封裝提高Codeeer的效率
- C#中使用SQLite數(shù)據(jù)庫(kù)的方法介紹
- ASP.NET(C#)中操作SQLite數(shù)據(jù)庫(kù)實(shí)例
- VC++基于Dx實(shí)現(xiàn)的截圖程序示例代碼
- VC++實(shí)現(xiàn)輸出GIF到窗體并顯示GIF動(dòng)畫的方法
- VC++開(kāi)發(fā)中完美解決頭文件相互包含問(wèn)題的方法解析
- 淺析VC++中的頭文件包含問(wèn)題
- VC++操作SQLite簡(jiǎn)單實(shí)例