入门
CLI 基础
SQLite 将整个数据库存储在单个文件中。sqlite3 CLI 按需打开或创建 .sqlite/.db 文件。与 MySQL/PostgreSQL 不同,没有服务器进程——应用程序直接链接 SQLite 库。.dump 以文本 SQL 形式导出整个数据库,用于备份或迁移。
# open/create a database file
sqlite3 mydb.sqlite
# at the sqlite> prompt
.help # show help
.tables # list tables
.schema users # show CREATE statement for users
.databases # list attached databases
.dump # dump entire DB as SQL text
.quit # exit
# execute SQL from a file
sqlite3 mydb.sqlite < script.sql
# execute a query from command line
sqlite3 mydb.sqlite "SELECT COUNT(*) FROM users;"点命令
点命令由 sqlite3 CLI 解释,而非 SQL 引擎——它们不需要分号,且不能通过编程 API 使用。.mode 控制输出格式(交互使用时配合 .header on 的 'column' 模式最易读)。.schema 显示 CREATE 语句;.fullschema 额外包含索引、视图和触发器。用 .help 发现更多命令。
-- dot commands are CLI-only, NOT SQL (no semicolon needed)
.help -- list all dot commands
.tables -- list all tables
.schema -- schema for all tables
.schema users -- schema for the users table
.fullschema -- schema incl. indexes, triggers, views
.indices users -- list indexes on users
.header on -- show column headers
.mode column -- column-aligned output
.mode list -- default, pipe-separated
.mode csv -- CSV output
.mode json -- JSON output
.show -- show current settings
.width 10 20 30 -- set column widths
.timer on -- show query execution time数据库文件管理
ATTACH DATABASE 允许一个连接同时查询最多 10 个数据库文件(作为模式 main、temp 加附加的)。:memory: URI 创建纯内存数据库,关闭后消失——非常适合测试或临时计算。URI 文件名 (file:...) 启用只读、共享缓存等模式。每个附加的 DB 都是磁盘上的独立文件。
-- attach another database file to the current connection
ATTACH DATABASE 'archive.sqlite' AS archive;
DETACH DATABASE archive;
-- list attached databases (main + attached)
.databases
-- the in-memory database (never written to disk)
sqlite3 :memory:
-- temporary database (deleted when connection closes)
sqlite3 "" -- empty arg => temp DB
-- open read-only
sqlite3 "file:mydb.sqlite?mode=ro" --readonly
-- query the underlying page size / file format
PRAGMA page_size;
PRAGMA journal_mode;表头与输出格式
输出格式仅影响 CLI,不影响数据本身。配合 .headers on 的 'column' 模式对人类最易读。'list'(默认)最适合 shell 管道。'json' 和 'csv' 让 SQLite 成为方便的转换器。.output 将结果重定向到文件;记得用 .output stdout 切回。.mode box/table 绘制 ASCII 边框。
-- readable interactive mode
.headers on
.mode column
.width 15 20 10
SELECT id, name, email FROM users LIMIT 5;
-- pipe-separated (good for scripts)
.mode list
.separator "|"
SELECT id, name FROM users;
-- boxed table output (sqlite 3.36+)
.mode box
.mode table
-- JSON output for piping to other tools
.mode json
SELECT id, name FROM users;
-- write output to a file
.output users.txt
SELECT id, name FROM users;
.output stdout执行脚本与 SQL 文件
.read 在打开的 CLI 会话中执行 SQL 文件;用 < 重定向则会在交互提示符之前运行。--bail 在第一个错误时停止批处理(否则 SQLite 会继续执行,可能掩盖失败)。.parameter (3.44+) 从 shell 绑定命名参数,避免字符串拼接。开发时用 .echo on 检查脚本。
-- run a .sql file from the shell
sqlite3 mydb.sqlite ".read setup.sql"
-- or piped in
sqlite3 mydb.sqlite < setup.sql
-- inside the CLI
.read setup.sql
-- stop on first error (shell flag)
sqlite3 mydb.sqlite --bail < script.sql
-- echo commands as they run
.echo on
.read migrations.sql
-- show EXPLAIN before each statement (debug)
.explain on
-- parameterize from the shell (sqlite 3.44+)
sqlite3 mydb.sqlite \
-cmd ".parameter set @min 18" \
"SELECT name FROM users WHERE age >= @min"表与数据类型
创建表
INTEGER PRIMARY KEY 是 rowid 的别名——它自动递增且是最快的键。AUTOINCREMENT 改变算法使已删除的 ID 永不重用(稍慢;通常不必要)。DEFAULT (expr) 允许任意表达式如 datetime('now')。CREATE TABLE AS SELECT 创建预加载查询结果的表,但不复制约束或索引。
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
email TEXT NOT NULL UNIQUE,
age INTEGER CHECK (age >= 0),
role TEXT NOT NULL DEFAULT 'user',
created TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE TABLE IF NOT EXISTS orders (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
total REAL NOT NULL DEFAULT 0,
FOREIGN KEY (user_id) REFERENCES users(id)
ON DELETE CASCADE ON UPDATE CASCADE
);
-- quick table from a query
CREATE TABLE archive AS
SELECT * FROM orders WHERE created < '2024-01-01';存储类与类型亲和性
SQLite 使用动态类型:任何列都可以持有 5 种存储类之一。'类型亲和性'在可能时尝试转换值(如 INTEGER 列中的 '42' 变为整数 42),但从不因类型不匹配报错。STRICT 表 (3.37+) 恢复了传统的严格类型检查。typeof() 报告实际存储类。这种灵活性很强大但需要纪律——对数据完整性要求高的新 schema 请用 STRICT。
-- SQLite has 5 storage classes (not strict types):
-- NULL, INTEGER, REAL, TEXT, BLOB
CREATE TYPE demo... -- not supported
-- columns have 'type affinity', not enforced types
CREATE TABLE t (
a INTEGER, -- affinity INTEGER
b TEXT, -- affinity TEXT
c REAL, -- affinity REAL
d BLOB, -- affinity BLOB (no conversion)
e -- no affinity (accepts anything)
);
-- this WORKS despite 'INTEGER' column (dynamic typing)
INSERT INTO t (a) VALUES ('hello');
SELECT typeof(a) FROM t; -- 'text'
-- STRICT tables (SQLite 3.37+) actually enforce types
CREATE TABLE strict_t (a INTEGER, b TEXT) STRICT;
INSERT INTO strict_t VALUES ('hi', 1); -- error常用数据类型
SQLite 没有原生的 DATE/TIME 或 BOOLEAN 类型——将时间戳存储为 ISO-8601 TEXT ('YYYY-MM-DD HH:MM:SS') 以便排序,用日期/时间函数操作。布尔值是整数 0/1。VARCHAR(n) 长度限制被忽略(仅为兼容性)。NUMERIC 亲和性先尝试 INTEGER 再 REAL,尽可能保留精确小数。BLOB 原样存储原始字节。
-- integers
CREATE TABLE numerics (
small INTEGER, -- 1/2/4/8 bytes depending on value
big INTEGER, -- up to 64-bit
-- no separate BIGINT/SMALLINT, all map to INTEGER affinity
flag BOOLEAN -- stored as 0 (false) or 1 (true)
);
-- text & blobs
CREATE TABLE blobs (
name TEXT, -- variable-length UTF-8, no length limit
data BLOB, -- raw bytes, no conversion
note VARCHAR(255) -- length ignored, affinity TEXT
);
-- real numbers
CREATE TABLE floats (
price REAL, -- 8-byte IEEE float
precise NUMERIC(10,2) -- affinity NUMERIC, decimal preserved if exact
);
-- dates: SQLite has no native DATE type — store as TEXT (ISO-8601)
CREATE TABLE events (
ts TEXT -- 'YYYY-MM-DD HH:MM:SS' recommended
);修改表结构
SQLite 的 ALTER TABLE 对无服务器引擎而言是有意限制的。ADD COLUMN 是 O(1)(无需重写表)。RENAME COLUMN (3.25+) 和 DROP COLUMN (3.35+) 是现代便利功能。对于类型更改或添加约束,使用 12 步表重建模式(创建新表、复制、删除、重命名、重建索引)。重建期间需临时禁用外键——参见 PRAGMA legacy_alter_table。
-- add a column (fast, appends to end)
ALTER TABLE users ADD COLUMN bio TEXT;
-- rename a column (SQLite 3.25+)
ALTER TABLE users RENAME COLUMN name TO username;
-- rename a table
ALTER TABLE users RENAME TO accounts;
-- drop a column (SQLite 3.35+)
ALTER TABLE users DROP COLUMN deprecated_field;
-- SQLite CANNOT: change a column type, add constraints to
-- an existing column, or reorder columns. Workaround:
-- 1) create new table with desired schema
-- 2) INSERT INTO new SELECT * FROM old
-- 3) DROP old; ALTER TABLE new RENAME TO old
-- 4) recreate indexes/triggers约束
约束在引擎层面强制数据完整性。PRIMARY KEY 隐含 NOT NULL 和 UNIQUE。CHECK 可引用行中任意列。ON CONFLICT 子句(OR IGNORE / OR REPLACE / OR ABORT / OR FAIL / OR ROLLBACK)控制单语句冲突解决——OR REPLACE 删除冲突行后插入,会重置其 rowid。对于 UPSERT 语义,优先使用 ON CONFLICT ... DO UPDATE(见 CRUD 部分)。
CREATE TABLE products (
id INTEGER PRIMARY KEY,
sku TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
price REAL NOT NULL CHECK (price >= 0),
stock INTEGER NOT NULL DEFAULT 0 CHECK (stock >= 0),
category TEXT CHECK (category IN ('a','b','c')),
-- composite unique constraint
UNIQUE (name, category),
-- table-level check
CHECK (price * stock < 1000000)
);
-- name a constraint for clearer error messages
CREATE TABLE t (
x INTEGER CONSTRAINT x_positive CHECK (x > 0)
);
-- conflict handling
INSERT OR IGNORE INTO products (sku, name, price) VALUES (...);
INSERT OR REPLACE INTO products (...) VALUES (...);
UPDATE OR ABORT products SET stock = stock - 1 WHERE id = 1;CRUD 操作
INSERT
多行 INSERT 比循环逐行插入快得多——请批量插入。INSERT ... SELECT 在表间复制数据。last_insert_rowid() 返回当前连接上最近 INSERT 的 rowid(非事务)。对于应用,优先使用参数化插入(见集成部分)以避免 SQL 注入和引号问题。用 DEFAULT 显式取列的默认值。
-- single row
INSERT INTO users (username, email, age)
VALUES ('alice', '[email protected]', 30);
-- multiple rows in one statement (fast)
INSERT INTO users (username, email) VALUES
('bob', '[email protected]'),
('carol', '[email protected]'),
('dave', '[email protected]');
-- insert from a query
INSERT INTO archive_users
SELECT * FROM users WHERE active = 0;
-- explicit NULL / default
INSERT INTO users (username, email, age)
VALUES ('eve', '[email protected]', DEFAULT);
-- return the new rowid (handy in apps)
INSERT INTO users (username) VALUES ('frank');
SELECT last_insert_rowid(); -- returns the new idUPSERT (ON CONFLICT)
ON CONFLICT (UPSERT) 是 SQLite '存在则更新'的惯用法——比 INSERT OR REPLACE 安全得多,后者会删除现有行(触发 DELETE 触发器并重置 rowid)。'excluded' 指待插入的行。DO NOTHING 静默跳过。冲突目标可以是任何 UNIQUE 或 PRIMARY KEY 约束。这是计数器、最后访问更新和幂等导入的正确模式。
-- insert, but update on conflict
INSERT INTO users (id, username, email)
VALUES (1, 'alice', '[email protected]')
ON CONFLICT(id) DO UPDATE SET
email = excluded.email,
username = excluded.username;
-- ignore on conflict
INSERT INTO users (id, username)
VALUES (1, 'alice')
ON CONFLICT(id) DO NOTHING;
-- conflict on a specific unique constraint
INSERT INTO users (username, email)
VALUES ('alice', '[email protected]')
ON CONFLICT(email) DO UPDATE SET
username = excluded.username;UPDATE
除非确实要更新每一行,否则始终加 WHERE——SQLite 没有试运行模式。UPDATE ... FROM (3.33+) 和 UPDATE ... RETURNING (3.35+) 使 SQLite 接近 PostgreSQL 的体验。RETURNING 对需要更新后状态而无需单独 SELECT 的应用极为有用。CASE 支持单语句批量更新中的逐行逻辑,比逐行更新快得多。
-- basic update (ALWAYS use WHERE in production!)
UPDATE users SET age = 31 WHERE id = 1;
-- multiple columns
UPDATE users
SET age = age + 1, role = 'admin'
WHERE username = 'alice';
-- conditional update with CASE
UPDATE products
SET price = CASE
WHEN category = 'a' THEN price * 0.9
WHEN category = 'b' THEN price * 0.95
ELSE price
END;
-- update from another table (subquery)
UPDATE orders
SET total = (SELECT SUM(qty * price)
FROM items WHERE order_id = orders.id);
-- UPDATE ... RETURNING (SQLite 3.35+)
UPDATE users SET role = 'admin'
WHERE id IN (1,2,3)
RETURNING id, username, role;DELETE
SQLite 没有 TRUNCATE——DELETE FROM table 删除所有行但会触发行级触发器,且不重置 AUTOINCREMENT 计数器(存储在 sqlite_sequence 中)。DELETE ... RETURNING (3.35+) 记录被删除的内容。对于审计数据,优先软删除(deleted_at 列)而非硬删除。删除产生的空闲空间会被未来插入重用;VACUUM 将其归还给操作系统。
-- basic delete
DELETE FROM users WHERE id = 1;
-- delete with a subquery condition
DELETE FROM orders
WHERE user_id IN (SELECT id FROM users WHERE active = 0);
-- DELETE ... RETURNING
DELETE FROM sessions
WHERE expires < datetime('now')
RETURNING id, user_id;
-- delete ALL rows (fast, but fires triggers)
DELETE FROM logs;
-- TRUNCATE equivalent: delete all + reset rowid
DELETE FROM logs;
DELETE FROM sqlite_sequence WHERE name = 'logs';
-- soft delete pattern
UPDATE users SET deleted_at = datetime('now') WHERE id = 1;SELECT 基础
SELECT 是主力。LIMIT count OFFSET skip 实现分页(大偏移量时键集分页更快——见查询部分)。别名(AS,可选)提高可读性,且视图中计算列必须使用别名。DISTINCT 折叠相同行;考虑用 GROUP BY 更精细控制。CASE 为结果集添加 if/else 逻辑,可用于任何子句。
-- basic select
SELECT id, username, email FROM users;
-- limit and offset
SELECT id, username FROM users LIMIT 10 OFFSET 20;
SELECT id, username FROM users LIMIT 20, 10; -- offset, count
-- aliasing columns and tables
SELECT u.id AS user_id, u.username AS name
FROM users AS u;
-- expressions
SELECT
username,
age,
age * 365 AS days_alive,
UPPER(username) AS upper_name
FROM users;
-- distinct rows
SELECT DISTINCT category FROM products;
-- conditional output
SELECT name,
CASE WHEN age >= 18 THEN 'adult' ELSE 'minor' END AS status
FROM users;查询与过滤
WHERE 与运算符
NULL 在 SQL 中很特殊——它是'未知',不是值。= NULL 总是返回 NULL(被视为 false),所以用 IS NULL / IS NOT NULL。NULL 与 AND/OR 遵循三值逻辑。BETWEEN 是包含边界。IN 是 OR 等价的简写;避免 IN (NULL, ...) 因其行为古怪。多用括号使复合条件明确无歧义。
-- comparison operators
SELECT * FROM products WHERE price < 50;
SELECT * FROM products WHERE price BETWEEN 10 AND 100;
SELECT * FROM users WHERE age IN (18, 21, 25);
SELECT * FROM users WHERE age NOT IN (18, 21);
-- NULL handling (NEVER use = NULL)
SELECT * FROM users WHERE email IS NULL;
SELECT * FROM users WHERE email IS NOT NULL;
-- logical operators
SELECT * FROM users
WHERE age >= 18 AND age <= 65 AND role = 'admin';
SELECT * FROM users
WHERE role = 'admin' OR role = 'editor';
-- range with multiple conditions
SELECT * FROM orders
WHERE (status = 'shipped' AND total > 100)
OR (status = 'pending' AND total > 500);ORDER BY 与 LIMIT
ORDER BY 不带唯一决胜列时跨运行顺序不确定——始终加一个唯一列(如 id)作为最后排序键。OFFSET 分页是 O(n) 因为要扫描并丢弃行;键集分页 (WHERE id > last_seen_id) 是 O(limit),是大表的正确选择。RANDOM() 需要完整排序——仅对小结果集或采样子集使用。
-- ascending (default) / descending
SELECT * FROM users ORDER BY username;
SELECT * FROM users ORDER BY created DESC;
SELECT * FROM users ORDER BY age DESC, username ASC;
-- sort with NULLs first or last (SQLite 3.30+)
SELECT * FROM tasks ORDER BY due_date ASC NULLS FIRST;
SELECT * FROM tasks ORDER BY due_date DESC NULLS LAST;
-- pagination: keyset is faster than OFFSET
-- slow (offset scans & discards rows):
SELECT * FROM users ORDER BY id LIMIT 10 OFFSET 10000;
-- fast (keyset):
SELECT * FROM users WHERE id > 10000 ORDER BY id LIMIT 10;
-- random sample
SELECT * FROM users ORDER BY RANDOM() LIMIT 5;模式匹配
LIKE 默认对 ASCII 不区分大小写(一个令人惊讶的陷阱——用 COLLATE BINARY 区分大小写)。GLOB 区分大小写且支持字符类如 [A-Z]。REGEXP 不是内置的——必须注册函数或加载扩展。大规模全文搜索请用 FTS5(见性能部分)而非 LIKE '%term%',后者无法使用索引。
-- LIKE: case-insensitive by default for ASCII,
-- wildcards % (any chars) and _ (single char)
SELECT * FROM users WHERE username LIKE 'al%';
SELECT * FROM users WHERE email LIKE '%@gmail.com';
SELECT * FROM users WHERE username LIKE '_lice';
-- case-sensitive LIKE
SELECT * FROM users WHERE username LIKE 'AL%' COLLATE BINARY;
-- GLOB: Unix shell-style, case-SENSITIVE
-- wildcards * and ?, plus [abc] character classes
SELECT * FROM users WHERE username GLOB 'Al*';
SELECT * FROM users WHERE username GLOB '[A-D]*';
-- REGEXP (requires extension or custom function)
SELECT * FROM users WHERE username REGEXP '^a.*e$';
-- substring match
SELECT * FROM users WHERE INSTR(username, 'lic') > 0;CASE 与条件逻辑
CASE 是 SQL 的 if/else,可用于 SELECT、WHERE、ORDER BY、GROUP BY 和 HAVING。'简单'形式匹配值;'搜索'形式按顺序评估布尔条件。SUM(CASE WHEN ... THEN 1 ELSE 0 END) 是经典的透视/条件计数惯用法。CASE 在第一个匹配的 WHEN 处短路,所以顺序重要。省略 ELSE 时默认为 NULL。
-- simple CASE (value match)
SELECT name,
CASE role
WHEN 'admin' THEN 'Administrator'
WHEN 'editor' THEN 'Editor'
ELSE 'Regular User'
END AS role_label
FROM users;
-- searched CASE (conditions)
SELECT name,
CASE
WHEN age < 18 THEN 'minor'
WHEN age < 65 THEN 'adult'
ELSE 'senior'
END AS age_group
FROM users;
-- CASE in aggregate (conditional count)
SELECT
COUNT(*) AS total,
SUM(CASE WHEN active = 1 THEN 1 ELSE 0 END) AS active_count,
SUM(CASE WHEN active = 0 THEN 1 ELSE 0 END) AS inactive_count
FROM users;
-- CASE in ORDER BY (custom sort order)
SELECT * FROM products
ORDER BY CASE category
WHEN 'featured' THEN 0
WHEN 'new' THEN 1
ELSE 2
END;DISTINCT 与集合操作
UNION ALL 比 UNION 快,因为 UNION 执行去重排序——当不可能有重复或可接受重复时优先用 UNION ALL。所有集合操作要求兼容的列数和类型。SQLite 中 INTERSECT/EXCEPT 优先级高于 UNION/UNION ALL;用括号控制顺序。复合查询中的 ORDER BY 作用于整个结果集,而非最后一个 SELECT。