数据不准确,数据丢失,SQLite怎么保证计算不丢数--SQLite 五脏俱全系列 (5)

开头还是介绍一下群,如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, OceanBase, Sql Server等有问题,有需求都可以加群群内有各大数据库行业大咖,可以解决你的问题。加群请联系 liuaustin3 ,(共3400人左右 1 + 2 + 3 + 4 +5 + 6 + 7 + 8 +9)(1 2 3 4 5 6 7 8群已经爆满 9群 300+,开10群PolarDB专业学习群110+)

SQLite 也可以做复杂的查询,我不信--SQLite 五脏俱全系列 (4)

SQLite 备份我不会,Down机了怎么办 SQLite 五脏俱全系列 (3)

什么int类型里面能插入文字,还不能改字段类型--SQLite 五脏俱全系列 (2)

SQLite需要初始化参数,怎么调优-- SQLite 五脏俱全系列 (1)

SQLite 开发中的数据库开发规范 --如何提升业务系统性能避免基础BUG

基于SQLite如何设计应用程序,拆散,散,还的散!

SQLite3 如果突发断电,关机,数据会丢还是不会丢?

今天的内容SQLite是一个网友的问题,如何控制数值计算的问题

Sqlite金额精度计算怎么弄

SQLite中处理数据精度时,是没有其他数据库的Decimal和Numeric类型的数值的类型来保证数据计算的准确性,这里SQLite只有 REAL 一个数据类型浮点数。

这里我们主要针对三个部分进行显示

1 数据的展示

2 数据的计算

3 数据的存储

go 复制代码
SQLite version 3.46.0 2024-04-15 20:43:21
Enter ".help"for usage hints.
sqlite> CREATE TABLE ShippingLogs (
    id INTEGER PRIMARY KEY,
    route_name TEXT,
    distance_km REAL,  -- 运输距离
    unit_price REAL    -- 每公里单价
);

INSERT INTO ShippingLogs (route_name, distance_km, unit_price) VALUES 
('北京-上海', 1214.8, 5.5),
('上海-杭州', 175.234, 4.25),
('广州-深圳', 138.0, 6.0),
('跨国物流', 12500.5567, 12.888);
sqlite> 
sqlite> 
sqlite> select * from shippinglogs;
1|北京-上海|1214.8|5.5
2|上海-杭州|175.234|4.25
3|广州-深圳|138.0|6.0
4|跨国物流|12500.5567|12.888
sqlite>
go 复制代码
SELECT 
    route_name AS 线路,
    -- 展现 3 位精度的距离
    format("%.3f KM", distance_km) AS 格式化里程,
    -- 展现 2 位精度的单价
    format("%.2f", unit_price) AS 单价,
    -- 计算总价并进行展示封装
    format("¥%.2f", ROUND(distance_km * unit_price, 2)) AS 总运费
FROM ShippingLogs;
go 复制代码
sqlite> SELECT 
    route_name AS 线路,  
    format('%.3f KM', distance_km) AS 格式化里程,  
    format('%.2f', unit_price) AS 单价,   
    format('¥%.2f', ROUND(distance_km * unit_price, 2)) AS 总运费
FROM ShippingLogs;   

北京-上海|1214.800 KM|5.50|¥6681.40
上海-杭州|175.234 KM|4.25|¥744.74
广州-深圳|138.000 KM|6.00|¥828.00
跨国物流|12500.557 KM|12.89|¥161107.17
go 复制代码
sqlite> INSERT INTO TestPrecision VALUES (1.234567890123456789);
sqlite> 
sqlite> 
sqlite> SELECT 
    val AS 原始输入,
    format('%.20f', val) AS 存储后的底层值,
    format('%.20f', (val * 10.0) / 10.0) AS 运算后的值
FROM TestPrecision;   ...>    ...>    ...>    ...> 
1.23456789012346|1.23456789012345700000|1.23456789012345700000
sqlite>

上面我们展示怎么显示和输入数字,但是我们文中提到了SQLite只是提供了REAL类型的IEEE 754双精度浮点存储数据值,会产生浮点的误差,如1.234567890123456789被存储为 1.23456789012345700000。

所以,我们在明知SQLite不能处理高精度的数值计算的情况下,就要使用程序外部计算的方式来进行。

这里有两种方式我们来讲已将

1 外部程序计算法,下面我们以python为例

go 复制代码
from decimal import Decimal, getcontext

