主键和外键 SQLite 与例子

⚡ 智能摘要

主键和外键 SQLite 通过唯一标识每一行并链接相关表来强制执行数据完整性,确保引用的值始终存在,并防止关系数据库中出现重复、空或孤立记录。

  • ???? 首要的关键: 主键唯一标识每一行,其值必须唯一且不能为空。
  • 🧩 复合键: 当没有单一列是唯一的时,将两列或多列组合起来形成复合主键。
  • 🔗 外键: 外键引用父表键,并强制相关表之间的引用完整性。
  • ⚙️ 启用强制执行: SQLite 默认情况下禁用外键,因此运行 PRAGMA foreign_keys = ON 来激活它们。
  • 🧱 列约束: NOT NULL、DEFAULT、UNIQUE 和 CHECK 规则在值进入列之前对其进行验证。
  • 🤖 人工智能协助: AI 文本转 SQL 助手和 GitHub Copilot 可以根据纯英文生成键和约束 SQL。

主键和外键 SQLite

以下各节将解释 SQLite 详细讲解约束,从定义和连接表的 PRIMARY KEY 和 FOREIGN KEY 开始,涵盖了用于验证每一列数据的 NOT NULL、DEFAULT、UNIQUE 和 CHECK 规则。

SQLite 限制

列约束对插入到列中的值强制执行规则,以验证数据。这些约束在创建表时定义在列定义中。它们通过拒绝违反您设置的规则的值(例如重复值、空值或在相关表中不存在的值)来保持存储数据的一致性和准确性。

SQLite 首要的关键

主键列中的所有值都必须是唯一的且不能为空。主键唯一标识表中的每一行。

主键可以应用于单个列,也可以应用于多个列的组合。如果是后者,则这些列的值的组合对于表中的所有行都应该是唯一的。

语法:

定义表中的主键有几种不同的方法:

在列定义本身中:

ColumnName INTEGER NOT NULL PRIMARY KEY;

作为单独的定义:

PRIMARY KEY(ColumnName);

要创建列组合作为主键:

PRIMARY KEY(ColumnName1, ColumnName2);

SQLite NOT NULL、DEFAULT、UNIQUE 和 CHECK 约束

除了主键之外, SQLite 它提供了多个列约束,用于验证表中输入的值。NOT NULL、DEFAULT、UNIQUE 和 CHECK 约束均在列定义中定义,每个约束都对列强制执行特定的规则。 数据类型 和价值观。

NOT NULL 约束

此 SQLite NOT NULL 约束可以防止列中包含空值:

ColumnName INTEGER  NOT NULL;

默认约束

随着 SQLite DEFAULT 约束:如果您未在列中插入任何值,则插入默认值。

例如:

ColumnName INTEGER DEFAULT 0;

如果你编写插入语句但没有为该列指定任何值,则该列的值为 0。

唯一约束

此 SQLite 唯一约束可以防止列中的所有值出现重复值。

例如:

EmployeeId INTEGER NOT NULL UNIQUE;

此规则强制“EmployeeId”值唯一,不允许重复值。请注意,此规则仅适用于“EmployeeId”列的值。

检查约束

此 SQLite CHECK 约束设置一个条件来检查插入的值。如果该值不符合条件,则不会插入。

Quantity INTEGER NOT NULL CHECK(Quantity > 10);

“数量”列中的值不能小于 10。

SQLite 外键

此 SQLite 外键是一种约束,用于验证一个表中存在的值是否在另一个与第一个表有关联的表中存在,该第一个表定义了外键。

在使用多个表时,有时会遇到两个表通过一个共同的列相互关联的情况。如果您希望确保在一个表中插入的值必须存在于另一个表的同一列中,则应该对该共同列使用外键约束。

在这种情况下,当您尝试在该列中插入一个值时,外键将确保插入的值存在于引用表的列中。

请注意,默认情况下未启用外键约束。 SQLite您必须先运行以下命令来启用它们:

PRAGMA foreign_keys = ON;

外键约束于 SQLite 从 3.6.19 版本开始。

示例 SQLite 外键

假设我们有两个表:学生表和院系表。

Students 表包含学生列表,Departments 表包含院系列表。每个学生都属于一个院系;也就是说,每个学生都有一个 departmentId 列。

现在我们将看到外键约束如何帮助确保 Students 表中的部门 ID 值必须存在于 Departments 表中。

因此,如果我们在 Students 表中的 DepartmentId 上创建外键约束,则每个插入的 departmentId 都必须存在于 Departments 表中。

CREATE TABLE [Departments] (
	[DepartmentId] INTEGER  NOT NULL PRIMARY KEY AUTOINCREMENT,
	[DepartmentName] NVARCHAR(50)  NULL
);
CREATE TABLE [Students] (
	[StudentId] INTEGER  PRIMARY KEY AUTOINCREMENT NOT NULL,
	[StudentName] NVARCHAR(50)  NULL,
	[DepartmentId] INTEGER  NOT NULL,
	[DateOfBirth] DATE  NULL,
	FOREIGN KEY(DepartmentId) REFERENCES Departments(DepartmentId)
);

为了检查外键约束如何防止将未定义的元素或值插入到与另一个表有关联的表中,我们将研究以下示例。

在这个例子中,“部门”表与“学生”表存在外键关系,因此插入到“学生”表中的任何 departmentId 值都必须存在于“部门”表中。如果您尝试插入一个“部门”表中不存在的 departmentId 值,外键约束将阻止您执行此操作。

让我们在“部门”表中插入两个部门:“IT”和“艺术”,如下所示。 插入查询:

