SQLite索引 #

一、索引概述 #

1.1 什么是索引 #

sql
-- 索引是一种数据结构,用于加速查询
-- 类似于书籍的目录,可以快速定位数据

-- 索引的特点:
-- 1. 提高查询速度
-- 2. 降低插入/更新/删除速度
-- 3. 占用存储空间
-- 4. 自动维护

1.2 索引类型 #

text
SQLite 索引类型:
├── 单列索引
├── 复合索引
├── 唯一索引
├── 部分索引
├── 表达式索引
└── 全文索引(FTS)

二、创建索引 #

2.1 基本语法 #

sql
-- CREATE INDEX 基本语法
CREATE INDEX index_name ON table_name (column1, column2, ...);

-- 示例表
CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    name TEXT,
    email TEXT,
    age INTEGER,
    status INTEGER
);

-- 创建单列索引
CREATE INDEX idx_users_email ON users(email);

-- 创建复合索引
CREATE INDEX idx_users_status_age ON users(status, age);

2.2 唯一索引 #

sql
-- 唯一索引:确保列值唯一
CREATE UNIQUE INDEX idx_users_email_unique ON users(email);

-- 尝试插入重复值会失败
INSERT INTO users (name, email) VALUES ('Test', 'existing@example.com');
-- Error: UNIQUE constraint failed

2.3 IF NOT EXISTS #

sql
-- 避免重复创建
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);

2.4 部分索引 #

sql
-- 部分索引:只索引满足条件的行
CREATE INDEX idx_users_active ON users(email) WHERE status = 1;

-- 只有活跃用户会被索引
-- 查询时自动使用
SELECT * FROM users WHERE status = 1 AND email = 'test@example.com';

2.5 表达式索引 #

sql
-- 表达式索引:对表达式结果建立索引
CREATE INDEX idx_users_email_lower ON users(lower(email));

-- 查询时使用相同表达式
SELECT * FROM users WHERE lower(email) = 'test@example.com';

三、查看索引 #

3.1 查看表索引 #

sql
-- 查看表的所有索引
PRAGMA index_list(users);

-- 输出:
-- seq | name              | unique | origin | partial
-- ----|-------------------|--------|--------|--------
-- 0   | idx_users_email   | 0      | c      | 0
-- 1   | idx_users_status  | 0      | c      | 0

-- 查看索引详情
PRAGMA index_info(idx_users_email);

-- 输出:
-- seqno | cid | name
-- ------|-----|-------
-- 0     | 2   | email

3.2 查看所有索引 #

sql
-- 查看数据库中所有索引
SELECT name, tbl_name, sql 
FROM sqlite_master 
WHERE type = 'index';

-- 只查看用户创建的索引
SELECT name, tbl_name, sql 
FROM sqlite_master 
WHERE type = 'index' AND sql IS NOT NULL;

四、删除索引 #

4.1 DROP INDEX #

sql
-- 删除索引
DROP INDEX idx_users_email;

-- 使用 IF EXISTS
DROP INDEX IF EXISTS idx_users_email;

五、索引使用 #

5.1 自动使用索引 #

sql
-- SQLite 查询优化器会自动选择是否使用索引

-- 使用 EXPLAIN QUERY PLAN 查看执行计划
EXPLAIN QUERY PLAN SELECT * FROM users WHERE email = 'test@example.com';

