Bun SQLite 数据库 #

概述 #

Bun 内置了高性能的 SQLite 数据库驱动,无需安装额外依赖即可使用 SQLite。Bun 的 SQLite 实现性能极高,是 Node.js 上 sqlite3 的数倍。

快速开始 #

创建数据库 #

typescript
import { Database } from "bun:sqlite";

// 内存数据库
const db = new Database(":memory:");

// 文件数据库
const db = new Database("./mydb.sqlite");

// 只读模式
const db = new Database("./mydb.sqlite", { readonly: true });

// 创建文件(如果不存在)
const db = new Database("./mydb.sqlite", { create: true });

基本操作 #

typescript
import { Database } from "bun:sqlite";

const db = new Database(":memory:");

// 创建表
db.run(`
  CREATE TABLE users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    email TEXT UNIQUE,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
  )
`);

// 插入数据
db.run("INSERT INTO users (name, email) VALUES (?, ?)", ["Alice", "alice@example.com"]);
db.run("INSERT INTO users (name, email) VALUES (?, ?)", ["Bob", "bob@example.com"]);

// 查询数据
const users = db.query("SELECT * FROM users").all();
console.log(users);

// 关闭数据库
db.close();

查询方法 #

query() - 查询对象 #

typescript
// 创建查询对象
const query = db.query("SELECT * FROM users WHERE id = ?");

// 获取所有结果
const all = query.all(1);

// 获取单条结果
const one = query.get(1);

// 获取第一个值
const value = query.values(1);

// 释放查询
query.finalize();

run() - 执行语句 #

typescript
// 执行不返回结果的语句
db.run("CREATE TABLE test (id INTEGER, name TEXT)");

// 插入数据
const result = db.run("INSERT INTO users (name) VALUES (?)", ["Charlie"]);
console.log(result.lastInsertRowid);  // 最后插入的 ID
console.log(result.changes);          // 影响的行数

all() - 获取所有结果 #

typescript
const users = db.query("SELECT * FROM users").all();
// [{ id: 1, name: "Alice", ... }, { id: 2, name: "Bob", ... }]

get() - 获取单条结果 #

typescript
const user = db.query("SELECT * FROM users WHERE id = ?").get(1);
// { id: 1, name: "Alice", ... }

// 如果没有结果返回 null
const notFound = db.query("SELECT * FROM users WHERE id = ?").get(999);
// null

values() - 获取值数组 #

typescript
const names = db.query("SELECT name FROM users").values();
// [["Alice"], ["Bob"], ["Charlie"]]

参数绑定 #

位置参数 #

typescript
// 使用 ? 占位符
db.run("INSERT INTO users (name, email) VALUES (?, ?)", ["Alice", "alice@example.com"]);

// 查询
const user = db.query("SELECT * FROM users WHERE id = ?").get(1);

命名参数 #

typescript
// 使用 $name 或 :name 或 @name
db.run(
  "INSERT INTO users (name, email) VALUES ($name, $email)",
  { $name: "Alice", $email: "alice@example.com" }
);

// 查询
const user = db.query("SELECT * FROM users WHERE name = $name").get({ $name: "Alice" });

参数类型 #

typescript
// 支持的类型
db.run("INSERT INTO test VALUES (?, ?, ?, ?, ?)", [
  123,              // INTEGER
  3.14,             // REAL
  "hello",          // TEXT
  new Uint8Array([1, 2, 3]),  // BLOB
  null,             // NULL
]);

预编译语句 #

创建预编译语句 #

typescript
// 预编译语句性能更好
const insertStmt = db.prepare("INSERT INTO users (name, email) VALUES (?, ?)");
const selectStmt = db.prepare("SELECT * FROM users WHERE id = ?");

// 使用
insertStmt.run("Alice", "alice@example.com");
const user = selectStmt.get(1);

语句方法 #

typescript
const stmt = db.prepare("SELECT * FROM users WHERE id = ?");

// 运行(不返回结果)
stmt.run(1);

// 获取单条
const user = stmt.get(1);

// 获取所有
const users = stmt.all();

// 获取值
const values = stmt.values();

// 迭代
for (const user of stmt.iterate()) {
  console.log(user);
}

事务 #

基本事务 #

typescript
// 手动事务
db.run("BEGIN TRANSACTION");

try {
  db.run("INSERT INTO users (name) VALUES (?)", ["User1"]);
  db.run("INSERT INTO users (name) VALUES (?)", ["User2"]);
  db.run("COMMIT");
} catch (error) {
  db.run("ROLLBACK");
  throw error;
}

transaction() 方法 #

typescript
// 使用 transaction 方法
const insertMany = db.transaction((users) => {
  const stmt = db.prepare("INSERT INTO users (name, email) VALUES (?, ?)");
  for (const user of users) {
    stmt.run(user.name, user.email);
  }
});

