MySQL 的外键与参照完整性: Part 1

1. 什么是参照完整性?

——————–

参照完整性(完整性约束)是数据库设计中的一个重要概念,当数据库中的一个表与一个或多个表进行关联时都会涉及到参照完整性。比如下面这个例子:

文章分类表 -  categories

category_id     name

1               SQL Server

2               Oracle

3               PostgreSQL

5               SQLite

文章表 - articles

article_id      category_id     title

1                         1               aa

2                         2               bb

3                         4               cc

可见以上两个表之间是通过category_id,其中categories表有4条记录,articles表有3条记录。

然而可能因为某种原因我们删掉了categories 表中category_id=4的记录,而articles表却还是有一条category_id=4的记录,很明显,category_id=4的这条记录不应该存在在articles表中,这样会很容易造成数据错乱。

相反,外键关系(Foreign Key relationships)讨论的是父表(categories)与子表(articles)的关系,通过引入外键(Foreign Key)这个概念来保证参照完整性(Referential integrity),将使会数据库变的非常简单。比如,要要做到删除categories表中category_id=4记录的同时删除 articles 表中category_id=4的所有记录,如果没有引入外键的话,我们就必须执行2条SQL语句才行;如果有外键的话,可以很容易的用一条SQL语句就可以达到要求。

2. 使用外键的条件

—————–

MySQL只在v3.23.34版本以后才引入外键的,所以在这之前的版本就别想了:),除此之外,还必须具备以下几个条件:

1) 在my.cnf配置文件中打开InnoDB引擎支持。

# Uncomment the following if you are using InnoDB tables

innodb_data_home_dir = /var/db/mysql/

innodb_data_file_path = ibdata1:10M:autoextend

innodb_log_group_home_dir = /var/db/mysql/

innodb_log_arch_dir = /var/db/mysql/

2) 相关联的表都必须采用InnoDB引擎。

3) 相关联的字段都必须建立所以。

MySQL v4.0版本以后,定义外键时会自动建立所以,所以在 v4.0 版本以前(含v4.0版本)必须手工定义索引。

4) 相关联的字段必须采用类似的数据类型,或者说可转换的数据类型,当然相同类型是最好不过了。

比如父表的字段是TINYINT类型,则子表只能采用TINYINT、SMALLINT、INT、BIGINT等几种类型。

3. 外键语法参考

—————

可以通过 CREATE TABLE 或者 ALTER TABLE 来定义外键。

CREATE TABLE 语法:

CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name

[(create_definition,…)]

create_definition:

column_definition

