MySql數據庫約束

 

  《MySQL技術內幕:InnoDB存儲引擎》本書從源代碼的角度深度解析了InnoDB的體系結構、實現原理、工做機制,並給出了大量實踐,本着將書讀薄的思想,按部就班的記錄對本書學習的讀書筆記。

  關係型數據庫系統和文件系統的一個不一樣點是,關係數據庫自己能保證存儲數據的完整性,不須要應用程序的控制,而文件系統通常須要在程序端進行控制。當前幾乎全部的關係型數據庫都提供了約束(constraits)機制,該機制提供了一條強大而簡易的途徑來保證數據庫中的數據完整性,通常來講,數據完整性有如下三種形式:mysql

(1)實體完整性保證表中有一個主鍵,在InnoDB存儲引擎中,用戶能夠經過定義Primary Key或Unique Key約束來保證明體的完整性,用戶還能夠編寫一個觸發器來保證數據完整性、sql

(2)域完整性保證數據每列的值知足特定的條件。在InnoDB存儲引擎中,域完整性能夠經過如下途徑來保證:數據庫

  a. 選擇適合的數據類型確保一個數據值知足條件架構

  b. 外鍵(Foreign Key)約束學習

  c. 編寫觸發器spa

  d. 還能夠考慮用default約束做爲強制域完整性的一個方面code

(3)參照完整性保證兩張表之間的關係,InnoDB存儲引擎提供瞭如下幾種約束:orm

  a. primary keyblog

  b. unique key索引

  c. foreign key

  d. default

  e. not null

1. 約束的建立和查找

  約束的建立有如下兩種方式:

  (1)表創建時就進行約束定義

  (2)利用alter table命令來進行建立約束

  如下幾點須要關注和注意:

  a. 對Unique Key(惟一索引)的約束,用戶除了在建立時約定,還能夠經過Create Unique Index來建立

  b. 對於主鍵約束耳音,其默認約束名爲PRIMARY,而對於Unique Key約束而言,默認約束名和列名同樣,固然也能夠人爲的指定Unique Key的名字,Foreign Key約束彷佛會有一個比較神祕的默認名稱

1.1 例1:建立表u,設置一個primary key和unique key

mysql> Create table u(
    -> id int,
    -> id_card varchar(18),
    -> name varchar(20),
    -> primary key(id),
    -> unique key(name)
    -> )engine=InnoDB;
Query OK, 0 rows affected (0.56 sec)

mysql> select constraint_name,constraint_type
    -> from
    -> information_schema.table_constraints
    -> where table_schema='test' and table_name='u';
+-----------------+-----------------+
| constraint_name | constraint_type |
+-----------------+-----------------+
| PRIMARY         | PRIMARY KEY     |
| name            | UNIQUE          |
+-----------------+-----------------+
2 rows in set (0.01 sec)

  能夠看到,約束名如上面所說,主鍵的約束名爲PRIMARY,惟一索引的默認約束名與列名相同。

1.2 例2:alter table建立約束

mysql> alter table u
    -> add unique key uk_id(id_card);
Query OK, 0 rows affected (0.19 sec)
Records: 0  Duplicates: 0  Warnings: 0

mysql> select constraint_name,constraint_type
    -> from
    -> information_schema.table_constraints
    -> where table_schema='test' and table_name='u';
+-----------------+-----------------+
| constraint_name | constraint_type |
+-----------------+-----------------+
| PRIMARY         | PRIMARY KEY     |
| name            | UNIQUE          |
| uk_id           | UNIQUE          |
+-----------------+-----------------+
3 rows in set (0.00 sec)

1.3 例3:Foreign key的約束

mysql> create table p(
    -> id int,
    -> u_id int,
    -> primary key(id),
    -> foreign key(u_id) references p(id)
    -> )engine=InnoDB;
Query OK, 0 rows affected (0.41 sec)

mysql> select constraint_name,constraint_type
    -> from
    -> information_schema.table_constraints
    -> where table_schema='test' and table_name='p';
+-----------------+-----------------+
| constraint_name | constraint_type |
+-----------------+-----------------+
| PRIMARY         | PRIMARY KEY     |
| p_ibfk_1        | FOREIGN KEY     |
+-----------------+-----------------+
2 rows in set (0.00 sec)

  在上面的例子中,經過information_schema架構下的表table_constraints來查看當前MySql庫下全部的約束信息。

2. 對錯誤數據的約束

  在某些默認設置下,MySql數據庫容許非法或不正確的數據的插入或更新,又或者能夠在數據庫內部將其轉化爲一個合法的值,如向not null的字段插入一個null值,MySql數據庫會將其更改成0再進行插入,所以數據庫自己沒有對數據的正確性進行約束。

2.1 例1

mysql> Create table a(
    -> id int not null,
    -> data date not null
    -> )engine=InnoDB;
Query OK, 0 rows affected (0.20 sec)

mysql> insert into a select NULL, '2009-02-20';
Query OK, 1 row affected, 1 warning (0.05 sec)
Records: 1  Duplicates: 0  Warnings: 1
mysql> select * from a;
+----+------------+
| id | data       |
+----+------------+
|  0 | 2009-02-20 |
+----+------------+
1 row in set (0.00 sec)

  經過設置參數set sql_mode='strict_trans_tables';對MySql數據庫的輸入值進行了約束,並且針對不一樣的錯誤提示錯誤內容也不一樣。