// 执行事务
insertMany([
  { name: "User1", email: "user1@example.com" },
  { name: "User2", email: "user2@example.com" },
]);

嵌套事务 #

typescript
const outer = db.transaction((data) => {
  const inner = db.transaction((items) => {
    for (const item of items) {
      db.run("INSERT INTO items (name) VALUES (?)", [item]);
    }
  });
  
  inner(data.items);
  db.run("UPDATE stats SET count = count + ?", [data.items.length]);
});

outer({ items: ["a", "b", "c"] });

事务选项 #

typescript
const tx = db.transaction((data) => {
  // 事务代码
}, {
  // 事务类型
  type: "deferred",  // deferred, immediate, exclusive
});

tx(data);

数据类型映射 #

SQLite 到 JavaScript #

SQLite 类型 JavaScript 类型
INTEGER number
REAL number
TEXT string
BLOB Uint8Array
NULL null

JavaScript 到 SQLite #

JavaScript 类型 SQLite 类型
number INTEGER 或 REAL
string TEXT
Uint8Array BLOB
Buffer BLOB
null NULL
undefined NULL
boolean INTEGER (0 或 1)
Date TEXT (ISO 8601)
Array/Object TEXT (JSON 字符串)

自定义类型处理 #

typescript
// 存储日期
db.run("INSERT INTO events (created_at) VALUES (?)", [new Date().toISOString()]);

// 读取日期
const row = db.query("SELECT created_at FROM events").get();
const date = new Date(row.created_at as string);

// 存储 JSON
db.run("INSERT INTO config (data) VALUES (?)", [JSON.stringify({ key: "value" })]);

// 读取 JSON
const config = db.query("SELECT data FROM config").get();
const data = JSON.parse(config.data as string);

表操作 #

创建表 #

typescript
db.run(`
  CREATE TABLE IF NOT EXISTS products (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    name TEXT NOT NULL,
    price REAL NOT NULL,
    description TEXT,
    category_id INTEGER,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (category_id) REFERENCES categories(id)
  )
`);

// 创建索引
db.run("CREATE INDEX IF NOT EXISTS idx_products_name ON products(name)");
db.run("CREATE INDEX IF NOT EXISTS idx_products_category ON products(category_id)");

修改表 #

typescript
// 添加列
db.run("ALTER TABLE users ADD COLUMN age INTEGER");

// 重命名表
db.run("ALTER TABLE users RENAME TO members");

// 重命名列
db.run("ALTER TABLE users RENAME COLUMN name TO username");

删除表 #

typescript
db.run("DROP TABLE IF EXISTS temp_table");
db.run("DROP INDEX IF EXISTS idx_name");

查询技巧 #

分页查询 #

typescript
function getPaginated(page: number, limit: number) {
  const offset = (page - 1) * limit;
  
  const items = db.query(`
    SELECT * FROM products
    ORDER BY created_at DESC
    LIMIT ? OFFSET ?
  `).all(limit, offset);
  
  const total = db.query("SELECT COUNT(*) as count FROM products").get() as { count: number };
  
  return {
    items,
    total: total.count,
    page,
    limit,
    totalPages: Math.ceil(total.count / limit),
  };
}

搜索查询 #

typescript
function searchProducts(keyword: string) {
  return db.query(`
    SELECT * FROM products
    WHERE name LIKE ? OR description LIKE ?
    ORDER BY name
  `).all(`%${keyword}%`, `%${keyword}%`);
}

聚合查询 #

typescript
// 统计
const stats = db.query(`
  SELECT 
    COUNT(*) as total,
    AVG(price) as avg_price,
    MIN(price) as min_price,
    MAX(price) as max_price,
    SUM(price) as total_price
  FROM products
`).get();

// 分组统计
const byCategory = db.query(`
  SELECT 
    category_id,
    COUNT(*) as count,
    AVG(price) as avg_price
  FROM products
  GROUP BY category_id
  HAVING count > 0
  ORDER BY count DESC
`).all();

关联查询 #

typescript
// JOIN 查询
const orders = db.query(`
  SELECT 
    orders.id,
    orders.total,
    users.name as user_name,
    users.email as user_email
  FROM orders
  JOIN users ON orders.user_id = users.id
  WHERE orders.id = ?
`).get(1);

性能优化 #

使用预编译语句 #

typescript
// 不推荐:每次都解析 SQL
for (const user of users) {
  db.run("INSERT INTO users (name) VALUES (?)", [user.name]);
}

// 推荐:使用预编译语句
const stmt = db.prepare("INSERT INTO users (name) VALUES (?)");
for (const user of users) {
  stmt.run(user.name);
}

批量插入 #

typescript
// 使用事务批量插入
const bulkInsert = db.transaction((items) => {
  const stmt = db.prepare("INSERT INTO items (name, value) VALUES (?, ?)");
  for (const item of items) {
    stmt.run(item.name, item.value);
  }
});