| [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (index_col_name,…) [reference_definition]

column_definition:

col_name type [NOT NULL | NULL] [DEFAULT default_value]

[AUTO_INCREMENT] [UNIQUE [KEY] | [PRIMARY] KEY]

[COMMENT ’string’] [reference_definition]

index_col_name:

col_name [(length)] [ASC | DESC]

reference_definition:

REFERENCES tbl_name [(index_col_name,…)]

[MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]

[ON DELETE reference_option]

[ON UPDATE reference_option]

reference_option:

RESTRICT | CASCADE | SET NULL | NO ACTION

ALTER TABLE 语法:

ALTER [IGNORE] TABLE tbl_name

alter_specification [, alter_specification] …

alter_specification:

| ADD [CONSTRAINT [symbol]] PRIMARY KEY [index_type] (index_col_name,…)

| ADD [CONSTRAINT [symbol]] UNIQUE [INDEX] [index_name] [index_type] (index_col_name,…)

| ADD [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (index_col_name,…) [reference_definition]

| DROP FOREIGN KEY fk_symbol

4. 定义外键

———–

mysql> CREATE TABLE categories (

-> category_id tinyint(3) unsigned NOT NULL AUTO_INCREMENT,

-> name varchar(30) NOT NULL,

-> PRIMARY KEY(category_id)

-> ) ENGINE=INNODB;

Query OK, 0 rows affected (0.36 sec)

mysql> INSERT INTO categories VALUES (1, ‘SQL Server’), (2, ‘Oracle’), (3, ‘PostgreSQL’), (4, ‘MySQL’), (5, ‘SQLite’);

Query OK, 5 rows affected (0.48 sec)

Records: 5  Duplicates: 0  Warnings: 0

mysql> CREATE TABLE members (

-> member_id INT(11) UNSIGNED NOT NULL,

-> name VARCHAR(20) NOT NULL,

-> PRIMARY KEY(member_id)

-> ) ENGINE=INNODB;

Query OK, 0 rows affected (0.55 sec)

mysql> INSERT INTO members VALUES (1, ‘test’), (2, ‘admin’);

Query OK, 2 rows affected (0.44 sec)

Records: 2  Duplicates: 0  Warnings: 0

mysql> CREATE TABLE articles (

-> article_id INT(11) unsigned NOT NULL AUTO_INCREMENT,

-> title varchar(255) NOT NULL,

-> category_id tinyint(3) unsigned NOT NULL,

-> member_id int(11) unsigned NOT NULL,

-> INDEX (category_id),

-> FOREIGN KEY (category_id) REFERENCES categories (category_id),

-> CONSTRAINT fk_member FOREIGN KEY (member_id) REFERENCES members (member_id),

-> PRIMARY KEY(article_id)

-> ) ENGINE=INNODB;

Query OK, 0 rows affected (0.63 sec)

注意:对于非InnoDB表,FOREIGN KEY子句会被忽略掉。

如果遇到如下错误:

ERROR 1005: Can’t create table ‘./test/articles.frm’ (errno: 150)

请仔细检查以下定义语句,常见的错误一般都是表类型不是INNODB、相关联的字段写错了、缺少索引等等。

至此categories.category_id和articles.category_id、members.member_id和articles.member_id已经建立外键关系,只有 articles.category_id 的值存在与 categories.category_id 表中并且articles.member_id的值存在与members.member_id表中才会允许被插入或修改。例如:

mysql> INSERT INTO articles (category_id, member_id, title) VALUES (6, 1, ‘foo’);

ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`test/articles`, CONSTRAINT `articles_ibfk_1` FOREIGN KEY (`category_id`)REFERENCES `categories` (`id`))

mysql> INSERT INTO articles (category_id, member_id, title) VALUES (3, 3, ‘foo’);

ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`test/articles`, CONSTRAINT `fk_member` FOREIGN KEY (`member_id`) REFERENCES `members` (`member_id`))

可见上面两条语句都会出现错误,因为在categories表中并没有category_id=6、members表中也没有member_id=3的记录,所以不能插入。而下面这条SQL语句就可以。

mysql> INSERT INTO articles (category_id, member_id, title) VALUES (3, 2, ‘bar’);

Query OK, 1 row affected (0.03 sec)

5. 删除外键定义

—————

不知道大家有没有发现,在前面定义外键的时候articles.member_id外键比articles.category_id子句多了一个CONSTRAINT fk_member ?

这个fk_member就是用来删除外键定义用的,如下所示:

mysql> ALTER TABLE articles DROP FOREIGN KEY fk_member;

Query OK, 1 row affected (0.25 sec)

Records: 1  Duplicates: 0  Warnings: 0

这样articles.member_id外键定义就被删除了,但是如果定义时没有指定CONSTRAINT fk_symbol (即外键符号)时该怎么删除呢?别急,没有指定时,MySQL会自己创建一个,可以通过以下命令查看:

mysql> SHOW CREATE TABLE articles;

+———-+————————————+

| Table    | Create Table                       |

+———-+————————————+

