SQLite 约束 (Constraints) 面试核心知识点
目录
1. 什么是数据库约束?
2. SQLite 中的主要约束类型
3. 约束详解与面试重点
3.1 PRIMARY KEY (主键约束)
3.2 NOT NULL (非空约束)
3.3 UNIQUE (唯一约束)
3.4 FOREIGN KEY (外键约束)
3.5 CHECK (检查约束)
3.6 DEFAULT (默认值约束)
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 NULLusername 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 -- 如果父表部门被删除,员工的部门设为NULLON 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), -- 价格必须大于0discount 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) ); -
