目录
[1. 什么是数据库约束?](#1. 什么是数据库约束?)
[2. SQLite 中的主要约束类型](#2. SQLite 中的主要约束类型)
[3. 约束详解与面试重点](#3. 约束详解与面试重点)
[3.1 PRIMARY KEY (主键约束)](#3.1 PRIMARY KEY (主键约束))
[3.2 NOT NULL (非空约束)](#3.2 NOT NULL (非空约束))
[3.3 UNIQUE (唯一约束)](#3.3 UNIQUE (唯一约束))
[3.4 FOREIGN KEY (外键约束)](#3.4 FOREIGN KEY (外键约束))
[3.5 CHECK (检查约束)](#3.5 CHECK (检查约束))
[3.6 DEFAULT (默认值约束)](#3.6 DEFAULT (默认值约束))
[4. 约束的定义级别](#4. 约束的定义级别)
1. 什么是数据库约束?
面试官提问: "你能谈谈什么是数据库约束吗?为什么我们要使用它?"
回答要点:
数据库约束(Constraints)是在表的数据列上强制执行的规则 。它们的主要目的是保证数据的完整性、准确性和可靠性。
-
目的:
-
限制可以存储到表中的数据类型。
-
防止无效或"脏"数据进入数据库。
-
维护表与表之间的关系(参照完整性)。
-
-
执行时机: 约束会在数据发生变化时(如
INSERT,UPDATE,DELETE)自动检查。如果操作违反了约束,该操作将被拒绝并报错。
2. SQLite 中的主要约束类型
SQLite 支持以下几种主要的约束:
-
PRIMARY KEY(主键) -
NOT NULL(非空) -
UNIQUE(唯一) -
FOREIGN KEY(外键) -
CHECK(检查) -
DEFAULT(默认值)
3. 约束详解与面试重点
3.1 PRIMARY KEY (主键约束)
-
定义: 用于唯一标识表中的每一行。
-
特性:
-
它隐含了
UNIQUE和NOT NULL两个约束。 -
一张表最多只能有一个主键。
-
主键可以是单列,也可以是多列(称为"复合主键")。
-
-
示例:
-- 单列主键 CREATE TABLE Users ( user_id INTEGER PRIMARY KEY, -- 隐含 UNIQUE 和 NOT NULL username TEXT ); -- 复合主键 (表级定义) CREATE TABLE OrderDetails ( order_id INTEGER, product_id INTEGER, quantity INTEGER, PRIMARY KEY (order_id, product_id) ); -
⭐ SQLite 面试重点:
INTEGER PRIMARY KEY与ROWID-
在 SQLite 中,几乎所有的表都有一个隐藏的、唯一的64位有符号整数列,名为
ROWID。 -
如果你将一列定义为
INTEGER PRIMARY KEY(必须是INTEGER,不能是INT或BIGINT),那么这一列实际上会成为ROWID的别名。 -
好处: 这是 SQLite 中最高效的主键类型,因为它直接使用了底层的
ROWID进行查找,速度极快。 -
对比: 如果你定义
user_id TEXT PRIMARY KEY,SQLite 会为user_id单独创建一个索引,并且仍然在内部维护一个ROWID。
-
-
⭐ SQLite 面试重点:
AUTOINCREMENT关键字-
误区: 很多人认为
INTEGER PRIMARY KEY就会自动增长。它确实会自动生成一个唯一的 ID,但它可能重用之前删除过的行的 ID。 -
AUTOINCREMENT的真正作用: 当你使用INTEGER PRIMARY KEY AUTOINCREMENT时,SQLite 会保证 新插入的 ID 永远大于 该表中曾经存在过的任何 ID。即使你删除了 ID 最大的行,下一个 ID 也会继续增长,绝不重用。 -
代价:
AUTOINCREMENT会带来额外的 CPU、内存和磁盘开销,因为它需要一个额外的sqlite_sequence表来跟踪每个表的最大 ID。 -
面试结论: 除非你明确需要"ID 永不重用"的特性,否则只使用
INTEGER PRIMARY KEY是最高效的选择。-- 推荐的高效方式 (可能会重用已删除的ID)
CREATE TABLE Logs (
log_id INTEGER PRIMARY KEY,
message TEXT
);-- 保证ID永不重用 (开销稍大)
CREATE TABLE Invoices (
invoice_id INTEGER PRIMARY KEY AUTOINCREMENT,
amount REAL
);
-
3.2 NOT NULL (非空约束)
-
定义: 确保该列的值永远不能为
NULL。 -
示例:
CREATE TABLE Products ( product_id INTEGER PRIMARY KEY, name TEXT NOT NULL, -- 商品名不能为空 price REAL NOT NULL -- 价格不能为空 ); -- 尝试插入 NULL 将会失败 INSERT INTO Products (name, price) VALUES (NULL, 9.99); -- 错误!
3.3 UNIQUE (唯一约束)
-
定义: 确保该列(或多列组合)中的所有值都是唯一的。
-
特性:
-
与
PRIMARY KEY类似,但UNIQUE约束允许NULL值。 -
⭐ SQLite 面试重点: 在
UNIQUE约束的列中,你可以存储多个NULL值 。SQLite (以及 PostgreSQL) 认为NULL不等于NULL,所以多个NULL并不违反唯一性。 (注意:这在 SQL Server 或 Oracle 中行为不同)。 -
一张表可以有多个
UNIQUE约束。
-
-
示例:
CREATE TABLE Employees ( emp_id INTEGER PRIMARY KEY, ssn TEXT UNIQUE, -- 社保号必须唯一 (但允许有人没有社保号, 即 NULL) email TEXT UNIQUE -- 邮箱也必须唯一 ); -- 以下插入在 SQLite 中是允许的 INSERT INTO Employees (ssn) VALUES (NULL); INSERT INTO Employees (ssn) VALUES (NULL); -- 再次插入 NULL,仍然允许
3.4 FOREIGN KEY (外键约束)
-
定义: 用于在两个表之间建立连接并强制实现参照完整性。
-
工作原理:
- 它指定一个(或一组)列,称为"子列",其值必须在另一张表("父表")的
PRIMARY KEY或UNIQUE列中存在,或者为NULL。
- 它指定一个(或一组)列,称为"子列",其值必须在另一张表("父表")的
-
示例:
-- 1. 创建父表 (Departments) CREATE TABLE Departments ( dept_id INTEGER PRIMARY KEY, dept_name TEXT NOT NULL ); -- 2. 创建子表 (Employees) CREATE TABLE Employees ( emp_id INTEGER PRIMARY KEY, name TEXT, dept_id INTEGER, -- 这是外键列 FOREIGN KEY (dept_id) REFERENCES Departments (dept_id) ON DELETE SET NULL -- 如果父表部门被删除,员工的部门设为NULL ON UPDATE CASCADE -- 如果父表部门ID更新,员工的部门ID也跟着更新 ); -
外键动作 (
ON DELETE/ON UPDATE):-
NO ACTION/RESTRICT(默认):如果子表存在关联数据,禁止删除/更新父表。 -
SET NULL:父表数据变化时,子表对应列设为NULL。 -
SET DEFAULT:父表数据变化时,子表对应列设为默认值。 -
CASCADE:父表数据删除/更新时,子表关联的行也级联删除/更新。
-
-
⭐ SQLite 面试重点:启用外键
-
在旧版本的 SQLite 中,外键支持默认是关闭的。在现代版本中(3.6.19 之后),通常在编译时默认开启。
-
但为了保证兼容性和安全性,在建立连接后立即执行
PRAGMA foreign_keys = ON;是一个非常好的习惯,这在面试中是加分项。-- 在你的应用程序连接数据库后,应首先执行这个命令
PRAGMA foreign_keys = ON;
-
3.5 CHECK (检查约束)
-
定义: 允许你指定一个布尔表达式,
INSERT或UPDATE的数据必须满足该表达式(即表达式结果为TRUE)才能被写入。 -
特性: SQLite 支持
CHECK约束(但 MySQL 长期忽略它,直到 8.0)。 -
示例:
CREATE TABLE Products ( id INTEGER PRIMARY KEY, name TEXT, price REAL CHECK (price > 0), -- 价格必须大于0 discount REAL, -- 表级 CHECK 约束,可以引用多列 CHECK (discount < price) -- 折扣必须小于价格 );
3.6 DEFAULT (默认值约束)
-
定义: 当
INSERT语句没有为该列提供值时,自动为其分配一个默认值。 -
示例:
CREATE TABLE Orders ( order_id INTEGER PRIMARY KEY, order_date TEXT DEFAULT (DATE('now')), -- 使用函数作为默认值 status TEXT DEFAULT 'Pending' -- 使用常量作为默认值 ); -- 插入时,可以不指定 order_date 和 status INSERT INTO Orders (order_id) VALUES (101); -- 数据库中该行将自动填充 (..., '2025-10-29', 'Pending')
4. 约束的定义级别
约束可以在两个级别上定义:
-
列级约束 (Column-level):
-
作为列定义的一部分。
-
NOT NULL只能在列级定义。 -
适用于:
NOT NULL,PRIMARY KEY,UNIQUE,CHECK,DEFAULT,FOREIGN KEY(单列)。CREATE TABLE Example (
id INTEGER PRIMARY KEY,
email TEXT UNIQUE NOT NULL CHECK (length(email) > 5)
);
-
-
表级约束 (Table-level):
-
在所有列定义之后,单独定义。
-
必须用于定义"复合主键"或"复合唯一/外键" (即约束涉及多于一列)。
-
适用于:
PRIMARY KEY,UNIQUE,CHECK,FOREIGN KEY。CREATE TABLE Example (
col1 INTEGER,
col2 INTEGER,
col3 TEXT,-- 表级定义 PRIMARY KEY (col1, col2), UNIQUE (col2, col3));
-