| articles | CREATE TABLE `articles` (

`article_id` int(11) unsigned NOT NULL auto_increment,

`category_id` tinyint(3) unsigned NOT NULL,

`member_id` int(11) unsigned NOT NULL,

`title` varchar(255) NOT NULL,

PRIMARY KEY  (`article_id`),

KEY `category_id` (`category_id`),

KEY `member_id` (`member_id`),

CONSTRAINT `articles_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1          |

+———-+————————————+

1 row in set (0.01 sec)

可以看出articles.category_id的外键符号为articles_ibfk_1,因为就可以执行以下命令删除外键定义:

mysql> ALTER TABLE articles DROP FOREIGN KEY articles_ibfk_1;

Query OK, 1 row affected (0.66 sec)

Records: 1  Duplicates: 0  Warnings: 0

6. 总结

——-

引入外键的缺点是会使速度和性能下降,当然外键所带来的优点还有很多,本文仅讨论如何定义、删除外键。至于外键的实际应用将会在以后的文章中介绍。

1. 什么是参照完整性?

——————–

参照完整性(完整性约束)是数据库设计中的一个重要概念,当数据库中的一个表与一个或多个表进行关联时都会涉及到参照完整性。比如下面这个例子:

文章分类表 -  categories

category_id     name

1               SQL Server

2               Oracle

3               PostgreSQL

5               SQLite

文章表 - articles

article_id      category_id     title

1                         1               aa

2                         2               bb

3                         4               cc

可见以上两个表之间是通过category_id,其中categories表有4条记录,articles表有3条记录。

然而可能因为某种原因我们删掉了categories 表中category_id=4的记录,而articles表却还是有一条category_id=4的记录,很明显,category_id=4的这条记录不应该存在在articles表中,这样会很容易造成数据错乱。

相反,外键关系(Foreign Key relationships)讨论的是父表(categories)与子表(articles)的关系,通过引入外键(Foreign Key)这个概念来保证参照完整性(Referential integrity),将使会数据库变的非常简单。比如,要要做到删除categories表中category_id=4记录的同时删除 articles 表中category_id=4的所有记录,如果没有引入外键的话,我们就必须执行2条SQL语句才行;如果有外键的话,可以很容易的用一条SQL语句就可以达到要求。

2. 使用外键的条件

—————–

MySQL只在v3.23.34版本以后才引入外键的,所以在这之前的版本就别想了:),除此之外,还必须具备以下几个条件:

1) 在my.cnf配置文件中打开InnoDB引擎支持。

# Uncomment the following if you are using InnoDB tables

innodb_data_home_dir = /var/db/mysql/

innodb_data_file_path = ibdata1:10M:autoextend

innodb_log_group_home_dir = /var/db/mysql/

innodb_log_arch_dir = /var/db/mysql/

2) 相关联的表都必须采用InnoDB引擎。

3) 相关联的字段都必须建立所以。

MySQL v4.0版本以后,定义外键时会自动建立所以,所以在 v4.0 版本以前(含v4.0版本)必须手工定义索引。

4) 相关联的字段必须采用类似的数据类型,或者说可转换的数据类型,当然相同类型是最好不过了。

比如父表的字段是TINYINT类型,则子表只能采用TINYINT、SMALLINT、INT、BIGINT等几种类型。

3. 外键语法参考

—————

可以通过 CREATE TABLE 或者 ALTER TABLE 来定义外键。

CREATE TABLE 语法:

CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name

[(create_definition,…)]

create_definition:

column_definition

| [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (index_col_name,…) [reference_definition]

column_definition:

col_name type [NOT NULL | NULL] [DEFAULT default_value]

[AUTO_INCREMENT] [UNIQUE [KEY] | [PRIMARY] KEY]

[COMMENT ’string’] [reference_definition]

index_col_name:

col_name [(length)] [ASC | DESC]

reference_definition:

REFERENCES tbl_name [(index_col_name,…)]

[MATCH FULL | MATCH PARTIAL | MATCH SIMPLE]

[ON DELETE reference_option]

[ON UPDATE reference_option]

reference_option:

RESTRICT | CASCADE | SET NULL | NO ACTION

ALTER TABLE 语法:

ALTER [IGNORE] TABLE tbl_name

alter_specification [, alter_specification] …

alter_specification:

| ADD [CONSTRAINT [symbol]] PRIMARY KEY [index_type] (index_col_name,…)

| ADD [CONSTRAINT [symbol]] UNIQUE [INDEX] [index_name] [index_type] (index_col_name,…)

| ADD [CONSTRAINT [symbol]] FOREIGN KEY [index_name] (index_col_name,…) [reference_definition]

| DROP FOREIGN KEY fk_symbol

4. 定义外键

———–

mysql> CREATE TABLE categories (

-> category_id tinyint(3) unsigned NOT NULL AUTO_INCREMENT,

-> name varchar(30) NOT NULL,

-> PRIMARY KEY(category_id)

-> ) ENGINE=INNODB;

Query OK, 0 rows affected (0.36 sec)

mysql> INSERT INTO categories VALUES (1, ‘SQL Server’), (2, ‘Oracle’), (3, ‘PostgreSQL’), (4, ‘MySQL’), (5, ‘SQLite’);

Query OK, 5 rows affected (0.48 sec)

Records: 5  Duplicates: 0  Warnings: 0

mysql> CREATE TABLE members (

-> member_id INT(11) UNSIGNED NOT NULL,

-> name VARCHAR(20) NOT NULL,

-> PRIMARY KEY(member_id)

-> ) ENGINE=INNODB;

Query OK, 0 rows affected (0.55 sec)

mysql> INSERT INTO members VALUES (1, ‘test’), (2, ‘admin’);

Query OK, 2 rows affected (0.44 sec)

Records: 2  Duplicates: 0  Warnings: 0

mysql> CREATE TABLE articles (

-> article_id INT(11) unsigned NOT NULL AUTO_INCREMENT,

-> title varchar(255) NOT NULL,

-> category_id tinyint(3) unsigned NOT NULL,

-> member_id int(11) unsigned NOT NULL,

-> INDEX (category_id),

-> FOREIGN KEY (category_id) REFERENCES categories (category_id),

-> CONSTRAINT fk_member FOREIGN KEY (member_id) REFERENCES members (member_id),

-> PRIMARY KEY(article_id)

-> ) ENGINE=INNODB;

Query OK, 0 rows affected (0.63 sec)

注意:对于非InnoDB表,FOREIGN KEY子句会被忽略掉。

如果遇到如下错误:

ERROR 1005: Can’t create table ‘./test/articles.frm’ (errno: 150)

请仔细检查以下定义语句,常见的错误一般都是表类型不是INNODB、相关联的字段写错了、缺少索引等等。

至此categories.category_id和articles.category_id、members.member_id和articles.member_id已经建立外键关系,只有 articles.category_id 的值存在与 categories.category_id 表中并且articles.member_id的值存在与members.member_id表中才会允许被插入或修改。例如:

mysql> INSERT INTO articles (category_id, member_id, title) VALUES (6, 1, ‘foo’);

ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`test/articles`, CONSTRAINT `articles_ibfk_1` FOREIGN KEY (`category_id`)REFERENCES `categories` (`id`))

mysql> INSERT INTO articles (category_id, member_id, title) VALUES (3, 3, ‘foo’);

ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`test/articles`, CONSTRAINT `fk_member` FOREIGN KEY (`member_id`) REFERENCES `members` (`member_id`))

可见上面两条语句都会出现错误,因为在categories表中并没有category_id=6、members表中也没有member_id=3的记录,所以不能插入。而下面这条SQL语句就可以。

mysql> INSERT INTO articles (category_id, member_id, title) VALUES (3, 2, ‘bar’);

Query OK, 1 row affected (0.03 sec)

5. 删除外键定义

—————

不知道大家有没有发现,在前面定义外键的时候articles.member_id外键比articles.category_id子句多了一个CONSTRAINT fk_member ?

这个fk_member就是用来删除外键定义用的,如下所示:

mysql> ALTER TABLE articles DROP FOREIGN KEY fk_member;

Query OK, 1 row affected (0.25 sec)

Records: 1  Duplicates: 0  Warnings: 0

这样articles.member_id外键定义就被删除了,但是如果定义时没有指定CONSTRAINT fk_symbol (即外键符号)时该怎么删除呢?别急,没有指定时,MySQL会自己创建一个,可以通过以下命令查看:

mysql> SHOW CREATE TABLE articles;

+———-+————————————+

| Table    | Create Table                       |

+———-+————————————+

| articles | CREATE TABLE `articles` (

`article_id` int(11) unsigned NOT NULL auto_increment,

`category_id` tinyint(3) unsigned NOT NULL,

`member_id` int(11) unsigned NOT NULL,

`title` varchar(255) NOT NULL,

PRIMARY KEY  (`article_id`),

KEY `category_id` (`category_id`),

KEY `member_id` (`member_id`),

CONSTRAINT `articles_ibfk_1` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`)

) ENGINE=InnoDB DEFAULT CHARSET=latin1          |