-- 输出:
-- QUERY PLAN
-- `--SEARCH TABLE users USING INDEX idx_users_email (email=?)

5.2 索引使用场景 #

sql
-- WHERE 条件
SELECT * FROM users WHERE email = 'test@example.com';

-- JOIN 条件
SELECT * FROM users u JOIN orders o ON u.id = o.user_id;
-- 需要在 orders.user_id 上创建索引

-- ORDER BY
SELECT * FROM users ORDER BY email;
-- 可以使用 idx_users_email 索引

-- GROUP BY
SELECT status, COUNT(*) FROM users GROUP BY status;
-- 可以使用 idx_users_status 索引

5.3 复合索引使用规则 #

sql
-- 复合索引:idx_users_status_age (status, age)

-- 使用索引
SELECT * FROM users WHERE status = 1;              -- ✓ 使用索引
SELECT * FROM users WHERE status = 1 AND age > 20; -- ✓ 使用索引

-- 不使用索引
SELECT * FROM users WHERE age > 20;                -- ✗ 不使用索引

-- 部分使用
SELECT * FROM users WHERE status = 1 ORDER BY age; -- ✓ 使用索引

-- 最左前缀原则:
-- 索引 (a, b, c)
-- WHERE a = 1         ✓
-- WHERE a = 1 AND b = 2 ✓
-- WHERE a = 1 AND b = 2 AND c = 3 ✓
-- WHERE b = 2         ✗
-- WHERE c = 3         ✗
-- WHERE a = 1 AND c = 3 ✓ (只用到a)

六、索引优化 #

6.1 选择索引列 #

sql
-- 选择索引列的原则:
-- 1. WHERE 条件中常用的列
-- 2. JOIN 条件中的列
-- 3. ORDER BY / GROUP BY 中的列
-- 4. 选择性高的列(值差异大)

-- 选择性计算
SELECT 
    COUNT(DISTINCT status) AS distinct_values,
    COUNT(*) AS total_rows,
    CAST(COUNT(DISTINCT status) AS REAL) / COUNT(*) AS selectivity
FROM users;

-- 选择性接近1的列适合建索引

6.2 避免过度索引 #

sql
-- 索引的代价:
-- 1. 占用存储空间
-- 2. 降低写入性能
-- 3. 增加维护成本

-- 建议:
-- 1. 只为必要的查询创建索引
-- 2. 定期审查和删除无用索引
-- 3. 使用复合索引代替多个单列索引

6.3 ANALYZE命令 #

sql
-- ANALYZE:收集统计信息,帮助优化器选择索引
ANALYZE;

-- 分析特定表
ANALYZE users;

-- 分析特定索引
ANALYZE idx_users_email;

-- 在大量数据变更后执行
INSERT INTO users ... -- 大量插入
ANALYZE;

七、索引与查询优化 #

7.1 使用EXPLAIN #

sql
-- EXPLAIN QUERY PLAN 查看执行计划
EXPLAIN QUERY PLAN SELECT * FROM users WHERE email = 'test@example.com';

-- 输出说明:
-- SCAN TABLE:全表扫描
-- SEARCH TABLE USING INDEX:使用索引
-- USE TEMP B-TREE:使用临时B树排序

-- 全表扫描示例
EXPLAIN QUERY PLAN SELECT * FROM users WHERE age > 20;
-- SCAN TABLE users

-- 使用索引示例
EXPLAIN QUERY PLAN SELECT * FROM users WHERE email = 'test@example.com';
-- SEARCH TABLE users USING INDEX idx_users_email

7.2 索引提示 #

sql
-- SQLite 不支持索引提示
-- 但可以通过 INDEXED BY 强制使用或不使用索引

-- 强制使用索引
SELECT * FROM users INDEXED BY idx_users_email WHERE email = 'test@example.com';

-- 禁止使用索引
SELECT * FROM users NOT INDEXED WHERE email = 'test@example.com';

7.3 覆盖索引 #

sql
-- 覆盖索引:查询的所有列都在索引中
CREATE INDEX idx_users_covering ON users(status, age, name);

-- 这个查询只需要访问索引
SELECT status, age, name FROM users WHERE status = 1;

-- 检查执行计划
EXPLAIN QUERY PLAN SELECT status, age, name FROM users WHERE status = 1;
-- SEARCH TABLE users USING COVERING INDEX idx_users_covering

八、特殊索引 #

8.1 主键索引 #

sql
-- 主键自动创建索引
CREATE TABLE users (
    id INTEGER PRIMARY KEY,  -- 自动创建索引
    name TEXT
);

-- INTEGER PRIMARY KEY 是 ROWID 的别名
-- 数据按 ROWID 存储,本身就是索引

8.2 唯一约束索引 #

sql
-- UNIQUE 约束自动创建唯一索引
CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    email TEXT UNIQUE  -- 自动创建唯一索引
);

-- 等价于
CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    email TEXT
);
CREATE UNIQUE INDEX idx_users_email ON users(email);

8.3 外键索引 #

sql
-- 外键不会自动创建索引
-- 建议手动创建
CREATE TABLE orders (
    id INTEGER PRIMARY KEY,
    user_id INTEGER,
    FOREIGN KEY (user_id) REFERENCES users(id)
);

-- 创建外键索引
CREATE INDEX idx_orders_user_id ON orders(user_id);

九、索引维护 #

9.1 重建索引 #

sql
-- SQLite 没有直接的 REINDEX 命令
-- 可以删除并重新创建

DROP INDEX idx_users_email;
CREATE INDEX idx_users_email ON users(email);

-- 或使用 REINDEX(SQLite 3.1+)
REINDEX idx_users_email;

-- 重建所有索引
REINDEX;

9.2 检查索引使用情况 #

sql
-- 查看索引统计
SELECT 
    name AS index_name,
    tbl_name AS table_name
FROM sqlite_master
WHERE type = 'index' AND sql IS NOT NULL;

-- 检查是否有未使用的索引
-- 需要通过应用程序日志分析

十、总结 #

索引类型 #

类型 说明 示例
单列索引 单列索引 CREATE INDEX idx ON t(col)
复合索引 多列索引 CREATE INDEX idx ON t(col1, col2)
唯一索引 值唯一 CREATE UNIQUE INDEX idx ON t(col)
部分索引 条件索引 CREATE INDEX idx ON t(col) WHERE …
表达式索引 表达式索引 CREATE INDEX idx ON t(lower(col))

最佳实践 #

  1. 为常用查询条件创建索引
  2. 遵循最左前缀原则设计复合索引
  3. 定期执行 ANALYZE 更新统计信息
  4. 避免过度索引
  5. 使用 EXPLAIN 检查索引使用情况

下一步,让我们学习视图!

最后更新:2026-03-27