【问题标题】:SQLite showing exponential number instead of 0SQLite 显示指数数字而不是 0
【发布时间】:2020-12-27 04:59:23
【问题描述】:

数据

我有一个带有几列的 pandas DataFrame。其中一个是描述,存储为object,另一个是该描述的总量,存储为float64。类似这样:

   id    desc amount
12345 Item 1A   15.9
12345 Item 2N   -3.9
12345 Item 1A   2.99
12345 Item  3  -5.15
12345 Item  3  -9.84 

我使用 sqlalchemy 连接到数据库并使用 to_sql 导出 DataFrame:

con = sqlalchemy.create_engine("sqlite:///database.db")
df.to_sql(name = "table1", con = con, dtype = {"amount": sqlalchemy.Float()})

问题

在数据库中,我执行以下查询以获取支付给该 ID 的总金额:

SELECT id, SUM(amount) AS 'TOTAL PAID' FROM table1 GROUP BY ID;

数据库返回以下内容:

   id            TOTAL PAID
12345 -5.35510483179458e-10

正确的结果是:

   id TOTAL PAID
12345          0

我已经检查过了,数据库中的 amount 列确实有正确的金额。我认为这可能与使用的数据类型有关,但我不确定。结果为 0 的所有 ID 都会发生。在 SUM 函数中具有不同结果的 ID 可以正常显示。由于该列是指钱,我只需要它存储小数点后两位数。

Here你可以找到显示问题的记录之一。

问题(TL;DR)

为什么 SQLite 显示一个指数而不是 0 作为聚合函数的结果?我怎样才能得到预期的结果?

编辑

当使用 SUM 函数时,它不仅仅显示那个指数数。它显示了不同 ID 的其他几个指数数字。也有一些ID总和为0,但不受影响。受影响的人和未受影响的人没有什么不同。

【问题讨论】:

  • 这是因为你使用了花车。浮点数是近似值,而不是无限准确的奇迹。在网上搜索关于浮点数的近似值 (在所有使用浮点数的语言中都是如此,而不仅仅是 SQL),不要盲目地使用它们,对于货币值,您很可能应该使用小数,它们的可能值范围更有限,但它们是精确的,而不是近似值。 (为了清楚起见,你的结果是 -0.0000000005,这和该死的零一样好,除了使用浮点数引入的舍入误差。)
  • 谢谢,@MatBailie。
  • 简单地说,如果您想要精确的小数,请使用具有比例和精度的小数。如果您正在处理精度不如大小/性能重要的极大或极小数字,请使用浮点数。例如宇宙中有多少个原子;很好用的浮动。亚原子粒子之间的距离再次浮动。货币...使用小数。
  • 代码工作正常并返回0:dbfiddle.uk/…
  • @forpas 在这里查看:dbfiddle.uk/…

标签: sql database pandas sqlite


【解决方案1】:

来自SQLite documentation - Representation Of Numbers

SQLite4 不区分整数和浮点数

这意味着如果您在计算总和时使用任何浮点值,引擎将产生精度错误......这就是您的情况。在 SQLite 中没有办法解决这个问题,除非你强制所有值都是 INTEGER。不过,SQLite 引擎不会为您强制执行它。不幸的是,您需要切换到另一个引擎来解决此问题。

【讨论】:

  • 为什么不直接使用DECIMAL(9,2)
  • @MatBailie SQLite 中没有 DECIMAL 数据类型:sqlite.org/datatype3.html
  • @forpas - 我不使用 SQLite,所以我相信你。但是该文件确实在3.1.1. Affinity Name Examples 部分中提到了DECIMAL(10,5)NUMERIC 亲和力(它似乎确实在你的小提琴中工作:dbfiddle.uk/…
  • @MatBailie 完全正确。即使使用 ABC 之类的数据类型(允许),亲和性为 NUMERIC 且结果正确:dbfiddle.uk/…
  • @forpas 看看这个例子,这是给我错误的例子:dbfiddle.uk/…
【解决方案2】:

在 SQLite 中,像FLOAT64 这样的数据类型具有REAL 亲和性,正如3.1. Determination Of Column Affinity 中所解释的那样,因此可能会出现精度错误。
即使在 MySql 中,数据类型 REAL 也会发生同样的精度错误:https://www.db-fiddle.com/f/5Q5j3tnMnFieW9UHyMfFhK/0

在您的情况下,您需要像 DECIMAL(12,2) 这样的数据类型,但它在 SQLite 中不可用。

如果您想要一种解决方法,假设所有值在小数点后最多有 2 位数字,然后将每个值乘以 100 并将结果转换为 INTEGER 以提供更高的准确性。然后将这些值相加并除以100.0

SELECT id, 
       SUM(CAST((100 * amount) AS INTEGER)) / 100.0 AS [TOTAL PAID] 
FROM table1 
GROUP BY ID;

或者使用函数ROUND()

SELECT id, 
       ROUND(SUM(amount), 2) AS [TOTAL PAID] 
FROM table1 
GROUP BY ID;

请参阅demo
结果:

>    id | TOTAL PAID
> ----: | ---------:
> 12345 |          0

【讨论】:

  • 谢谢!正是我需要的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-24
  • 2018-11-18
  • 2018-10-12
  • 2016-07-16
相关资源
最近更新 更多