如何将Python十进制转换为SQLite数字?
我有一个程序,可以读取JSON格式的财务数据并将其插入SQLite数据库。问题是当我将它插入SQLite数值列时,它似乎不喜欢这个对象 我发现了这个问题,但答案已经过时,据我所知,SQLite现在有了一个货币数据类型如何将Python十进制转换为SQLite数字?,python,sqlite,decimal,numeric,Python,Sqlite,Decimal,Numeric,我有一个程序,可以读取JSON格式的财务数据并将其插入SQLite数据库。问题是当我将它插入SQLite数值列时,它似乎不喜欢这个对象 我发现了这个问题,但答案已经过时,据我所知,SQLite现在有了一个货币数据类型 现在作为一种解决方法,我将十进制值存储为文本,但是否可以将其存储为数字?对于数据库插入和财务计算,我是否被小数转换为字符串,反之亦然的开销所困扰?至少你链接到的页面没有提到货币数据类型,并且只打开了数值关联 如果它是我的数据,我可能会存储一个整数的货币“单位”——便士,或十分之一便
现在作为一种解决方法,我将十进制值存储为文本,但是否可以将其存储为数字?对于数据库插入和财务计算,我是否被小数转换为字符串,反之亦然的开销所困扰?至少你链接到的页面没有提到
货币
数据类型,并且只打开了数值
关联
如果它是我的数据,我可能会存储一个整数的货币“单位”——便士,或十分之一便士,或百分之一便士,任何合适的——然后使用比例因子将整数输入转换为十进制,相当于从数据库读取数据时的计算。处理整数更难。
sqlite3
允许您注册适配器(插入时透明地将小数
转换为文本
)和转换器(提取时透明地将文本
转换为小数
)
以下是示例代码的一个稍加修改的版本:
屈服
4.12
<class 'decimal.Decimal'>
4.12
我发现我不得不对unutbu的方法做一点小小的调整。对于一个值为“4.00”的修改示例,它从数据库返回为“4”。我处理的是商品,不想将精度硬编码到数据库中(就像我只需乘以和除以100所做的那样)。因此,我对转换函数进行了如下调整:
def adapt_decimal(d):
return '#'+str(d)
def convert_decimal(s):
return D(s[1:])
这在美学上不是很好,但确实挫败了sqlite将字段存储为整数的渴望,并失去了对精度的跟踪。
- 如果列的声明类型包含任何字符串 “CHAR”、“CLOB”或“TEXT”则该列具有文本相关性。通知 类型VARCHAR包含字符串“CHAR”,因此被赋值 文本亲和力
不知道这是否是一个好的处理方法-欢迎评论我基本上遵循与其他人相同的方法,但添加了一项。问题在于,定义适配器和转换器会处理逐行访问十进制列的情况。但是,当您发出聚合函数时,例如对所有行的十进制列求和,则使用实数(即浮点算术)进行求和,返回的结果将是浮点。在下面的示例中,我知道我存储的十进制值有两位小数(代表美国货币),因此我希望最终答案返回到两位小数。我可以在检索到浮点和之后执行此操作。但这很简单:
小数2
类型指定额外的转换器sqlite3.PARSE\u COLNAMES
12010.000000000178
12010.00
120100000.00
3889.54
3889.54
重要提示
这个解决方案并不完美。如果您要聚合足够多的行,则浮点近似值可能会累积,因此在将总和转换回十进制时,四舍五入到两位不足以得到正确的答案。例如,如果我们插入并汇总了10000000行:
for _ in range(10000000):
cursor.execute("insert into test(amount) values(?)", ("12.01",))
conn.commit()
那么,我们本来可以得到的总数将是120100000.02
,而不是希望得到的120100000.01
。因此,为了绝对确保将正确的结果聚合到正确的位置数,我们必须分别读取和求和每一行:
cursor.execute("select amount from test")
sum = Decimal('0.00')
for row in iter(cursor.fetchone, None):
sum += row[0]
print(sum)
印刷品:
12010.000000000178
12010.00
120100000.00
3889.54
3889.54
但在许多情况下,我所概述的方法应该是足够的,并且肯定是对完全省略舍入的改进
比如说。AVG
函数不太可能出现与SUM
函数相同程度的舍入问题,其值可能是您无论如何都想要舍入的值。在下面的示例中,我们在.01
和7777.77
之间插入10000000个随机值,首先使用AVG
聚合函数计算四舍五入到两位的平均值,然后将各行相加,除以行数,并将此商四舍五入到两位:
for _ in range(10000000):
cursor.execute("insert into test(amount) values(?)", (Decimal(randint(1, 777777)) / 100,))
conn.commit()
cursor.execute("select avg(amount) as `amount [decimal2]` from test")
row = cursor.fetchone()
print(row[0])
cursor.execute("select amount from test")
sum = Decimal('0.00')
cnt = 0
for row in iter(cursor.fetchone, None):
sum += row[0]
cnt += 1
print((sum / cnt).quantize(Decimal('.01'), rounding=ROUND_HALF_UP))
印刷品:
12010.000000000178
12010.00
120100000.00
3889.54
3889.54
使用您自己的数据类型并使用BLOB类型?这不是在文章中提到的吗?@William,是的,这远不是一个新想法。:)只需确保为应用程序设置足够大的缩放参数<代码>2对于小型企业来说可能足够了,但您可能需要
3
、4
、或5
或更多,具体取决于货币换算或利率计算等。此解决方案不再有效。根据sqlite3文档,转换器函数总是以bytes
格式调用,因此为了使代码在Python 3.8中工作,必须在convert\u decimal
函数的定义上调用D(s.decode('utf-8'))
。