1. 程式人生 > >MySQL中對三種約束的支援

MySQL中對三種約束的支援

引言

文章地址:http://leekai.me/?p=198
關係型資料庫通過約束機制可以保證資料的完整性。資料完整性通常由三種形式:
1. 實體完整性:即表中有一個主鍵。
2. 域完整性:資料值滿足指定的條件。
3. 參照完整性:表示與參照表的關係和資料約束,即外來鍵。

一、MySQL中的資料完整性

注:以下所討論的主題均基於InnoDB儲存引擎。

1.實體完整性

實體完整性在MySQL中表現為設定主鍵約束和唯一性約束,即primary key與unique key。這兩個約束均會在資料庫中建立對應的索引。

2.域完整性

域完整性又稱為使用者自定義完整性,保證資料值滿足使用者指定的條件。在InnoDB中域的完整性通過以下幾點:Default(資料的預設值),not null(資料非空)。在InnoDB中不支援check約束,可以通過enum型別變數去約束離散的值,或者設定觸發器來檢查資料合法值。

3.參照完整性

參照完整性通過外來鍵實現,並會建立索引。

InnoDB中的約束:

InnoDB支援以下幾種約束:
- primary key
- unique key
- foreign key
- default
- not null
對應的表格如下:

約束型別 MySQL SQL Server Oracle
主鍵約束 生成唯一索引 生成唯一索引 使用已存在的索引或者建立新索引
外來鍵約束 生成索引 不生成索引 不生成索引
唯一約束 生成唯一索引 生成唯一索引 使用已存在的索引或者建立新索引

二、MySQL中的check約束

MySQL所有的儲存引擎均不支援check約束,MySQL會對check子句進行分析,但是在插入資料時會忽略,因此check並不起作用,因此實現對資料約束有兩種方法:a:在mysql種約束,如使用enum型別或者觸發器等。b:在應用程式裡面對資料進行檢查再插入。
這裡著重討論再mysql種使用enum和觸發器約束資料。

1. enum型別

enum型別將資料的取值限定在在一個離散的集合中。在建立表的時候可以制定資料為enum
如:

create table t (
    id int auto_increment primary
key, sex enum('F', 'M'), name varchar(20) not null );

在MySQL中,enum的變數是按照整數與其列表對應,從1開始,比如上面的表中F的索引為1,M的索引為2。表中的輸入如下:

mysql> select * from t;
+----+------+------+
| id | sex  | name |
+----+------+------+
|  1 | F    | z    |
|  2 | M    | t    |
|  3 | F    | k    |
+----+------+------+
3 rows in set (0.02 sec)

如果要檢索根據sex列檢索資料,則可以根據列舉中的字串檢索,結果如下

mysql> select * from t where sex = 'F';
+----+------+------+
| id | sex  | name |
+----+------+------+
|  1 | F    | z    |
|  3 | F    | k    |
+----+------+------+
2 rows in set (0.03 sec)

還可以根據列舉列的索引檢索,結果如下,其中1代表F的索引為1。

mysql> select * from t where sex = 1;
+----+------+------+
| id | sex  | name |
+----+------+------+
|  1 | F    | z    |
|  3 | F    | k    |
+----+------+------+
2 rows in set (0.02 sec)

當插入值不符合列舉列表中的值時,即插入非法資料會發生什麼呢,如下:

mysql> insert into t (sex, name) value ('Mae','y' );
Query OK, 1 row affected, 1 warning (0.03 sec)

執行結果有一個warning,檢視warning如下:

mysql> show warnings;
+---------+------+------------------------------------------+
| Level   | Code | Message                                  |
+---------+------+------------------------------------------+
| Warning | 1265 | Data truncated for column 'sex' at row 1 |
+---------+------+------------------------------------------+
1 row in set (0.02 sec)

資料被截斷,可以看出此時資料庫並沒有拒絕插入,而是資料被截斷,具體是截斷成什麼,可以檢視一下:

mysql> select * from t;
+----+------+------+
| id | sex  | name |
+----+------+------+
|  1 | F    | z    |
|  2 | M    | t    |
|  3 | F    | k    |
|  4 |      | y    |
+----+------+------+

結果顯示是一個空字串(但不同於null),這裡優於sql_mode設定的問題(注:Mysql之SQL Mode用法詳解),因此資料庫會接受插入的非法值,但提示warning。
並且空字串的索引為0,可以通過使用sex列檢索資料驗證:

mysql> select * from t where sex = 0;
+----+------+------+
| id | sex  | name |
+----+------+------+
|  4 |      | y    |
+----+------+------+
1 row in set (0.03 sec)

將sql_mode設定為STRICT_TRANS_TABLES會使得資料庫伺服器在插入非法值時拒絕:

mysql> insert into t (sex, name) value ('Mae','y' );
ERROR 1265 (01000): Data truncated for column 'sex' at row 1

2.觸發器[1]

enum僅僅對離散的取值奏效,當遇到需要限定資料範圍的時候,enum則不能使用,因此可以使用觸發器來實現約束。
觸發器會在insert,delete和update命令之前或者之後自動呼叫sql命令或儲存過程。
建立觸發器命令:

create [definer = { user | current_user}]
trigger trigger_name before|after  insert|update|delete
on tbl_name for each row trigger_stmt;

可以在資料更新或者插入異常資料可以根據建立的觸發器對其進行操作。

附:
[1] MySQL技術內幕:InnoDB儲存引擎