SQLite不能正确存储小数

Qaz*_*zzi 2 vb.net sqlite datagridview

我有一个带有名为tbl_invent的表的sqlite DB,在窗体加载时,它用表中的内容填充了datagridview。问题是我有字段名称cost和sell_price,字段名称都有小数点,并且在加载表单时它仅显示数字而不是小数。

样品:

表格= 1.75,DGV = 1.00

   Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    connect()
    Dim da As New SQLiteDataAdapter("select * from tbl_Invent", connection)
    Dim ds As New DataSet
    da.Fill(ds, "tbl_Invent")
    DataGridView1.DataSource = ds
    DataGridView1.DataMember = "tbl_Invent"
    DataGridView1.Columns(6).ValueType = GetType(Single)
    DataGridView1.Columns(6).DefaultCellStyle.Format = "N2"
    DataGridView1.Columns(7).ValueType = GetType(Single)
    DataGridView1.Columns(7).DefaultCellStyle.Format = "N2"

    connection.Close()
    da.Dispose()
End Sub
Run Code Online (Sandbox Code Playgroud)

我已经检查了正确的字段类型“整数”,我也尝试了“ GetType(Single)”和“ GetType(Decimal)”,但仍然相同。谁能指出我正确的方向?谢谢。


来自评论:

SQLite中没有其他类型。在SQLite中也只有“文本”,“整数”,“实数”和“斑点”,它表示整数可以有小数。

Ňɏs*_*arp 5

您没有指明正在使用的数据库提供程序,但是标准提供程序(来自SQLite开发人员)将看到Integer数据并将其映射到Int32不允许小数的NET 类型。 Real将节省分数Decimal

there is no other type in SQLite. there is only "Text", "Integer", "Real" and "Blob"

是的,但这适用于SQLite数据库,而不适用于数据库提供程序。标准的DB Provider巧妙地编写为能够将4种基本类型转换为各种NET类型,从而使实际的存储类型/格式成为实现细节。


提供者代码包括执行转换的许多步骤,查找表,子系统,词典和方法。甚至还有一种定义自定义类型名称的方法。以下是工作原理的一般说明。

SQLite NET Provider识别的列类型名称

字节,SByte
INT8,INTEGER8,TINYSINT(SByte)UINT8,UNSIGNEDINTEGER8,TINYINT(字节)

整数(短,长,有符号,无符号等)
BIGINT,BIGUINT,COUNTER,IDENTITY,INT,INT16,INT32,INT64,INTEGER,INTEGER16,INTEGER32,INTEGER64,LONG,SMALLINT,SMALLUINT,UINT,UINT16,UINT32,UINT64,ULONG ,UNSIGNEDINTEGER,UNSIGNEDINTEGER16,UNSIGNEDINTEGER32,UNSIGNEDINTEGER64

布尔
BIT,BOOL,BOOLEAN,LOGICAL,YESNO

文字/字串
CHAR,CLOB,LONGCHAR,LONGTEXT,LONGVARCHAR,MEMO,NCHAR,NOTE,NTEXT,NVARCHAR,STRING,TEXT,VARCHAR,VARCHAR2

数值
DOUBLE,FLOAT,REAL;单(单)

十进制
货币,十进制,货币,数字,数字

BLOB
BINARY,BLOB,GENERAL,IMAGE,OLEOBJECT,RAW,VARBINARY

日期/时间
DATE,DATETIME,SMALLDATE,TIME,TIMESTAMP

GUID
GUID,唯一身份

来源:SQLiteDbTypeMapSQLiteConvert.cs(1.0.103版; 9月,2016)。

本质上,DBProvider 以适当的SQLite类型存储数据,但是在回它时,它将使用在表定义中使用的类型将数据转换回NET类型。SQLite提供程序包括一个大型SQLiteConvert类,可以为您完成所有转换。


尽管对于SQLite奉献者来说这似乎是常识,但我找不到狂野的记载。大多数站点只是重新格式化SQLite网站内容。它可能记录在帮助文件中,但是我的主题中没有内容。根据列表,很容易意外使用有效名称并发现其有效。

该列表合并了其他DB使用的最常见的符号,以及一些NET类型。例如,Boolean可以定义为BIT, BOOL, BOOLEAN, LOGICAL or YESNO。因此,此表定义是合法的并且具有完整功能:

CREATE TABLE LiteColTypes (
    Id        INTEGER     PRIMARY KEY AUTOINCREMENT,
    Name      TEXT,
    ItemDate  DATETIME,
    Char3     CHAR (3),
    UINT32    UINT32,
    Value     INT16,
    VarChar5  VARCHAR (5),
    GCode     GUID,
    Price     DECIMAL,
    ItemImg   IMAGE,
    Active    BOOL,
    NotActive YESNO
);
Run Code Online (Sandbox Code Playgroud)

有几件事要注意,还有一些有用的DateTime选项。

怎么运行的

该列表来自以下代码:

