在数据库设计中,约束是用于确保数据的准确性和完整性的规则。
-
非空约束(NOT NULL)
- 描述:非空约束用于确保字段中的数据不能为null,即必须填写值。
- 关键字:
NOT NULL
-
唯一约束(UNIQUE)
- 描述:唯一约束保证字段中的所有数据都是唯一的,不允许有重复的值。
- 关键字:
UNIQUE
-
主键约束(PRIMARY KEY)
- 描述:主键是表中每一行数据的唯一标识。一个表只能有一个主键,主键字段不能为null,并且必须是唯一的。
- 关键字:
PRIMARY KEY
-
默认约束(DEFAULT)
- 描述:默认约束用于在插入数据时,如果未指定该字段的值,则自动采用默认值。
- 关键字:
DEFAULT
-
检查约束(CHECK)
- 描述:检查约束用于确保字段值满足某个条件。这个约束在MySQL 8.0.16版本之后得到支持。
- 关键字:
CHECK
-
外键约束(FOREIGN KEY)
- 描述:外键约束用于在两个表之间建立关系,确保数据的一致性和完整性。外键通常指向另一个表的主键,用于维护表之间的关系。
- 关键字:
FOREIGN KEY
使用这些约束可以有效地管理数据库中的数据,防止数据错误和不一致性。在设计数据库时,应根据实际需求合理地使用这些约束。
CREATE TABLE tb_user (
id int AUTO_INCREMENT PRIMARY KEY COMMENT 'ID唯一标识',
name varchar(10) NOT NULL UNIQUE COMMENT '姓名',
age int CHECK (age > 0 AND age <= 120) COMMENT '年龄',
status char(1) DEFAULT '1' COMMENT '状态',
gender char(1) COMMENT '性别'
);
上述代码的约束如下
-
id
: 字段类型为int
,它是主键,并且会自动增长,用作每条记录的唯一标识。- 约束关键字:
PRIMARY KEY
,AUTO_INCREMENT
- 约束条件:主键,自动增长
- 约束关键字:
-
name
: 字段类型为varchar(10)
,它不能为空,并且必须是唯一的。- 约束关键字:
NOT NULL
,UNIQUE
- 约束条件:不为空,唯一
- 约束关键字:
-
age
: 字段类型为int
,它必须满足大于0并且小于等于120的条件。- 约束关键字:
CHECK
- 约束条件:
age > 0 AND age <= 120
- 约束关键字:
-
status
: 字段类型为char(1)
,如果没有指定值,则默认为'1'
。- 约束关键字:
DEFAULT
- 约束条件:默认为
'1'
- 约束关键字:
-
gender
: 字段类型为char(1)
,没有指定任何约束条件。- 约束关键字:无
- 约束条件:无
外键约束
添加外键
方法1:在创建表时添加外键约束
在创建表的同时,可以直接在字段定义之后指定外键约束。
CREATE TABLE 表名 (
字段名1 数据类型1,
字段名2 数据类型2,
-- 其他字段定义
[CONSTRAINT] [外键名称] FOREIGN KEY (外键字段名) REFERENCES 主表(主表列名)
);
案例
假设我们有一个主表 tb_parent
和一个从表 tb_child
。主表 tb_parent
有一个主键字段 parent_id
,而从表 tb_child
需要引用这个字段作为外键。
CREATE TABLE tb_child (
child_id INT PRIMARY KEY,
parent_id INT,
-- 其他字段定义
CONSTRAINT fk_parent_child FOREIGN KEY (parent_id) REFERENCES tb_parent(parent_id)
);
方法2:在已存在的表上添加外键约束
如果表已经存在,可以使用 ALTER TABLE
语句来添加外键约束。
ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名) REFERENCES 主表(主表列名);
案例
假设 tb_child
表已经存在,现在需要添加外键约束 fk_parent_child
,将 parent_id
字段作为外键引用 tb_parent
表的 parent_id
字段。
ALTER TABLE tb_child ADD CONSTRAINT fk_parent_child FOREIGN KEY (parent_id) REFERENCES tb_parent(parent_id);
在这两种方法中,[CONSTRAINT]
和 [外键名称]
是可选的,但如果想为外键指定一个名称,这是很有用的,特别是在需要引用或删除外键约束时。外键约束的名称在数据库中必须是唯一的。
删除外键
ALTER TABLE 表名 DROP FOREIGN KEY 外键名称;
在执行此操作之前,请确保:
- 有足够的权限来修改表结构。
- 删除外键不会破坏数据的完整性或引用关系。
- 如果外键约束涉及到级联操作,删除它可能会导致相关数据无法正常更新或删除。
可以通过查询数据库的元数据或查看表的结构定义来获取正确的名称。在某些数据库管理系统中,可以使用 SHOW CREATE TABLE 表名;
来查看表的定义,其中包括外键约束的名称。
删除和更新行为
在创建外键约束时,可以指定当父表中的记录被删除或更新时,子表应该采取的行为。
以下是一些删除和更新行为的说明:
删除行为
-
NO ACTION
说明:当在父表中删除对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除(与RESTRICT一致)。这是默认行为。 -
RESTRICT
说明:当在父表中删除对应记录时,首先检查该记录是否有对应外键,如果有则不允许删除(与NO ACTION一致)。这也是默认行为。 -
CASCADE
说明:当在父表中删除对应记录时,首先检查该记录是否有对应外键,如果有,则也删除子表中对应的外键记录。 -
SET NULL
说明:当在父表中删除对应记录时,首先检查该记录是否有对应外键,如果有则设置子表中该外键值为null(这就要求该外键允许取null)。 -
SET DEFAULT
说明:当在父表中删除对应记录时,首先检查该记录是否有对应外键,如果有则将子表中外键列设置成一个默认的值(InnoDB不支持)。
更新行为
更新行为的选项与删除行为相同,只是它们应用于父表中的记录更新时:
-
NO ACTION / RESTRICT
说明:当在父表中更新对应记录时,首先检查该记录是否有对应外键,如果有则不允许更新。 -
CASCADE
说明:当在父表中更新对应记录时,首先检查该记录是否有对应外键,如果有,则也更新子表中对应的外键记录。 -
SET NULL
说明:当在父表中更新对应记录时,首先检查该记录是否有对应外键,如果有则设置子表中该外键值为null。 -
SET DEFAULT
说明:当在父表中更新对应记录时,首先检查该记录是否有对应外键,如果有则将子表中外键列设置成一个默认的值。
--向已存在的表添加一个外键约束,并指定在更新或删除父表记录时的行为。
ALTER TABLE 表名
ADD CONSTRAINT 外键名称
FOREIGN KEY (外键字段)
REFERENCES 主表名 (主表字段名)
ON UPDATE CASCADE
ON DELETE CASCADE;
案例(仅举例说明删除操作的情况,没有提及更新操作)
通过模拟一个数据库环境,创建两个表(parent_table
和 child_table
),并在每个表中插入一些数据来说明各种外键删除行为的操作结果。
--创建 parent_table 和 child_table,并设置外键约束
CREATE TABLE parent_table (
parent_id INT PRIMARY KEY,
parent_name VARCHAR(50)
);
CREATE TABLE child_table (
child_id INT PRIMARY KEY,
parent_id INT,
child_name VARCHAR(50),
CONSTRAINT fk_child_parent FOREIGN KEY (parent_id) REFERENCES parent_table(parent_id)
);
--向 parent_table 和 child_table 中插入一些数据
-- 插入父表数据
INSERT INTO parent_table (parent_id, parent_name) VALUES (1, 'Parent 1');
INSERT INTO parent_table (parent_id, parent_name) VALUES (2, 'Parent 2');
INSERT INTO parent_table (parent_id, parent_name) VALUES (3, 'Parent 3');
-- 插入子表数据
INSERT INTO child_table (child_id, parent_id, child_name) VALUES (101, 1, 'Child 1-1');
INSERT INTO child_table (child_id, parent_id, child_name) VALUES (102, 1, 'Child 1-2');
INSERT INTO child_table (child_id, parent_id, child_name) VALUES (201, 2, 'Child 2-1');
INSERT INTO child_table (child_id, parent_id, child_name) VALUES (301, 3, 'Child 3-1');
NO ACTION / RESTRICT
默认情况下,如果尝试删除 parent_table
中有外键引用的记录,操作将不被允许。
-- 尝试删除父表中的记录,将失败
DELETE FROM parent_table WHERE parent_id = 1;
-- 结果:失败,因为存在外键引用
CASCADE
修改外键约束为 ON DELETE CASCADE
ALTER TABLE child_table DROP FOREIGN KEY fk_child_parent;
ALTER TABLE child_table
ADD CONSTRAINT fk_child_parent FOREIGN KEY (parent_id) REFERENCES parent_table(parent_id) ON DELETE CASCADE;
现在,如果删除 parent_table
中的记录,child_table
中对应的记录也将被删除。
-- 删除父表中的记录,子表中对应的记录也将被删除
DELETE FROM parent_table WHERE parent_id = 1;
-- 结果:父表中的记录被删除,子表中 child_id 为 101 和 102 的记录也被删除
SET NULL
修改外键约束为 ON DELETE SET NULL
ALTER TABLE child_table DROP FOREIGN KEY fk_child_parent;
ALTER TABLE child_table
ADD CONSTRAINT fk_child_parent FOREIGN KEY (parent_id) REFERENCES parent_table(parent_id) ON DELETE SET NULL;
如果删除 parent_table
中的记录,child_table
中对应的外键将被设置为 NULL
。
-- 删除父表中的记录,子表中对应的外键将被设置为 NULL
DELETE FROM parent_table WHERE parent_id = 2;
-- 结果:父表中的记录被删除,子表中 child_id 为 201 的记录的 parent_id 被设置为 NULL
SET DEFAULT
( InnoDB 不支持 ON DELETE SET DEFAULT
)如果数据库支持此操作,需要首先确保外键列有一个默认值,然后设置外键约束。
-- 假设支持 SET DEFAULT,则操作如下
ALTER TABLE child_table DROP FOREIGN KEY fk_child_parent;
ALTER TABLE child_table
ADD CONSTRAINT fk_child_parent FOREIGN KEY (parent_id) REFERENCES parent_table(parent_id) ON DELETE SET DEFAULT;
删除 parent_table
中的记录,child_table
中对应的外键将被设置为默认值。
-- 删除父表中的记录,子表中对应的外键将被设置为默认值
DELETE FROM parent_table WHERE parent_id = 3;
-- 结果:父表中的记录被删除,子表中 child_id 为 301 的记录的 parent_id 被设置为默认值
注:上述示例操作假设在一个支持 SQL 的数据库环境中执行。实际执行时,需要根据自己使用的数据库管理系统的具体语法和功能进行调整。
此处需注意
上述案例的代码语句块之后没有其他语句指定 ON UPDATE
的行为,那么默认情况下,如果父表中的 parent_id
被更新,子表中的对应外键将不会自动更新,可能会违反外键约束,除非该外键值在子表中不存在,或者子表中的对应记录被手动更新。
也就是说,仅指定了当父表中的记录被删除时,子表中对应的外键 parent_id
应该被设置为 NULL
。它没有提及当父表中的记录被更新时的行为。默认情况下,如果父表中的 parent_id
被更新,子表中的对应外键不会自动更新,除非数据库系统允许空值或者子表中的对应记录被手动更新。
例如
ALTER TABLE child_table DROP FOREIGN KEY fk_child_parent;
ALTER TABLE child_table
ADD CONSTRAINT fk_child_parent FOREIGN KEY (parent_id) REFERENCES parent_table(parent_id) ON UPDATE SET NULL ON DELETE SET NULL;
这个语句块做了以下两件事情:
- 删除了
child_table
中名为fk_child_parent
的外键约束。 - 添加了名为
fk_child_parent
的外键约束,并指定了两种行为:- 当父表中的记录被更新时,子表中对应的外键
parent_id
应该被设置为NULL
。 - 当父表中的记录被删除时,子表中对应的外键
parent_id
也应该被设置为NULL
。
- 当父表中的记录被更新时,子表中对应的外键
这个语句块明确指定了在更新和删除操作时都应该将子表中的外键设置为 NULL
,从而确保了外键约束的完整性。
案例(完善)
为了完整性,我将重新模拟数据库环境,创建表,并通过示例操作来展示每种外键删除和更新行为的实际效果。
首先,我们创建一个数据库和一个父表 parent_table
,以及一个子表 child_table
。
CREATE DATABASE test_db;
USE test_db;
CREATE TABLE parent_table (
parent_id INT PRIMARY KEY,
parent_name VARCHAR(50)
);
CREATE TABLE child_table (
child_id INT PRIMARY KEY,
parent_id INT,
child_name VARCHAR(50)
);
插入示例数据
INSERT INTO parent_table (parent_id, parent_name) VALUES (1, 'Parent 1');
INSERT INTO parent_table (parent_id, parent_name) VALUES (2, 'Parent 2');
INSERT INTO child_table (child_id, parent_id, child_name) VALUES (101, 1, 'Child 1-1');
INSERT INTO child_table (child_id, parent_id, child_name) VALUES (102, 1, 'Child 1-2');
INSERT INTO child_table (child_id, parent_id, child_name) VALUES (201, 2, 'Child 2-1');
示例操作
NO ACTION / RESTRICT
默认情况下,如果尝试删除或更新有外键引用的父表记录,操作将不被允许。
-- 尝试删除父表中的记录,将失败
DELETE FROM parent_table WHERE parent_id = 1;
-- 结果:失败,因为存在外键引用
-- 尝试更新父表中的记录,将失败
UPDATE parent_table SET parent_id = 3 WHERE parent_id = 1;
-- 结果:失败,因为存在外键引用
CASCADE
修改外键约束为 ON DELETE CASCADE
和 ON UPDATE CASCADE
。
ALTER TABLE child_table
ADD CONSTRAINT fk_child_parent
FOREIGN KEY (parent_id)
REFERENCES parent_table(parent_id)
ON DELETE CASCADE
ON UPDATE CASCADE;
-- 删除父表中的记录,子表中对应的记录也将被删除
DELETE FROM parent_table WHERE parent_id = 1;
-- 结果:父表中的记录被删除,子表中 child_id 为 101 和 102 的记录也被删除
-- 更新父表中的记录,子表中对应的外键记录也会更新
UPDATE parent_table SET parent_id = 3 WHERE parent_id = 2;
-- 结果:父表中的记录更新,子表中 child_id 为 201 的记录的 parent_id 也更新为 3
SET NULL
修改外键约束为 ON DELETE SET NULL
和 ON UPDATE SET NULL
。
ALTER TABLE child_table
ADD CONSTRAINT fk_child_parent
FOREIGN KEY (parent_id)
REFERENCES parent_table(parent_id)
ON DELETE SET NULL
ON UPDATE SET NULL;
-- 删除父表中的记录,子表中对应的外键将被设置为 NULL
DELETE FROM parent_table WHERE parent_id = 2;
-- 结果:父表中的记录被删除,子表中 child_id 为 201 的记录的 parent_id 被设置为 NULL
-- 更新父表中的记录,子表中对应的外键将被设置为 NULL
UPDATE parent_table SET parent_id = 4 WHERE parent_id = 3;
-- 结果:父表中的记录更新,子表中 child_id 为 201 的记录的 parent_id 被设置为 NULL