# 设置全局精度(可选)
getcontext().prec = 28

# 从数据库获取数据
rows = cursor.execute("""
    SELECT route_name, distance_km, unit_price FROM ShippingLogs
""").fetchall()

results = []
for row in rows:
    name, distance, price = row
    # 转换为 Decimal 进行计算
    # 注意:从数据库取出的 float 需要先转为字符串,以避免引入浮点误差
    total_cost = Decimal(str(distance)) * Decimal(str(price))
    # 格式化最终结果
    formatted_cost = f"¥{total_cost:.2f}"
    results.append((name, formatted_cost))

# 现在 results 列表里的数据是精确的

JAVA 程序同样的方式处理,这样处理的好处是,程序是可以随意定制的,程序可以支持各种精度的数字的计算,整个业务逻辑都在同一个语言中处理,方便进行维护和调试。

这样操作就和SQLite没有关系了。

计算的问题在程序中解决了,剩下的就是数据存储的问题,这里我们掌握两个核心

1 精确计算后的数值,不要存储成REAL类型,要存储成TEXT类型也将数值存储成文本

2 不要一股脑的将数据存储到一个数值字段中,或者一个文字字段中,而是要存储成两个字段,我们举例。

go 复制代码
sqlite> 
sqlite> CREATE TABLE ShippingLogs_Split (
    id INTEGER PRIMARY KEY,
    route_name TEXT,
    distance_km REAL,             -- 距离仍可用 REAL
    unit_price_int INTEGER,       -- 单价整数部分
    unit_price_frac INTEGER,      -- 单价小数部分(2(x1...> (x1...> (x1...> (x1...> (x1...> 位)
    total_price_int INTEGER,      -- 总价整数部分
    total_price_frac INTEGER       -- 总价小数部分(2位)
);(x1...> (x1...> (x1...> 
sqlite> 
sqlite> 
sqlite> INSERT INTO ShippingLogs_Split (
    route_name,
    distance_km,
    unit_price_int,
    unit_price_frac,
    total_price_int,
    total_price_frac
) VALUES
('北京-上海', 1214.8, 5, 50, 6681, 40),
('上海-杭州', 175.234, 4, 25, 744, 74),
('广州-䧸1...> (x1...> (x1...> (x1...> (x1...> (x1...> (x1...>    ...>    ...>    ...> ·±圳', 138.0, 6, 0, 828, 0),
('跨国物流', 12500.5567, 12, 89, 161107, 17);   ...> 
sqlite> 
sqlite> select * from shippinglogs_split;
1|北京-上海|1214.8|5|50|6681|40
2|上海-杭州|175.234|4|25|744|74
3|广州-深圳|138.0|6|0|828|0
4|跨国物流|12500.5567|12|89|161107|17
sqlite>

数值输入后,我们展示的时候可以这样做

go 复制代码
sqlite> 
sqlite> 
sqlite> SELECT
    route_name,
    format('%d.%02d', total_price_int, total_price_frac) AS total_price
FROM ShippingLogs_Split;   ...>    ...>    ...> 
北京-上海|6681.40
上海-杭州|744.74
广州-深圳|828.00
跨国物流|161107.17
sqlite>

最后我们总结一下,SQLite使用中,不支持精确的浮点计算,和数字的存储,作为SQLite的使用者,我们要改变原有的使用方式

在程序中计算,将整数和小数分开存储,并存储成文本防止存储的时候丢失精度。

相关推荐
滑稽之神眷顾者1 小时前
基于正倒排索引的文档搜索引擎测试报告
java·开发语言·功能测试
橙子圆1231 小时前
Redis知识5之持久化
数据库·redis·缓存
霸道流氓气质1 小时前
Spring AI ChatMemory 对话记忆配置指南:概念、实战与常见问题
java·人工智能·spring
jiayong231 小时前
Python面试题集 - 数据结构与算法
开发语言·python
十六年开源服务商1 小时前
外贸WordPress用户调查与满意度调查实战指南2026
大数据·数据库·人工智能
cui_ruicheng1 小时前
Linux线程(四):线程池、日志系统与单例模式
linux·开发语言·单例模式
24白菜头1 小时前
MySQL学习笔记
数据库·笔记·学习·mysql
伊甸31 小时前
Neo4j 常用语法速查(Cypher)
java·数据库·neo4j
小程故事多_801 小时前
深度解析Claude Code,AI编码助手的底层架构与工作原理
java·人工智能·架构·智能体