强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

rqlite 完全指南 / 第 4 章:基础操作

第 4 章:基础操作

掌握 rqlite 的表管理、CRUD 操作和基本查询语法。


4.1 操作前的准备

rqlite 所有数据库操作都通过 HTTP API 进行。本章示例使用 curl 发送请求,假设 rqlite 单节点运行在 localhost:4001

为简化后续示例,先定义一些别名:

# 查询(SELECT)
alias rq='curl -s -G localhost:4001/db/query --data-urlencode'

# 执行(INSERT/UPDATE/DELETE/DDL)
alias rxe='curl -s -XPOST localhost:4001/db/execute -H "Content-Type: application/json" -d'

4.2 创建表

4.2.1 基本建表

# 创建用户表
rxe '[["CREATE TABLE IF NOT EXISTS users (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    username TEXT NOT NULL UNIQUE,
    email TEXT NOT NULL,
    age INTEGER DEFAULT 0,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
)"]]'

响应:

{
    "results": [{"time": 0.000345}]
}

4.2.2 常用字段类型

SQLite 类型说明示例
INTEGER整数id INTEGER PRIMARY KEY
TEXT文本name TEXT NOT NULL
REAL浮点数price REAL DEFAULT 0.0
BLOB二进制数据avatar BLOB
DATETIME日期时间(存储为 TEXT/REAL/INTEGER)created_at DATETIME
BOOLEAN布尔值(0 或 1)active BOOLEAN DEFAULT 1
JSONJSON 文本(SQLite 3.38+)metadata JSON

4.2.3 创建索引

# 单列索引
rxe '[["CREATE INDEX IF NOT EXISTS idx_users_email ON users(email)"]]'

# 复合索引
rxe '[["CREATE INDEX IF NOT EXISTS idx_users_age_name ON users(age, username)"]]'

# 唯一索引
rxe '[["CREATE UNIQUE INDEX IF NOT EXISTS idx_users_username ON users(username)"]]'

4.2.4 创建带约束的表

rxe '[["CREATE TABLE IF NOT EXISTS orders (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id INTEGER NOT NULL,
    product TEXT NOT NULL,
    quantity INTEGER CHECK(quantity > 0),
    price REAL CHECK(price >= 0),
    status TEXT CHECK(status IN (\"pending\", \"paid\", \"shipped\", \"completed\", \"cancelled\")) DEFAULT \"pending\",
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
)"]]'

注意: 默认情况下 rqlite 不强制外键约束。如需启用,启动时添加 -fk 参数。


4.3 插入数据(INSERT)

4.3.1 单条插入

# 使用参数绑定(推荐,防止 SQL 注入)
rxe '[["INSERT INTO users (username, email, age) VALUES (?, ?, ?)", "zhangsan", "zhangsan@example.com", 28]]'

响应:

{
    "results": [
        {
            "last_insert_id": 1,
            "rows_affected": 1,
            "time": 0.000456
        }
    ]
}

4.3.2 批量插入

rqlite 支持在一个请求中发送多条 SQL,它们会在同一个事务中执行:

rxe '[
    ["INSERT INTO users (username, email, age) VALUES (?, ?, ?)", "lisi", "lisi@example.com", 32],
    ["INSERT INTO users (username, email, age) VALUES (?, ?, ?)", "wangwu", "wangwu@example.com", 25],
    ["INSERT INTO users (username, email, age) VALUES (?, ?, ?)", "zhaoliu", "zhaoliu@example.com", 30],
    ["INSERT INTO users (username, email, age) VALUES (?, ?, ?)", "sunqi", "sunqi@example.com", 22]
]'

响应:

{
    "results": [
        {"last_insert_id": 2, "rows_affected": 1, "time": 0.000123},
        {"last_insert_id": 3, "rows_affected": 1, "time": 0.000112},
        {"last_insert_id": 4, "rows_affected": 1, "time": 0.000109},
        {"last_insert_id": 5, "rows_affected": 1, "time": 0.000115}
    ],
    "time": 0.000500
}

重要: 单个请求中的多条 SQL 在同一事务中执行。如果任何一条失败,全部回滚。

4.3.3 使用 INSERT OR REPLACE

# 如果记录已存在(主键或唯一约束冲突),则替换
rxe '[["INSERT OR REPLACE INTO users (id, username, email, age) VALUES (?, ?, ?, ?)", 1, "zhangsan", "new_email@example.com", 29]]'

4.3.4 使用 INSERT OR IGNORE

# 如果记录已存在,则忽略
rxe '[["INSERT OR IGNORE INTO users (username, email, age) VALUES (?, ?, ?)", "zhangsan", "another@example.com", 30]]'

4.4 查询数据(SELECT)

4.4.1 基本查询

# 查询所有用户
rq 'q=SELECT * FROM users' | python3 -m json.tool

响应:

