SQLite 附加数据库
SQLite 附加数据库(ATTACH DATABASE)详解(2025 最新版)
核心概念:
一个 SQLite 连接 → 可附加多个
.db文件
每个附加数据库有 别名(alias),像“命名空间”一样使用。
一、为什么需要附加数据库?
| 场景 | 说明 |
|---|---|
| 跨数据库查询 | 合并多个 .db 文件的数据 |
| 模块化设计 | 用户数据、日志、配置分开存储 |
| 数据迁移 | 从旧数据库导入到新数据库 |
| 临时分析 | 附加只读数据库进行 JOIN 查询 |
二、基本语法
ATTACH DATABASE '文件路径' AS 别名;
DETACH DATABASE 别名;
三、实战示例
示例 1:附加并跨库查询
# 创建两个数据库
sqlite3 users.db "CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT);"
sqlite3 orders.db "CREATE TABLE orders(id INTEGER PRIMARY KEY, user_id INTEGER, product TEXT);"
# 插入数据
sqlite3 users.db "INSERT INTO users(name) VALUES ('张三'), ('李四');"
sqlite3 orders.db "INSERT INTO orders(user_id, product) VALUES (1, '手机'), (2, '电脑');"
附加并查询:
sqlite3 users.db
-- 附加 orders.db,起别名 ord
ATTACH DATABASE 'orders.db' AS ord;
-- 跨库 JOIN 查询
SELECT u.name, o.product
FROM users u
LEFT JOIN ord.orders o ON u.id = o.user_id;
-- 查看所有附加数据库
.databases
输出:
name product
---------- -------
张三 手机
李四 电脑
示例 2:数据迁移(旧 → 新)
-- 1. 创建新数据库结构
sqlite3 newdb.db
CREATE TABLE users(id INTEGER PRIMARY KEY, name TEXT, email TEXT);
-- 2. 附加旧数据库
ATTACH DATABASE 'olddb.db' AS old;
-- 3. 复制并转换数据
INSERT INTO users(id, name, email)
SELECT id, name, name || '@example.com'
FROM old.users;
-- 4. 验证
SELECT * FROM users;
-- 5. 分离
DETACH DATABASE old;
示例 3:附加内存数据库(临时计算)
-- 主数据库
ATTACH DATABASE ':memory:' AS temp;
-- 在内存中创建临时表
CREATE TABLE temp.stats AS
SELECT department, AVG(salary) FROM main.employees GROUP BY department;
-- 使用临时结果
SELECT * FROM temp.stats;
四、路径写法详解
| 写法 | 说明 |
|---|---|
'data.db' | 相对路径(当前目录) |
'../config.db' | 上级目录 |
'/home/user/app.db' | 绝对路径(Linux/macOS) |
'C:\data\app.db' | 绝对路径(Windows) |
':memory:' | 内存数据库 |
'' | 临时磁盘数据库(自动删除) |
五、限制与注意事项
| 项目 | 限制 |
|---|---|
| 最大附加数 | 理论 125 个(受 SQLITE_MAX_ATTACHED 限制,默认 10) |
| 事务跨库 | 不支持!不能 BEGIN; INSERT INTO db1...; INSERT INTO db2...; COMMIT; |
| 外键跨库 | 不支持 |
| 写权限 | 附加的数据库必须有写权限才能修改 |
| 路径安全 | 避免动态拼接路径,防 SQL 注入 |
查看当前限制:
PRAGMA compile_options;
-- 查找 SQLITE_MAX_ATTACHED=10
六、查看附加状态
-- 查看所有附加数据库
.databases
-- 输出示例
seq name file
--- ------- -----------------------
0 main /path/to/main.db
1 temp :memory:
2 backup /path/to/backup.db
-- 查看表来源
SELECT name, tbl_name, sql FROM sqlite_master WHERE type='table';
七、编程中使用(Python 示例)
import sqlite3
conn = sqlite3.connect('main.db')
cursor = conn.cursor()
# 附加数据库
cursor.execute("ATTACH DATABASE 'logs.db' AS logs")
# 跨库查询
cursor.execute("""
SELECT u.name, l.action, l.timestamp
FROM users u
LEFT JOIN logs.actions l ON u.id = l.user_id
ORDER BY l.timestamp DESC
""")
for row in cursor.fetchall():
print(row)
# 分离(可选)
cursor.execute("DETACH DATABASE logs")
conn.close()
八、最佳实践
1. 别名命名规范
ATTACH DATABASE 'config.db' AS cfg;
ATTACH DATABASE 'archive_2024.db' AS arch_2024;
2. 只读附加(安全)
ATTACH DATABASE 'file:readonly.db?mode=ro' AS readonly;
-- URI 模式,防止误写
3. 临时附加 + 事务分离
-- 不要跨库事务!
BEGIN; INSERT INTO main.users ...; COMMIT;
BEGIN; INSERT INTO logs.events ...; COMMIT;
4. 定期分离
DETACH DATABASE temp; -- 释放内存
九、常见问题
| 问题 | 解决 |
|---|---|
database is locked | 不要跨库写事务 |
no such table: db.table | 检查别名和路径 |
unable to open database file | 检查文件权限、路径 |
too many attached databases | 减少附加数,或重新编译 SQLite |
十、速查表(贴桌边)
-- 附加
ATTACH DATABASE 'file.db' AS alias;
-- 分离
DETACH DATABASE alias;
-- 查看
.databases
-- 只读附加
ATTACH DATABASE 'file:data.db?mode=ro' AS ro;
-- 内存数据库
ATTACH DATABASE ':memory:' AS temp;
十一、完整实战:多模块系统
# 项目结构
app/
├── main.db ← 用户、订单
├── config.db ← 系统设置
├── logs.db ← 操作日志
└── analyze.sql ← 分析脚本
analyze.sql:
-- 附加所有数据库
ATTACH DATABASE 'main.db' AS main;
ATTACH DATABASE 'config.db' AS cfg;
ATTACH DATABASE 'logs.db' AS logs;
-- 复杂分析
SELECT
u.name,
COUNT(o.id) as order_count,
MAX(l.timestamp) as last_action
FROM main.users u
LEFT JOIN main.orders o ON u.id = o.user_id
LEFT JOIN logs.actions l ON u.id = l.user_id
GROUP BY u.id
HAVING order_count > 5;
-- 清理
DETACH DATABASE main;
DETACH DATABASE cfg;
DETACH DATABASE logs;
运行:
sqlite3 < analyze.sql > report.csv
下一步学习?回复:
跨库迁移→ 完整数据迁移方案只读附加→ URI 模式详解Python 多数据库→ 连接池管理性能优化→ 附加数据库的索引策略
现在试试:
sqlite3 test1.db "CREATE TABLE t(x); INSERT INTO t VALUES('A');"
sqlite3 test2.db "ATTACH 'test1.db' AS db1; SELECT * FROM db1.t;"
看到 'A' 就成功了!