文章目录
SQLite04-表数据管理
1、表数据管理
(1)通用增删改查
(2)SQLite插入数据
- SQLite 主键为
INTEGER PRIMARY KEY时,插入NULL可自动生成唯一值。
(3)SQLite数据删除
- SQLite 不支持
TRUNCATE,可用DELETE FROM 表名。
(4)SQLite连接查询
| 类型 | 语法 | 是否支持 | 说明 |
|---|---|---|---|
| 内连接 (INNER JOIN) | JOIN / INNER JOIN |
✅ | 返回匹配记录 |
| 左外连接 (LEFT JOIN) | LEFT JOIN |
✅ | 左表全量 + 右表匹配 |
| 交叉连接 (CROSS JOIN) | CROSS JOIN |
✅ | 笛卡尔积 |
| 右外连接 (RIGHT JOIN) | - | ❌ | 需用 LEFT JOIN 模拟 |
| 全外连接 (FULL JOIN) | - | ❌ | 需用 UNION 模拟 |
2、系统表查看
(1)常用系统表
- sqlite_master:最重要的系统表,存储所有数据库对象的定义信息:表结构,索引,视图,触发器
sql
-- 查询用户表
SELECT name FROM sqlite_master WHERE type = 'table';
-- 查询索引
SELECT name, tbl_name, sql FROM sqlite_master WHERE type = 'index';
-- 查询视图
SELECT name FROM sqlite_master WHERE type = 'view';
-- 查询建表语句
SELECT sql FROM sqlite_master WHERE type='table' AND name='users';
- sqlite_temp_master:存储临时表信息
sql
SELECT name FROM sqlite_temp_master;
(2)系统表特性
- 只读属性:不能直接INSERT/UPDATE/DELETE sqlite_master表
- 自动维护:使用CREATE/DROP命令自动更新
plsql
-- 创建新表会自动在sqlite_master中添加记录
CREATE TABLE test(id INTEGER PRIMARY KEY, name TEXT);
-- 删除表会自动从sqlite_master中移除记录
DROP TABLE test;
- 隐藏性:
- 不能被重命名
- 不能被附加数据库访问(每个数据库有自己的sqlite_master)
- 不能在CREATE VIEW语句中引用
3、高级查询
(1)通用高级查询
(2)SQlite开窗函数
- 最低版本:SQLite 3.25.0(2018年9月)开始支持开窗函数,3.28.0 后完善了 GROUPS 和 RANGE 帧类型。低于此版本需升级。
- 调用位置限制:开窗函数只能出现在
SELECT列表和顶层ORDER BY中,不可用于WHERE、GROUP BY、HAVING。 - DISTINCT 禁用:窗口函数前不可加
DISTINCT。
(3)SQlite行转列
- SQLite的行转列不支持直接的PIVOT语法
(4)SQlite公共表表达式
- 递归CTE的深度:SQLite默认递归深度有限制,如果需要更深的递归,可以使用
OPTION (MAXRECURSION n)(但SQLite的实现可能略有不同) - SQLite支持在一个WITH子句中定义多个CTE:
sql
WITH
cte1 AS (SELECT * FROM table1 WHERE condition1),
cte2 AS (SELECT * FROM table2 WHERE condition2),
cte3 AS (SELECT * FROM cte1 JOIN cte2 ON cte1.id = cte2.id)
SELECT * FROM cte3;
(5)SQLite排除EXCEPT
- 大小写敏感:在SQLite中,EXCEPT操作符是大小写敏感的,列名的大小写必须匹配
- 不支持EXCEPT ALL:SQLite不支持
EXCEPT ALL(保留重复行),只能返回唯一结果
3、事务和锁
(1)通用事务和锁
(2)SQLite的事务控制命令
sql
BEGIN; -- 启动新事务
-- 执行数据库操作
COMMIT; -- 提交事务,永久保存修改
ROLLBACK; -- 回滚事务,撤销所有操作
(3)SQLite的事务隔离级别
- SQLite通过MVCC(Multi-Version Concurrency Control)实现事务隔离,支持以下隔离级别:
- 串行化(Serializable):提供最高级别的隔离,确保事务按顺序执行。
- 可重复读(Repeatable Read):SQLite通过MVCC实现,保证同一事务内的多次读取数据一致。
- 注意:SQLite不支持读未提交(READ UNCOMMITTED)隔离级别,因为它可能导致脏读、不可重复读和幻读等问题。
- MVCC机制与事务一致性:
- 事务读取数据时,SQLite返回事务ID小于或等于当前事务ID的数据版本
- 事务写入数据时,SQLite创建新页面并设置当前事务ID
- 保证同一事务内的多次读取结果一致
(4)SQLite的锁机制
- SQLite使用基于磁盘文件的锁定机制,支持五种不同的锁定级别,从低到高依次为:
| 锁状态 | 描述 | 允许并发 | 适用场景 |
|---|---|---|---|
| UNLOCKED | 数据库默认状态,无任何事务访问 | 所有 | 事务开始前 |
| SHARED | 用于读取数据,多个事务可同时持有 | 是 | 读操作 |
| RESERVED | 事务表示写入意图,但未实际写入 | 读操作允许,写操作禁止 | 准备写入 |
| PENDING | 事务等待获取排他锁,等待所有共享锁释放 | 读操作已存在,新读操作禁止 | 写入前准备 |
| EXCLUSIVE | 事务正在写入数据库,唯一持有 | 无 | 实际写入操作 |
- 每个级别的锁都可以阻止更高级别的锁,但不能阻止更低级别的锁。例如,共享锁可以阻止其他事务获取排他锁,但不能阻止其他事务获取共享锁。
- 锁状态转换:锁状态转换遵循以下路径:
plain
UNLOCKED → SHARED → RESERVED → PENDING → EXCLUSIVE → UNLOCKED
- 事务可以升级其锁定级别,但不能降级。例如,从共享锁可以升级到保留锁,但不能从保留锁降级到共享锁。
(5)SQLite的事务类型
- DEFERRED事务(默认)
- 不获取任何锁(直到需要时)
- 开始于UNLOCKED状态
- 第一次读操作获取SHARED锁
- 第一次写操作获取RESERVED锁
sql
BEGIN; -- 默认DEFERRED事务
- IMMEDIATE事务
- 尝试获取RESERVED锁
- 保证没有其他连接可以写数据库
- 其他连接可以读取数据库
- 但会阻止其他IMMEDIATE或EXCLUSIVE事务
- 提交时可能返回SQLITE_BUSY错误(如果有读事务未完成)
sql
BEGIN IMMEDIATE TRANSACTION;
- EXCLUSIVE事务
- 尝试获取EXCLUSIVE锁
- 保证没有其他连接可以访问数据库
- 适合需要独占访问的场景
sql
BEGIN EXCLUSIVE TRANSACTION;
(6)SQLite的锁机制工作流程
- 读操作流程
- 事务从UNLOCKED状态开始
- 执行读操作时获取SHARED锁
- 保持SHARED锁直到事务结束
- 写操作流程
- 事务从UNLOCKED状态开始
- 获取SHARED锁(如果需要读)
- 升级为RESERVED锁(表示写意图)
- 升级为PENDING锁(等待其他读锁释放)
- 最终获取EXCLUSIVE锁进行写入
- 提交后释放锁,回到UNLOCKED状态
(7)SQLite常见锁问题与解决方案
- 死锁问题
- 现象:两个连接同时尝试写数据库,导致死锁。
sql
-- 连接A
BEGIN;
INSERT INTO foo VALUES('x');
-- 试图获取RESERVED锁
-- 连接B
BEGIN;
INSERT INTO foo VALUES('y');
-- 拥有PENDING锁
- 原因:B先获取PENDING锁,阻止A获取RESERVED锁,导致A等待;同时A的共享锁阻止B获取EXCLUSIVE锁,形成死锁。
- 解决方案:
* 使用正确的事务类型:避免同时使用DEFERRED事务进行写操作
* 优先使用EXCLUSIVE事务进行写操作
* 确保写操作不会长时间持有锁
- database is locked
- 现象:当尝试写入数据库而数据库已被其他事务锁定时,会返回"database is locked"错误。
- 解决方法:
- 确保写操作使用正确的事务类型(如EXCLUSIVE)
- 避免长时间持有事务
- 优化应用程序逻辑,减少事务持有时间
(8)实际应用建议
- 默认使用DEFERRED事务:适用于大多数读多写少的场景
- 写操作使用IMMEDIATE或EXCLUSIVE:确保写入操作能及时获取所需锁
- 避免长时间持有事务:减少锁竞争,提高并发性能
- 合理使用保存点:在复杂事务中设置保存点,提高事务灵活性
sql
-- 使用保存点的示例
START TRANSACTION;
INSERT INTO accounts (id, balance) VALUES (1, 100);
SAVEPOINT sp1;
INSERT INTO accounts (id, balance) VALUES (2, 200);
ROLLBACK TO sp1; -- 仅回滚第二个操作
COMMIT;