+———-+————————————+

1 row in set (0.01 sec)

可以看出articles.category_id的外键符号为articles_ibfk_1,因为就可以执行以下命令删除外键定义:

mysql> ALTER TABLE articles DROP FOREIGN KEY articles_ibfk_1;

Query OK, 1 row affected (0.66 sec)

Records: 1  Duplicates: 0  Warnings: 0

6. 总结

——-

引入外键的缺点是会使速度和性能下降,当然外键所带来的优点还有很多,本文仅讨论如何定义、删除外键。至于外键的实际应用将会在以后的文章中介绍。


相关文章

  • MySQL优化(二) MySQL 卓越资源
  • MySQL优化(二) 2007-06-24 23:24 来源: imysql.cn 作者:叶金荣 网友评论 0 条 浏览次数 99 7.2.1 EXPLAIN 语法(得到SELECT 的相关信息) EXPLAIN tbl_name 或者: ...查看


  • 数据库设计规范
  • 修订历史记录 发放范围:产品研发部 1. 概述 2. 数据库设计的基本原则 3. 数据库建模 3.1 数据分析 3.2 数据关系分析 3.3 数据量分析 3.4 扩展性分析 3.5 数据字典(参考) 3.5.1 数据项 3.5.2 数据结构 ...查看


  • 系统构架图
  • 系统架构文档 文档编号 文档状态草案.秘密 系统架构方案编写人 编写日期 最后修订周汉仓2011年03月01日 系统架构文档 系统架构文档 修改记录 日期 2011/3/1修改人周汉仓编写初稿修改内容 系统架构文档 目 1. 录 2. 3. ...查看


  • 数据库原理实验指导书(Mysql)
  • 数据库原理实验指导书 实验项目列表 实验一:数据库的定义实验 一.实验目的: 1.理解MySQL Server 6.0 服务器的安装过程和方法: 2.要求学生熟练掌握和使用SQL .T-SQL .SQL Server Enterpriser ...查看


  • 学生学籍管理系统
  • 诚信申明 本人申明: 我所呈交的本科毕业设计(论文)是本人在导师指导下运用四年专业知识进行的研究工作及全面的总结.尽我所知,除了文中特别加以标注和致谢中所罗列的内容以外,论文中创新处不包含其他人已经发表或撰写过的研究成果,也不包含为获得北京 ...查看


  • 网上投票系统论文
  • 摘 要 从国际互联网到校园网,企业局域网,各种网上投票系统随处可见,意见调查,用户信息统计,经营情况调查都可以作为投票的内容.网上投票系统凭借其方便快捷等特点,已经成为统计收集信息不可或缺的工具.随着网络技术的发展,网上投票系统的作用将会越 ...查看


  • 资源list:Github上关于大数据的开源项目.论文等合集
  • 资源list:Github上关于大数据的开源项目.论文等合集 Awesome Big Data A curated list of awesome big data frameworks, resources and other aweso ...查看


  • Asterisk 配置文详解
  • 当安装完 Asterisk,验证启动无误,就需要开始配置PBX了,首先需要告诉Asterisk那些类型的电话连接(手持设备,交换设备等,不管是通过电路还是IP),这些被称为通道. Asterisk 的通道配置 下一步任务是配置拨号方案,拨号 ...查看


  • GnuDIP制作动态域名服务器(DDNS Server)
  • 这个阶段在做DDNS,虽然有dyndns和tzo两个免费的国外的DDNS服务器(支持免费用户注册使用),但是公司需求中要有GnuDIP这种服务.于是只能自己制作DDNS服务器,颇费功夫,于是想把这段记下来. 首先讲一下GnuDIP这个协议, ...查看


热门内容