我在.Net中对Sqlite没有太多经验,但我看到的行为相当奇怪。我们假设我们有一个.Net核心应用程序,其中包含以下project.json
:
{
"version": "1.0.0-*",
"buildOptions": {
"debugType": "portable",
"emitEntryPoint": true
},
"dependencies": {
"Microsoft.Data.Sqlite": "1.0.0",
"Dapper": "1.50.2"
},
"frameworks": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": {
"type": "platform",
"version": "1.0.0"
}
},
"imports": "dnxcore50"
}
}
}
我们还有一个简单的课程Item
:
public class Item
{
public Item() { }
public Item(int id, string name, decimal price)
{
this.Id = id;
this.Name = name;
this.Price = price;
}
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
然后我创建一个内存数据库并用数据填充它(使用Dapper):
var connection = new SqliteConnection("Data Source=:memory:");
connection.Open();
connection.Execute("CREATE TABLE IF NOT EXISTS Items(Id INT, Name NVARCHAR(50), Price DECIMAL)");
var items = new List<Item>
{
new Item(1, "Apple", 3m),
new Item(2, "Banana", 1.4m)
};
connection.Execute("INSERT INTO Items(Id, Name, Price) VALUES (@Id, @Name, @Price)", items);
然后我尝试从Items
表中读取:
var dbItems = connection.Query<Item>("SELECT Id, Name, Price FROM Items").ToList();
当我运行解决方案时,我得到以下异常:
未处理的异常:System.InvalidOperationException:解析时出错 第2栏(价格= 1.4 - 双倍)---&gt; System.Invali dCastException: 无法投射“System.Double”类型的对象。输入&#39; System.Int64&#39;。
好的,然后我尝试使用Microsoft.Data.Sqlite
来获取数据:
var command = connection.CreateCommand();
command.CommandText = "SELECT Price FROM Items";
var reader = command.ExecuteReader();
while (reader.Read())
{
Console.WriteLine(reader[0].GetType());
}
结果我得到了:
System.Int64 // Price = 3
System.Double // Price = 1.4
我尝试使用十进制价格在真实数据库上运行查询,返回的数据类型是正确的,并且始终是十进制的(如预期的那样)。
我应该进一步挖掘什么方向?我的内存数据库有问题吗?如何使其与小数一致?
答案 0 :(得分:4)
在SQLite中,如果您创建一个具有数字/十进制列数据类型的表,它与数字类型相关性相关联,并且其默认行为是在值没有小数时存储整数,或者如果值包含小数,则存储实数。这有助于节省字节存储,因为最常用的整数值(小到中等应用程序的值不到一百万)需要的字节数比实际数据类型少:
整数(约):
针对8字节的实际IEEE数据类型
如果你需要总是存储一个实数,就必须声明一个float / real数据类型,以便被认为是真正的类型亲和关系,因此即使你向SQLite发送一个整数,它也会被存储为真实的
来源:https://sqlite.org/datatype3.html
我知道真实可能是不精确的,但我在wal模式下使用C#Decimal数据类型和SQLite与磁盘中的数据库进行了一些测试,如果所有操作都是在C#中进行的,我从未遇到过舍入错误。但是当我直接在SQLite中进行一些计算时,由于2个整数除法,我得到了一些舍入误差和一些不好的计算
我使用EF6和Dapper读取/写入数值(来自SQLite的整数/实数)并且从未出错。
现在,我计划利用SQLite数字行为来使用dapper TypeHandler来节省磁盘空间,因此我可以复制SQL Server Money
实现(在内部,SQL Server保存一个8字节的整数,它除以10000加载到内存时):
public class NumericTypeHandler : SqlMapper.TypeHandler<decimal>
{
public override void SetValue(IDbDataParameter parameter, decimal value)
{
parameter.Value = (long)(value / 10000);
}
public override decimal Parse(object value)
{
return ((decimal)value)/10000M;
}
}
此外,我设置了datetime UnixEpoch实现以节省磁盘空间并提高性能
注意:
SQLite可以在低需求的asp.net应用程序中处理数十个用户,诀窍是使用pragma指令和其他东西调整SQLite:
我也在C#方面做了一些改进:
避免保存不必要的数据(错误日志,电子邮件html文本)
如果您计划使用SQLite作为后端,我认为您应该使用像EFx或Dapper这样的ORM。使用dapper,您需要创建自己的微框架以节省开发时间(基本CRUD功能和Linq查询转换)。
我找到的最好的工具是https://github.com/linq2db/linq2db,你使用linq以非常好的速度和linq的容易性访问数据库。并且可以制作CRUD功能。
如果这个答案有帮助,我会很高兴
问候
答案 1 :(得分:3)
这是SQLite的一项功能,请参阅此链接:https://sqlite.org/faq.html#q3