CREATE TABLE LiteColTypes (
    Id        INTEGER     PRIMARY KEY AUTOINCREMENT,
    Name      TEXT,
    ItemDate  DATETIME,
    Char3     CHAR (3),
    UINT32    UINT32,
    Value     INT16,
    VarChar5  VARCHAR (5),
    GCode     GUID,
    Price     DECIMAL,
    ItemImg   IMAGE,
    Active    BOOL,
    NotActive YESNO
);
Run Code Online (Sandbox Code Playgroud)

XML注释被保留,因为它具有启发性和权威性:

构建并返回包含此提供程序识别的数据库列类型的映射。(强调我的)。

DbType对过程至关重要。

读取数据

上面的代码SQLiteDbTypeMap将它识别出的许多许多列名称与一个DbType用于确定要返回的NET数据类型相关联。该列表足够全面,可以为您转换除1或2种类型之外的所有类型。

例如,请注意,GUIDIMAG*都存储BLOB,但GUID类型名称与不同的相关联DbType,其允许被比不同地返回BLOB IMAGEBLOB。

您也可以通过连接对象指定类型。空格和作用域不允许解释,但是虽然有点乏味,但它允许您为自定义类型名称提供数据类型。

储存资料

存储数据时,无需担心如何存储数据。DB Provider将使用DbType传递来查找要使用的SQLite类型(Affinity“。如果使用AddWithValue或(过时的)Add(object, object)重载,则DBProvider会猜测类型。在猜测上相当不错,但不要这么做。

因此,不需要此转换:

/// <summary>
/// Builds and returns a map containing the database column types
/// recognized by this provider.
/// </summary>
/// <returns>
/// A map containing the database column types recognized by this
/// provider.
/// </returns>
private static SQLiteDbTypeMap GetSQLiteDbTypeMap()
{
return new SQLiteDbTypeMap(new SQLiteDbTypeMapping[] {
    new SQLiteDbTypeMapping("BIGINT", DbType.Int64, false),
    new SQLiteDbTypeMapping("BINARY", DbType.Binary, false),
    new SQLiteDbTypeMapping("BIT", DbType.Boolean, true),
    new SQLiteDbTypeMapping("BLOB", DbType.Binary, true),
    new SQLiteDbTypeMapping("BOOL", DbType.Boolean, false),
    new SQLiteDbTypeMapping("BOOLEAN", DbType.Boolean, false),
    ...
    new SQLiteDbTypeMapping("GUID", DbType.Guid, false),
    new SQLiteDbTypeMapping("IMAGE", DbType.Binary, false)
    ... (many more)
Run Code Online (Sandbox Code Playgroud)

使用与任何其他数据库相同的代码:

cmd.Parameters.Add("@g", DbType.Binary).Value = myGuid.ToByteArray();
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 使用DbType来描述传递的数据,而不是您认为应如何保存(例如DbType.Guid,而不是Binary用于Guid)。提供者将执行大多数转化。
  • 有没有DbType.Image这样一个字节数组转换必要的。
  • Char()/VarChar()字段指定大小不会限制保存的字符数。这似乎是一个错误,因为保存的字符数超过定义的字符数可能会阻止加载该行。
  • 一个UInt16相反的方法:尝试传递超出范围的值,例如UInt16的-5,将导致Overflow Exception。但是它将65531为已存储的该值返回。
  • 大小/精度参数(例如Decimal(9,2)列)似乎无关紧要。内部表提供固定的精度和大小。
  • 对于日期,请传递日期并指示DbType.DateTime。永远都不需要传递特定格式的字符串。提供者知道事情。(请参阅下面的DateTime选项。)
  • 要仅保存日期,请仅传递日期:.Value = DateTime.Now.Date

两个不同的查询表用于保存和读取数据,它们的共同点是DbType这就是为什么它很重要。使用正确的数据可确保数据可以往返。避免使用AddWithValue

演示/结果

在此处输入图片说明 来自UI浏览器的数据视图

加载数据不需要任何特殊操作:

' // add trailing semicolons for c#
cmd.Parameters.Add("@n", DbType.String).Value = "Ziggy"
cmd.Parameters.Add("@dt", DbType.DateTime).Value = DateTime.Now 
cmd.Parameters.Add("@c3", DbType.StringFixedLength, 3).Value = "XYZ123" '// see notes
cmd.Parameters.Add("@u", DbType.UInt16).Value = 3
cmd.Parameters.Add("@g", DbType.Guid).Value = myGuid
    cmd.Parameters.Add("@p", DbType.Decimal).Value = 3.14D

'// 'ToByteArray()' is an extension method to convert
cmd.Parameters.Add("@img", DbType.Binary).Value = myImg.ToByteArray()
cmd.Parameters.Add("@act", DbType.Boolean).Value = True
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明
DataGridView中的相同数据

DGV可以正确识别并显示GUID,图像和布尔值列。每个的数据类型DataColumn都符合预期:

       Name --->    System.String (maxLen = 2147483647)  
   ItemDate --->  System.DateTime  
      Char3 --->    System.String (maxLen = 3)  
     UINT16 --->    System.UInt16  
   VarChar5 --->    System.String (maxLen = 5)  
      GCode --->      System.Guid  
      Price --->   System.Decimal  
    ItemImg --->    System.Byte[]  
     Active --->   System.Boolean  
  NotActive --->   System.Boolean  
Run Code Online (Sandbox Code Playgroud)

请注意,GuidImage项都存储为,BLOB但返回的方式不同。ActiveBOOL)和NotActiveYESNO)使用不同的类型名称,但返回相同的数据类型。一切都按需工作。

DateTime“问题”和选项

TIME作为列类型名称不能按预期工作。它不会解析DateTime.Now.TimeofDayTimespan)。该表将TIME映射到DbType.DateTime

请勿使用 DbType.DateTime2.DateTimeOffset。这些在转换器查找中丢失,因此数据以无效格式(版本1.0.103.0)存储为文本。

UTC,种类和标志

SQLite NET提供程序不只支持一种日期格式。当另存为UTC时,数据包括一个指示器。但是,无论是保存为本地或UTC时,Kind总是返回Unspecified。解决此问题的方法之一是添加datetimekind到您的连接字符串中:

`...;datetimekind=Utc;`
`...;datetimekind=Local;`   
Run Code Online (Sandbox Code Playgroud)

这将设置Kind为所有DateTime的值返回,但没有转化价值。

为补救是使用(相对)新的BindDateTimeWithKind连接标志。这将转换日期,以配合DateTimeKind在连接的保存

 // Dim SQL = "SELECT * FROM LiteColTypes"   ' for VB
 string SQL = "SELECT * FROM LiteColTypes";      
 ...
 dbCon.Open();
 Dim dt As New DataTable();
 dt.Load(cmd.ExecuteReader());
 dgv.DataSource = dt;
Run Code Online (Sandbox Code Playgroud)

尽管已传递本地日期,但BindDateTimeWithKind结果会保存为UTC以匹配连接。由于“ DateTimeKind = Utc;”而返回UTC日期。连接设置。

请注意,在保存日期时起作用,而DateTimeKind读取日期时BindDateTimeWithKind起作用。个别而言,它们似乎会使情况变得更糟。整个数据库一起成为基于UTC(或本地)的日期,日期统一保存并读取为同一日期-您无需执行任何操作。 Kind

ConnectionFlags 在连接字符串中指定它们会很麻烦,因为它们需要手动操作:

connx = "...;datetimekind=Utc;flags='Default, BindDateTimeWithKind';"
Run Code Online (Sandbox Code Playgroud)

限制/问题

均匀Kind处理对a DbDataReader和至少与Dapper一起有效。但是,使用时DataTableKindfor日期仍未指定。显然,这是由于Microsoft 的DateTimeMode特性,DataColumn并且可能是Microsoft的一项设计决定,即不假定列中的所有日期都将始终相同Kind。这也体现在其他数据库中。

使用UTC或本地连接时,提供程序将不指定未指定(这同样适用于查询中的日期)。因此,不应有任何不希望的额外转换:读取的UTC日期和“伪装为”未指定的UTC日期DataTable不会在更新中再次转换。

报价格式

与“传统智慧”日期相反,日期并不总是且仅保存为TEXT。为了节省一点空间,您可以保存刻度值。由于这些不能具有时区指示符,因此Kind相关选项可能非常有用。要启用Ticks,请使用DateTimeFormat连接字符串选项:

Private LiteConnStr = "...;datetimekind=Utc;DateTimeFormat=Ticks;..."
'e.g: 634939900800000000
Run Code Online (Sandbox Code Playgroud)

其他DateTimeFormat选项包括CurrentCultureISO8601(默认设置),JulianDayUnixEpoch。无需更改列类型名称即可使用这些格式之一。现在仍然是日期,SQLite Provider将根据连接标志处理实现细节。

UI浏览器

许多SQLite UI浏览器似乎只知道四种规范类型。也许这是故意的,但这限制了它们对NET开发人员的实用性,并隐藏了NET提供程序的功能。

SQLiteStudio(版本:3.1.0)提供了更多功能,但它似乎并不了解完整列表,因为缺少了一些非常有用的功能(例如GUID,IMAGE,SINGLE,整数变体)。

确实允许您输入所需的任何类型名称,因此,Profit!

摘要(tl; dr)

  • NET Provider通过支持各种类型的列名称来为SQLite添加功能,
  • 每个受支持的名称都与关联,该名称DBType确定实际的返回数据类型
  • DbType保存数据时使用正确的数据可确保数据往返
  • NET Provider将为您执行大多数转换
  • DateTimeKindBindDateTimeWithKind选项允许日期的自动,统一存储时区

首先,NET提供程序使实际存储成为实现细节