使用适配器时,sqlite3中十进制和货币的问题
我在用sqlite3作为数据库,想保存几个小数值,一个是百分比,另一个是美元金额。不过,我发现当我对超过20,000条记录使用sum(amount)时,结果偏差了好几美元。
我想到了用一个适配器来保存分(也就是美元的百分之一),这样聚合计算应该就能正常工作了。
sqlite3.register_adapter(decimal.Decimal, lambda x:str(x))
sqlite3.register_adapter(decimal.Decimal, lambda x:int(x*100))
不过,这样我就需要两个类,因为我不能用同一个类。尝试去继承Decimal类时遇到了问题,因为它内部又使用了Decimal。没关系,我决定直接复制decimal.py文件,把里面所有的Decimal和decimal都换成Money和money。
$ copy decimal.py money.py $ sed -e "s/Decimal/Money/g" -e "s/decimal/money/g" -i money.py $ money.py
所有的单元测试都通过了。我现在试了一下,却出现了“可能不支持的类型”的错误。我调整了一下转换器,使用了一些基本类型,但就是无法正常工作,我也没有更好的解决办法。
我需要一些帮助,下面有个示例。有没有什么想法可以让它正常工作,或者用标准库有更好的解决方案?
import decimal
import money
import sqlite3
sqlite3.register_adapter(decimal.Decimal, lambda x:str(x))
sqlite3.register_converter('decimal', decimal.Decimal)
sqlite3.register_adapter(money.Money, lambda x: int(value*100))
sqlite3.register_converter('intcents', lambda x: money.Money(x)/100)
conn = sqlite3.connect(":memory:",
detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
cursor = conn.cursor()
cursor.executescript("CREATE TABLE example(rate decimal, amount intcents)")
for x in (1,1,1,2,2,2,3,3,3):
rate = decimal.Decimal(str(x))/100
amount = money.Money(str(rate))
try:
cursor.execute("INSERT INTO example VALUES(?,?)", (rate, amount))
except:
print (rate, amount)
raise
cursor.execute("""SELECT sum(rate), sum(amount) FROM example""")
print cursor.fetchone()
cursor.execute("""SELECT sum(rate) as "t [decimal]", sum(amount)as "a [intcents]"FROM example""")
print cursor.fetchone()
2 个回答
2
这里有两个简单的解决办法,希望有人能提供更直接的方法:
1. 把数据转换成字符串,然后把这个字符串存到数据库里。
2. 把钱的数值乘以100,然后以整数的形式存储。
无论哪种方法,当你从数据库读取这些值时,都需要再把它们转换回小数格式。
0
问题出在register_adapter这个地方。奇怪的是,它并没有让我遇到NameError(名称错误)。另外一种解决方案需要在每次输入和输出时都进行处理,这样就失去了使用register_adapter和register_converter的意义。按照评论里的语法修正后,它运行得相当不错。