SQLite 约束 (Constraints) 面试核心知识点

目录

[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 支持以下几种主要的约束:

  1. PRIMARY KEY (主键)

  2. NOT NULL (非空)

  3. UNIQUE (唯一)

  4. FOREIGN KEY (外键)

  5. CHECK (检查)

  6. DEFAULT (默认值)

3. 约束详解与面试重点

3.1 PRIMARY KEY (主键约束)

  • 定义: 用于唯一标识表中的每一行。

  • 特性:

    1. 隐含了 UNIQUENOT NULL 两个约束。

    2. 一张表最多只能有一个主键。

    3. 主键可以是单列,也可以是多列(称为"复合主键")。

  • 示例:

    复制代码
    -- 单列主键
    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 KEYROWID

    • 在 SQLite 中,几乎所有的表都有一个隐藏的、唯一的64位有符号整数列,名为 ROWID

    • 如果你将一列定义为 INTEGER PRIMARY KEY(必须是 INTEGER,不能是 INTBIGINT),那么这一列实际上会成为 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 KEYUNIQUE 列中存在,或者为 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 (检查约束)

  • 定义: 允许你指定一个布尔表达式,INSERTUPDATE 的数据必须满足该表达式(即表达式结果为 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. 约束的定义级别

约束可以在两个级别上定义:

  1. 列级约束 (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)
      );

  2. 表级约束 (Table-level):

    • 在所有列定义之后,单独定义。

    • 必须用于定义"复合主键"或"复合唯一/外键" (即约束涉及多于一列)。

    • 适用于:PRIMARY KEY, UNIQUE, CHECK, FOREIGN KEY

      CREATE TABLE Example (
      col1 INTEGER,
      col2 INTEGER,
      col3 TEXT,

      复制代码
        -- 表级定义
        PRIMARY KEY (col1, col2),
        UNIQUE (col2, col3)

      );

相关推荐
QX_hao8 小时前
【Go】--接口(interface)
开发语言·后端·golang
憨憨崽&8 小时前
C语言、Java、Python 的选择与未来发展以及学习路线
java·c语言·python
西西学代码8 小时前
Flutter---个人信息(1)---实现简单的UI
开发语言·javascript·flutter
superman超哥8 小时前
仓颉语言中正则表达式引擎的深度剖析与实践
开发语言·后端·仓颉
在坚持一下我可没意见8 小时前
Java 网络编程:TCP 与 UDP 的「通信江湖」(基于UDP回显服务器)
java·服务器·开发语言·tcp/ip·udp·java-ee
少爷晚安。8 小时前
Java零基础学习完整笔记,基于Intellij IDEA开发工具,笔记持续更新中
java·笔记·学习
悟能不能悟8 小时前
在service方法中已经catch异常,Transactional失效怎么办
java·数据库·sql
西红柿维生素8 小时前
23种设计模式-框架中的使用
java·开发语言·设计模式
LNN20228 小时前
Qt creator +Valgrind检测内存泄漏(linux)
linux·开发语言·qt