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' 就成功了!

类似文章

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注