{
    "results": [
        {
            "columns": ["id", "username", "email", "age", "created_at"],
            "types": ["integer", "text", "text", "integer", "datetime"],
            "values": [
                [1, "zhangsan", "new_email@example.com", 29, "2026-05-10 10:00:00"],
                [2, "lisi", "lisi@example.com", 32, "2026-05-10 10:00:01"],
                [3, "wangwu", "wangwu@example.com", 25, "2026-05-10 10:00:01"],
                [4, "zhaoliu", "zhaoliu@example.com", 30, "2026-05-10 10:00:01"],
                [5, "sunqi", "sunqi@example.com", 22, "2026-05-10 10:00:01"]
            ]
        }
    ],
    "time": 0.000234
}

4.4.2 条件查询

# WHERE 条件
rq 'q=SELECT * FROM users WHERE age > 25'

# LIKE 模糊匹配
rq 'q=SELECT * FROM users WHERE username LIKE "zhang%"'

# IN 操作符
rq 'q=SELECT * FROM users WHERE id IN (1, 3, 5)'

4.4.3 排序和分页

# 排序
rq 'q=SELECT * FROM users ORDER BY age DESC'

# 分页(LIMIT + OFFSET)
rq 'q=SELECT * FROM users ORDER BY id LIMIT 2 OFFSET 0'
rq 'q=SELECT * FROM users ORDER BY id LIMIT 2 OFFSET 2'

4.4.4 聚合查询

# 计数
rq 'q=SELECT COUNT(*) as total FROM users'

# 按条件分组
rq 'q=SELECT age, COUNT(*) as count FROM users GROUP BY age ORDER BY count DESC'

# 平均值
rq 'q=SELECT AVG(age) as avg_age FROM users'

# 最大值和最小值
rq 'q=SELECT MAX(age) as max_age, MIN(age) as min_age FROM users'

4.4.5 多表联查

# 先插入一些订单数据
rxe '[
    ["INSERT INTO orders (user_id, product, quantity, price, status) VALUES (?, ?, ?, ?, ?)", 1, "笔记本", 2, 5999.0, "completed"],
    ["INSERT INTO orders (user_id, product, quantity, price, status) VALUES (?, ?, ?, ?, ?)", 1, "鼠标", 5, 99.0, "shipped"],
    ["INSERT INTO orders (user_id, product, quantity, price, status) VALUES (?, ?, ?, ?, ?)", 2, "键盘", 1, 399.0, "pending"],
    ["INSERT INTO orders (user_id, product, quantity, price, status) VALUES (?, ?, ?, ?, ?)", 3, "显示器", 1, 2499.0, "paid"]
]'

# JOIN 查询:用户及其订单
rq 'q=SELECT u.username, o.product, o.quantity, o.price, o.status
     FROM users u
     INNER JOIN orders o ON u.id = o.user_id
     ORDER BY u.username'

4.4.6 子查询

# 查找有订单的用户
rq 'q=SELECT * FROM users WHERE id IN (SELECT DISTINCT user_id FROM orders)'

# 查找消费总额最高的用户
rq 'q=SELECT u.username, SUM(o.quantity * o.price) as total_spent
     FROM users u
     INNER JOIN orders o ON u.id = o.user_id
     GROUP BY u.id
     ORDER BY total_spent DESC
     LIMIT 1'

4.5 更新数据(UPDATE)

4.5.1 基本更新

# 更新单条记录
rxe '[["UPDATE users SET email = ? WHERE id = ?", "zs@new.com", 1]]'

4.5.2 批量更新

# 同一事务中更新多条
rxe '[
    ["UPDATE users SET age = age + 1 WHERE age < 30"],
    ["UPDATE orders SET status = \"completed\" WHERE status = \"shipped\""]
]'

4.5.3 条件更新

# 使用 CASE 表达式
rxe '[["UPDATE orders SET price = CASE
    WHEN quantity > 3 THEN price * 0.8
    WHEN quantity > 1 THEN price * 0.9
    ELSE price
END WHERE status != \"completed\""]]'

4.6 删除数据(DELETE)

4.6.1 基本删除

# 删除特定记录
rxe '[["DELETE FROM orders WHERE status = \"cancelled\""]]'

# 删除所有记录(保留表结构)
rxe '[["DELETE FROM orders"]]'

4.6.2 级联删除

由于外键约束(需启动时启用 -fk),删除用户时关联的订单也会被删除:

# 删除用户及其关联订单(需外键约束)
rxe '[["DELETE FROM users WHERE id = 5"]]'

4.7 表管理操作

4.7.1 查看表结构

# 查看所有表
rq 'q=SELECT name FROM sqlite_master WHERE type=\"table\" AND name NOT LIKE \"sqlite_%\"'

# 查看特定表的 DDL
rq 'q=SELECT sql FROM sqlite_master WHERE name=\"users\"'

# PRAGMA 方式查看表信息
rq 'q=PRAGMA table_info(users)'

4.7.2 修改表结构

SQLite 的 ALTER TABLE 支持有限,rqlite 同样继承这些限制:

# 重命名表
rxe '[["ALTER TABLE orders RENAME TO customer_orders"]]'