const items = Array.from({ length: 10000 }, (_, i) => ({
  name: `Item ${i}`,
  value: i,
}));

bulkInsert(items);

使用 WAL 模式 #

typescript
// 启用 WAL 模式(Write-Ahead Logging)
db.run("PRAGMA journal_mode = WAL");

// 其他优化设置
db.run("PRAGMA synchronous = NORMAL");
db.run("PRAGMA cache_size = 10000");
db.run("PRAGMA temp_store = MEMORY");

索引优化 #

typescript
// 创建索引
db.run("CREATE INDEX idx_users_email ON users(email)");
db.run("CREATE INDEX idx_orders_user_id ON orders(user_id)");
db.run("CREATE INDEX idx_orders_created_at ON orders(created_at)");

// 复合索引
db.run("CREATE INDEX idx_products_category_price ON products(category_id, price)");

// 分析查询计划
const plan = db.query("EXPLAIN QUERY PLAN SELECT * FROM users WHERE email = ?").get("test@example.com");
console.log(plan);

错误处理 #

捕获错误 #

typescript
try {
  db.run("INSERT INTO users (email) VALUES (?)", ["duplicate@example.com"]);
} catch (error) {
  if (error.message.includes("UNIQUE constraint failed")) {
    console.error("Email already exists");
  } else {
    throw error;
  }
}

错误类型 #

typescript
import { Database, SQLiteError } from "bun:sqlite";

try {
  db.run("INVALID SQL");
} catch (error) {
  if (error instanceof SQLiteError) {
    console.error("SQLite Error:", error.message);
    console.error("Code:", error.code);
  }
}

数据库维护 #

备份数据库 #

typescript
// 备份到文件
import { copyFile } from "fs/promises";

await copyFile("./mydb.sqlite", "./mydb-backup.sqlite");

// 或使用 SQLite 命令
db.run("VACUUM INTO './backup.sqlite'");

优化数据库 #

typescript
// VACUUM - 重建数据库,释放空间
db.run("VACUUM");

// ANALYZE - 更新统计信息
db.run("ANALYZE");

// 检查完整性
const integrity = db.query("PRAGMA integrity_check").get();
console.log(integrity);

数据库信息 #

typescript
// 表列表
const tables = db.query("SELECT name FROM sqlite_master WHERE type='table'").all();

// 表结构
const schema = db.query("SELECT sql FROM sqlite_master WHERE name = ?").get("users");

// 数据库大小
const stats = require("fs").statSync("./mydb.sqlite");
console.log(`Database size: ${stats.size} bytes`);

完整示例 #

用户管理 #

typescript
import { Database } from "bun:sqlite";

class UserRepository {
  private db: Database;
  private insertStmt: ReturnType<Database["prepare"]>;
  private updateStmt: ReturnType<Database["prepare"]>;
  private deleteStmt: ReturnType<Database["prepare"]>;
  private findByIdStmt: ReturnType<Database["prepare"]>;
  private findAllStmt: ReturnType<Database["prepare"]>;

  constructor(dbPath: string = ":memory:") {
    this.db = new Database(dbPath);
    this.init();
    this.prepareStatements();
  }

  private init() {
    this.db.run(`
      CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT NOT NULL,
        email TEXT UNIQUE NOT NULL,
        created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
        updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
      )
    `);
  }

  private prepareStatements() {
    this.insertStmt = this.db.prepare(
      "INSERT INTO users (name, email) VALUES ($name, $email) RETURNING *"
    );
    this.updateStmt = this.db.prepare(
      "UPDATE users SET name = $name, email = $email, updated_at = CURRENT_TIMESTAMP WHERE id = $id RETURNING *"
    );
    this.deleteStmt = this.db.prepare("DELETE FROM users WHERE id = ?");
    this.findByIdStmt = this.db.prepare("SELECT * FROM users WHERE id = ?");
    this.findAllStmt = this.db.prepare("SELECT * FROM users ORDER BY created_at DESC");
  }

  create(name: string, email: string) {
    return this.insertStmt.get({ $name: name, $email: email });
  }

  update(id: number, name: string, email: string) {
    return this.updateStmt.get({ $id: id, $name: name, $email: email });
  }

  delete(id: number) {
    return this.deleteStmt.run(id);
  }

  findById(id: number) {
    return this.findByIdStmt.get(id);
  }

  findAll() {
    return this.findAllStmt.all();
  }

  close() {
    this.db.close();
  }
}

// 使用
const userRepo = new UserRepository();

const user = userRepo.create("Alice", "alice@example.com");
console.log(user);

const allUsers = userRepo.findAll();
console.log(allUsers);

userRepo.close();

下一步 #

现在你已经了解了 Bun 的 SQLite 数据库,接下来学习 网络请求 深入了解 Bun 的网络 API。

最后更新:2026-03-29