大家好,我是只谈技术不剪发的 Tony 老师。今天给大家带来一个关于 SQLite 嵌入式数据库的最新消息,SQLite 开发团队于 2020 年 8 月 14 日发布了 SQLite Release 3.33.0 版本。本文就来给大家分析一下这个版本中的一些新特性。
UPDATE FROM 语句是针对 SQL 标准的一个扩展,允许使用其他表中的数据更新目标表。通过 UPDATE FROM 语句可以将目标表和数据库中的其他表进行连接,确定需要更新的数据行以及更新后的字段值。
SQLite 中的 UPDATE FROM 语句使用 PostgreSQL语法。假设有一个销售应用程序,SALES 表存储了产品每天的累积销售。在每天销售结束后,需要根据每天的销售情况更新 INVENTORY 库存表;为此,可以基于每天的累积销量更新 INVENTORY 表中的库存数量。例如:
UPDATE inventory
SET quantity = quantity - daily.amt
FROM (SELECT sum(quantity) AS amt, itemId FROM sales GROUP BY 2) AS daily
WHERE inventory.itemId = daily.itemId;
FROM 子句中的子查询按照不同的 itemId 计算销售的数量,然后将它和 inventory 表进行连接并更新 inventory 表中的库存数量。
更新的目标表不在 FROM 子句中,除非是执行自连接查询。此时,FROM 子句中的表必须使用别名和目标表进行区分。
如果连接操作导致目标表中的同一数据行返回多行结果,只更新其中的随机一行结果。例如:
UPDATE department d
SET d.manager = e.emp_id
FROM (SELECT dept_id, emp_id FROM employee) AS e
WHERE d.dept_id = e.dept_id;
一个部门可能存在多名员工,子查询 e 中的每个部门编号对应多个 emp_id,最终部门的 manager 可能是该部门中的任意员工。
每个数据库由一个或多个数据页组成,一个数据库内的所有数据页大小都相同,但是不同的数据库可以使用不同的数据页大小,范围从 512 到 65536 字节。
从 SQLite 3.33.0 开始,一个数据库文件最多使用 4294967294 个数据页(由 max_page_count 编译指令决定),意味着最大支持约 2.8e+14 字节(281 TB)。
由于开发人员没有能够达到该限制的硬件,因此没有对这个上限进行测试。但是,测试验证了 SQLite 在数据库达到底层文件系统的最大文件大小(通常比该限制小得多)和磁盘空间耗尽而导致数据库无法增长时仍然能够正确且稳定地运行。
PRAGMA integrity_check 语句现在可以选择性地针对单个表及其索引进行完整性检查,而不仅仅是针对整个数据库文件。
PRAGMA schema.integrity_check;
PRAGMA schema.integrity_check(N)
PRAGMA schema.integrity_check(TABLENAME)
默认情况下针对整个数据库文件进行检查。如果指定了 TABLENAME 参数,则只会针对该表和相关索引进行检查,也就是“部分完整性检查”。
由于只检查了数据库的部分内容,可能无法检测出某些错误,例如文件中存在的未使用部分,或者两个或多个表重复使用文件中的相同部分。只有当 TABLENAME 参数为 sqlite_schema 或者它的别名时,部分完整性检查才会验证 空闲列表(freelist)。
新版本增加了 decimal 插件,支持任意精度的十进制算术运算。由于采用文本格式存储数字,因此可以保留任意精度,不需要进行近似运算。该插件包含了三个数学函数:
decimal_add(A,B)
decimal_sub(A,B)
decimal_mul(A,B)
它们分别用于计算参数相加、相减和相乘,返回一个字符串格式的结果。例如:
sqlite> select decimal_add(1, 0.23456789);
1.23456789
sqlite> select decimal_sub(1, 0.23456789);
0.76543211
sqlite> select decimal_mul(2, 0.23456789);
0.46913578
目前还没有提供除法运算函数。
函数 decimal_cmp(A,B) 可以用于比较两个数字值的大小,如果 A 小于、等于或者大于 B,分别返回一个负数、零或者正数。例如:
sqlite> select decimal_cmp(1, 2);
-1
sqlite> select decimal_cmp(2, 2);
0
sqlite> select decimal_cmp(3, 2);
1
函数 decimal_sum(X) 是一个类似于 sum() 的聚合函数,支持任意精度数字的计算。例如:
sqlite> WITH RECURSIVE generate_series(v) AS (
...> SELECT 1
...> UNION ALL
...> SELECT v+1.0/3 FROM generate_series
...> WHERE v+1.0/3<=10
...> )
...> SELECT decimal_sum(v)
...> FROM generate_series;
153.99999999999997
decimal 插件提供了“十进制”的排序规则,也就是按照数字顺序比较包含数字的文本字符串。
增强了 ieee754 插件,用于支持 IEEE 754 浮点数的 64 位二进制(binary64)格式和科学计数法( M × 2 E M×2^E M×2E )之间的转换:
F = M × 2 E F = M × 2^E F=M×2E
函数 ieee754(F) 接受一个浮点数的输入参数,返回一个如下所示的字符串:
'ieee754(M,E)'
其中,M 和 E 分别代表了浮点数的尾数和指数部分。例如:
sqlite> SELECT ieee754(47.49) AS x;
ieee754(6683623321994527,-47)
与此相反,函数 ieee754(M, E) 将一个使用尾数和指数表示的浮点数转换为十进制格式。例如:
sqlite> SELECT ieee754(6683623321994527,-47) as x;
47.49
函数 ieee754(F) 虽然方便阅读,但是不方便在表达式中使用。因此,增加了 ieee754_mantissa() 和 ieee754_exponent() 两个函数,分别用于返回浮点数的尾数和指数部分。例如:
sqlite> SELECT ieee754_mantissa(47.49) AS M, ieee754_exponent(47.49) AS E;
6683623321994527|-47
函数 ieee754_to_blob(F) 用于将浮点数转为为一个 8 字节 BLOB 数据,代表了该数字的 big-endian 模式的 binary64 编码。函数 ieee754_from_blob(B) 执行相反的转换。例如:
sqlite> SELECT quote(ieee754_to_blob(4.94065645841247e-324)) AS binary64;
X'0000000000000001'
sqlite> SELECT ieee754_from_blob(x'0000000000000001') AS F;
4.94065645841247e-324
SQLite 命令行工具(CLI)增加了 4 种输出模式:box、json、markdown 以及 table。例如:
sqlite> .mode box
sqlite> select 1 as val, 'sqlite' as str;
┌─────┬────────┐
│ val │ str │
├─────┼────────┤
│ 1 │ sqlite │
└─────┴────────┘
sqlite> .mode json
sqlite> select 1 as val, 'sqlite' as str;
[{"val":1,"str":"sqlite"}]
sqlite> .mode markdown
sqlite> select 1 as val, 'sqlite' as str;
| val | str |
|-----|--------|
| 1 | sqlite |
sqlite> .mode table
sqlite> select 1 as val, 'sqlite' as str;
+-----+--------+
| val | str |
+-----+--------+
| 1 | sqlite |
+-----+--------+
column(以及 box、markdown、table)输出模式自动将字段的显示长度扩展为输出数据行的最大长度,并且自动打开 .headers 设置。
quote 输出模式支持 .separator"。例如:
sqlite> .mode quote
sqlite> select 1 as val, 'sqlite' as str;
'val','str'
1,'sqlite'
sqlite> .separator ":"
sqlite> select 1 as val, 'sqlite' as str;
'val':'str'
1:'sqlite'
CLI 默认支持 decimal 插件和 ieee754 插件。
查询计划对于使用了 INDEXED BY 选项的 SQL 语句会显示 full-index-scan 信息,例如:
sqlite> create table t(id int, c1 int);
sqlite> create index idx_t_id on t(id);
sqlite> EXPLAIN QUERY PLAN select * from t indexed by idx_t_id;
QUERY PLAN
`--SCAN TABLE t USING INDEX idx_t_id
如果在之前的版本中执行以上命令,将会返回错误信息:
sqlite> EXPLAIN QUERY PLAN select * from t indexed by idx_t_id;
Error: no query solution
SQLite 可以更好地检测丢失的、不完整的和/或不可靠的 sqlite_stat1 统计数据,并且在存在错误信息能够生成良好的查询计划。
如果 t(x,y) 上存在索引,能够提高类似以下查询语句的性能:
SELECT min(x) FROM t WHERE y IN (?,?,?);
在预写式日志(WAL)模式下,如果写入器崩溃导致 shm 文件处于不一致状态,随后的事务可以恢复该 shm 文件,即使存在活动的读取事务。
在此之前,这种情况下恢复 shm 文件将会导致 SQLITE_PROTOCOL 错误。
了解了这么多新特性,哪个是你最期待的功能?赶快点击下载最新版使用吧!
如果觉得文章对你有用,或者想要学习更多数据库相关的知识,欢迎关注❤️、评论、点赞