# 添加列
rxe '[["ALTER TABLE users ADD COLUMN phone TEXT"]]'

# 注意:SQLite 不支持 DROP COLUMN(3.35.0 前)
# 如需删除列或修改列类型,需要重建表

4.7.3 重建表的步骤

rxe '[
    ["CREATE TABLE users_new (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT NOT NULL UNIQUE, email TEXT NOT NULL, age INTEGER DEFAULT 0, phone TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP)"],
    ["INSERT INTO users_new SELECT id, username, email, age, phone, created_at FROM users"],
    ["DROP TABLE users"],
    ["ALTER TABLE users_new RENAME TO users"]
]'

4.7.4 删除表

rxe '[["DROP TABLE IF EXISTS customer_orders"]]'

# 删除所有表
rq 'q=SELECT name FROM sqlite_master WHERE type="table"' | \
    python3 -c "
import json, sys
data = json.load(sys.stdin)
tables = data['results'][0]['values']
stmts = [[f'DROP TABLE IF EXISTS {t[0]}'] for t in tables]
print(json.dumps(stmts))
" | curl -s -XPOST localhost:4001/db/execute \
    -H 'Content-Type: application/json' -d @-

4.8 PRAGMA 配置

通过 PRAGMA 可以查看和调整 SQLite 的运行参数:

# 查看当前 journal 模式
rq 'q=PRAGMA journal_mode'

# 查看页面大小
rq 'q=PRAGMA page_size'

# 查看缓存大小
rq 'q=PRAGMA cache_size'

# 查看所有 PRAGMA 设置
rq 'q=PRAGMA compile_options'
PRAGMA说明常用值
journal_mode日志模式WAL(rqlite 默认)
page_size页面大小4096
cache_size缓存页面数-2000(约 2MB)
foreign_keys外键约束ON/OFF
busy_timeout忙等待超时5000ms
synchronous同步级别NORMAL

注意: 大多数 PRAGMA 在 rqlite 中读取有效,但写入 PRAGMA 不保证在所有节点上生效。


4.9 常见操作速查表

操作SQL 示例
建表CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)
建索引CREATE INDEX idx ON t(name)
插入INSERT INTO t (name) VALUES (?)
查询SELECT * FROM t WHERE id = ?
更新UPDATE t SET name = ? WHERE id = ?
删除DELETE FROM t WHERE id = ?
聚合SELECT COUNT(*), AVG(col) FROM t GROUP BY col2
联表SELECT * FROM a JOIN b ON a.id = b.aid
分页SELECT * FROM t LIMIT ? OFFSET ?
模糊SELECT * FROM t WHERE name LIKE ?

4.10 业务场景:博客系统数据模型

一个简单的博客系统数据模型示例:

# 1. 创建表
rxe '[
    ["CREATE TABLE IF NOT EXISTS categories (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE, slug TEXT NOT NULL UNIQUE)"],
    ["CREATE TABLE IF NOT EXISTS articles (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, slug TEXT NOT NULL UNIQUE, content TEXT, category_id INTEGER, status TEXT CHECK(status IN (\"draft\", \"published\", \"archived\")) DEFAULT \"draft\", view_count INTEGER DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (category_id) REFERENCES categories(id))"],
    ["CREATE TABLE IF NOT EXISTS comments (id INTEGER PRIMARY KEY AUTOINCREMENT, article_id INTEGER NOT NULL, author TEXT NOT NULL, content TEXT NOT NULL, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE)"],
    ["CREATE INDEX IF NOT EXISTS idx_articles_status ON articles(status)",
     "CREATE INDEX IF NOT EXISTS idx_articles_category ON articles(category_id)",
     "CREATE INDEX IF NOT EXISTS idx_comments_article ON comments(article_id)"]
]'

# 2. 插入分类
rxe '[
    ["INSERT INTO categories (name, slug) VALUES (?, ?)", "技术", "tech"],
    ["INSERT INTO categories (name, slug) VALUES (?, ?)", "随笔", "essay"],
    ["INSERT INTO categories (name, slug) VALUES (?, ?)", "教程", "tutorial"]
]'

# 3. 插入文章
rxe '[["INSERT INTO articles (title, slug, content, category_id, status) VALUES (?, ?, ?, ?, ?)", "rqlite 入门", "rqlite-intro", "本文介绍 rqlite 的基本概念...", 1, "published"]]'

# 4. 查询已发布文章列表
rq 'q=SELECT a.title, a.view_count, c.name as category FROM articles a LEFT JOIN categories c ON a.category_id = c.id WHERE a.status = \"published\" ORDER BY a.created_at DESC'

4.11 本章小结

要点内容
API 风格查询用 GET /db/query,写入用 POST /db/execute
参数绑定使用 ? 占位符防止 SQL 注入
批量操作单请求多条 SQL,在同一事务中执行
SQLite 限制ALTER TABLE 功能有限,不支持 ATTACH
推荐实践始终使用参数绑定,批量操作利用事务

上一章:第 3 章:架构深度解析 下一章:第 5 章:HTTP API 详解