2.1 例2:enum與set約束

mysql> Create table a(
    -> id int,
    -> sex enum('male','female')
    -> )engine=InnoDB;
Query OK, 0 rows affected (0.17 sec)

mysql> insert into a select 1,'male';
Query OK, 1 row affected (0.06 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> insert into a select 2,'hello';
ERROR 1265 (01000): Data truncated for column 'sex' at row 1

3. 觸發器與約束

3.1 觸發器的認識

  觸發器的做用是在執行insert,delete和update命令以前或以後自動調用sql命令或存儲過程

3.1.1  觸發器的建立

Create
[definer = { user | current_user}]
trigge trigger_name BEFORE|AFTER INSERT|UPDATE|DELETE
on tble_name FOR EACH ROW trigger_stmt

(1)最多能夠爲一個表創建6個觸發器,即分別爲insert,update,delete的before和after各定義一個

(2)只有表才支持觸發器,視圖不支持(臨時表也不支持)

(3)若是before觸發器失敗,則MySQl將不執行請求的操做,此外若是before觸發器或語句自己失敗,MySql將不執行after觸發器(若是有的話)

3.1.2  觸發器的刪除

  DROP TRIGGER trigger_name;

注:觸發器不能更新或覆蓋,爲了修改一個觸發器,必須先刪除它,而後再從新建立

3.2 觸發器約束

  假設有張用戶消費表,每次用戶購買同樣物品後其金額都是減的,若這時有不壞好意的用戶作了一個相似減去一個負值的操做,這樣用戶的錢沒有減小反而不斷增長

mysql> Create table usercash(
    -> userid int not null,
    -> cash int unsigned not null
    -> ,
    -> Primary key(userid))
    -> engine=InnoDB;
Query OK, 0 rows affected (0.16 sec)

mysql> insert into usercash select 1,1000;
Query OK, 1 row affected (0.03 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> update usercash set cash=cash-(-20) where userid = 1;
Query OK, 1 row affected (0.05 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from usercash;
+--------+------+
| userid | cash |
+--------+------+
|      1 | 1020 |
+--------+------+
1 row in set (0.00 sec)

  上面運行的SQL語句對數據庫來講沒有任何的問題,均可以正常運行,不會報錯。但從業務邏輯上來講,這是絕對錯誤的。下面採用觸發器來約束這個邏輯行爲:

mysql> Create table usercash_error_log(
    -> userid int not null,
    -> old_cash int unsigned not null,
    -> new_cash int unsigned not null,
    -> user varchar(30),
    -> time datetime,
    -> primary key(userid)
    -> )engine=InnoDB;
Query OK, 0 rows affected (0.23 sec)

mysql> delimiter //
mysql> Create trigger tgr_usercash_update before update on usercash
    -> for each row
    -> begin
    -> if new.cash -old.cash>0 then
    -> insert into usercash_error_log select old.userid, old.cash,new.cash,US
),NOW();
    -> set new.cash=old.cash;
    -> end if;
    -> end//
Query OK, 0 rows affected (0.05 sec)

mysql> delimiter ;
mysql> delete from usercash;
Query OK, 1 row affected (0.05 sec)

mysql> insert into usercash select 1,1000;
Query OK, 1 row affected (0.05 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> update usercash set cash=cash-(-20) where userid=1;
Query OK, 0 rows affected (0.11 sec)
Rows matched: 1  Changed: 0  Warnings: 0

mysql> select * from usercash;
+--------+------+
| userid | cash |
+--------+------+
|      1 | 1000 |
+--------+------+
1 row in set (0.00 sec)

mysql> select * from usercash_error_log;
+--------+----------+----------+----------------+---------------------+
| userid | old_cash | new_cash | user           | time                |
+--------+----------+----------+----------------+---------------------+
|      1 |     1000 |     1020 | root@localhost | 2018-06-01 15:09:51 |
+--------+----------+----------+----------------+---------------------+
1 row in set (0.00 sec)

  能夠看出此次對於異常的數據更新經過觸發器將其保存到了usercash_error_log。此外該觸發器還記錄了操做該SQL語句的用戶和時間。 

4. 外鍵約束

外鍵用來保證參照完整性,MySQL數據庫的MyIsAM存儲引擎自己並不支持外鍵,對於外鍵的定義只是起到一個註釋的做用,而InonoDB存儲引擎則完整支持外鍵約束。

通常來講,稱被引用的表爲父表,引用的表稱爲子表,外鍵定義時的on delete和on update表示在對父表進行delete和updata操做時,對子表所作的操做。可定義的子表操做有:

(1)CASCADE

   表示當父表發生delete和update操做時,對相應的子表中的數據頁進行delete和update操做

(2)SET FULL

  表示當父表發生delete和update操做時,相應的子表中的數據被更新爲NULL值,可是子表中對應的列必須容許爲NULL值

(3)NO ACTION

  表示父表發生delete或update操做時,拋出錯誤,不容許這類操做發生

(4)RESTRICT

  表示父表發生delete或update操做時,拋出錯誤,不容許這類操做發生,若是定義外鍵時沒有指定on delete或on update,RESTRICT就是默認的外鍵設置

相關文章
相關標籤/搜索