数据库设计——约束 学习笔记

在数据库设计中,约束是用于确保数据的准确性和完整性的规则。

  1. 非空约束(NOT NULL)

    • 描述:非空约束用于确保字段中的数据不能为null,即必须填写值。
    • 关键字:NOT NULL
  2. 唯一约束(UNIQUE)

    • 描述:唯一约束保证字段中的所有数据都是唯一的,不允许有重复的值。
    • 关键字:UNIQUE
  3. 主键约束(PRIMARY KEY)

    • 描述:主键是表中每一行数据的唯一标识。一个表只能有一个主键,主键字段不能为null,并且必须是唯一的。
    • 关键字:PRIMARY KEY
  4. 默认约束(DEFAULT)

    • 描述:默认约束用于在插入数据时,如果未指定该字段的值,则自动采用默认值。
    • 关键字:DEFAULT
  5. 检查约束(CHECK)

    • 描述:检查约束用于确保字段值满足某个条件。这个约束在MySQL 8.0.16版本之后得到支持。
    • 关键字:CHECK
  6. 外键约束(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 KEYAUTO_INCREMENT
    • 约束条件:主键,自动增长
  • name: 字段类型为 varchar(10),它不能为空,并且必须是唯一的。

    • 约束关键字:NOT NULLUNIQUE
    • 约束条件:不为空,唯一
  • 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;

这个语句块做了以下两件事情:

  1. 删除了 child_table 中名为 fk_child_parent 的外键约束。
  2. 添加了名为 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
  • 15
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值