INSERT INTO Departments VALUES(1, 'IT');
INSERT INTO Departments VALUES(2, 'Arts');

这两条语句应该会将两个部门插入到 Departments 表中。之后,您可以运行查询“SELECT * FROM Departments”来确认这两个值是否已插入:

SELECT 查询结果显示了 IT 和艺术部门 SQLite

然后尝试插入一个部门 ID 在 Departments 表中不存在的新学生:

INSERT INTO Students(StudentName,DepartmentId) VALUES('John', 5);

该行将不会被插入,并且您会收到一条错误消息,提示:外键约束失败。

SQLite 外键约束失败错误消息

主键和外键的区别 SQLite

主键和外键都有助于维护数据完整性,但它们的作用不同。主键用于标识单个表中的行,而外键则连接两个相关表中的行。下表总结了它们的主要区别。

基地 首要的关键 外键
目的 唯一标识其自身表中的每一行 指的是另一个表的主键,以便将它们链接起来。
唯一 值必须唯一 值可以重复,因此多个子行可以共享一个父行。
空值 不能为空 当关系为可选关系时,此值可能为空。
每桌人数 每个表只能有一个主键 一个表可以有多个外键
索引 自动索引 未自动索引;为提高性能,请添加一个。

在“学生和部门”示例中,DepartmentId 是“部门”表的主键,也是“学生”表的外键,它将每个学生与一个有效的部门关联起来。

SQLite 复合主键

复合主键是由两列或多列组成的主键。当任何单列本身都不唯一,但各列组合起来对于每一行数据都是唯一的时,可以使用复合主键。 SQLite 将组合后的值视为一个键。

例如,选课表可能允许同一学生选修多门课程,也可能允许同一门课程被多名学生选修,但每个学生-课程组合应该只出现一次:

CREATE TABLE Enrollments (
	StudentId INTEGER NOT NULL,
	CourseId INTEGER NOT NULL,
	Grade TEXT,
	PRIMARY KEY (StudentId, CourseId)
);

这里,StudentId 和 CourseId 单独都不是唯一的,但 (StudentId, CourseId) 这对键是唯一的,因此同一个学生不能选修同一门课程两次。使用复合键时,请注意以下几点:

  • 当单个列无法唯一标识一行时,请使用复合键。
  • 复合键中的每一列都遵循主键规则,因此组合值必须是唯一的且不能为空。
  • 复合键是作为单独的表级 PRIMARY KEY 子句编写的,而不是写在单个列定义中。

SQLite 外键操作:删除时和更新时

外键还可以控制当其引用的父行被删除或更新时,子行会发生什么情况。这些引用操作是在定义外键时通过 ON DELETE 和 ON UPDATE 子句添加的。 SQLite 支持五项行动:

  • 没有行动 — 默认操作,如果子行仍然引用父行,则会引发错误。
  • 限制 — 防止在任何其他更改运行之前立即删除或更新。
  • 置空 — 将子外键列设置为 null。
  • 默认设置 — 将子外键列设置为其声明的默认值。
  • CASCADE — 对子行应用相同的更改,因此删除父行也会删除其子行。

以下示例重新创建了 Students 表,以便删除一个部门会自动删除该部门的学生,而更新部门 ID 会更新与之匹配的学生:

CREATE TABLE Students (
	StudentId INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
	StudentName NVARCHAR(50) NULL,
	DepartmentId INTEGER NOT NULL,
	FOREIGN KEY(DepartmentId) REFERENCES Departments(DepartmentId)
		ON DELETE CASCADE
		ON UPDATE CASCADE
);

请记住,引用操作仅在启用外键支持时才会运行,因此请在每次连接开始时运行 `PRAGMA foreign_keys = ON`。否则, SQLite 解析 ON DELETE 和 ON UPDATE 子句,但不强制执行它们。

常见问题

是的。当单个列被明确声明为 INTEGER PRIMARY KEY 时,它就成为表内置 rowid 的别名。 SQLite 它不单独保存索引,因此按该键查找速度很快,而且不占用额外存储空间。

默认情况下,外键强制功能处于关闭状态,以保持与 3.6.19 版本之前编写的旧数据库和脚本的向后兼容性。每个数据库连接都必须先运行 `PRAGMA foreign_keys = ON` 命令。 SQLite 开始检查外键约束。

SQLite 它会自动为主键和唯一列建立索引,但不会为外键列建立索引。由于每次约束检查都会读取子列,因此为了提高性能,建议为每个外键列创建自定义索引。

否。ALTER TABLE 在 SQLite 无法向现有表中添加主键或外键。您可以重命名旧表,创建一个定义好键的新表,使用 INSERT SELECT 语句复制数据,然后删除旧表。

普通整数主键会将下一个 ID 设置为比现有最大行 ID 大 1 的值,并且可以在删除后重用 ID。自增 tracks 是 sqlite_sequence 中使用过的最高 id,并且从不重复使用值,性能损失很小。

当您插入或更新子行时,如果子行的外键值在父表中没有匹配的行,或者当您删除仍包含子行的父行时,会出现此错误。请先插入父记录。

是的。AI文本转SQL助手可以将您对表的英文描述转换为带有主键和外键子句的CREATE TABLE语句。提供现有模式可以提高准确性,生成的SQL语句在应用于实际数据之前务必进行审核。

是的。 GitHub 副驾驶 建议在编辑器中以内联方式编写带有主键、外键和其他约束的 CREATE TABLE 代码,例如: VS Code它会读取你现有的模式和迁移,因此它的补全功能会重用你真实的表名和列名。

总结一下这篇文章: