MySql数据库约束

 

  《MySQL技术内幕:InnoDB存储引擎》本书从源代码的角度深度解析了InnoDB的体系结构、实现原理、工作机制,并给出了大量实践,本着将书读薄的思想,循序渐进的记录对本书学习的读书笔记。

  关系型数据库系统和文件系统的一个不同点是,关系数据库本身能保证存储数据的完整性,不需要应用程序的控制,而文件系统一般需要在程序端进行控制。当前几乎所有的关系型数据库都提供了约束(constraits)机制,该机制提供了一条强大而简易的途径来保证数据库中的数据完整性,一般来说,数据完整性有以下三种形式:

(1)实体完整性保证表中有一个主键,在InnoDB存储引擎中,用户可以通过定义Primary Key或Unique Key约束来保证实体的完整性,用户还可以编写一个触发器来保证数据完整性、

(2)域完整性保证数据每列的值满足特定的条件。在InnoDB存储引擎中,域完整性可以通过以下途径来保证:

  a. 选择适合的数据类型确保一个数据值满足条件

  b. 外键(Foreign Key)约束

  c. 编写触发器

  d. 还可以考虑用default约束作为强制域完整性的一个方面

(3)参照完整性保证两张表之间的关系,InnoDB存储引擎提供了以下几种约束:

  a. primary key

  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就是默认的外键设置

posted @ 2018-06-01 15:57  Fate0729  阅读(1091)  评论(0编辑  收藏  举报