SQLite没有正确存储小数

时间:2017-06-01 04:42:05

标签: vb.net sqlite datagridview

我有一个带有一个名为tbl_invent的表的sqlite数据库,在表单加载时,它使用表中的内容填充datagridview。问题是我有字段名称成本和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

我已经检查了字段类型它是否正确“整数”,我也尝试了“GetType(单一)”和“GetType(十进制)”但仍然相同。任何人都可以指出我正确的方向?谢谢。

来自评论:

SQLite中没有其他类型。在SQLite中只有“Text”,“Integer”,“Real”和“Blob”,它表示整数可以有小数。

1 个答案:

答案 0 :(得分:4)

您没有指明您使用的是哪个数据库提供程序,但标准提供程序(来自SQLite开发人员)会看到Integer并将数据映射到NET Int32类型,而不是允许小数。 Real会像Decimal那样保存分数。

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

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

提供者代码包括许多步骤,查找表,子系统,字典和执行转换的方法。甚至有一种方法可以定义自定义类型名称。以下是对工作的通用解释。

SQLite NET提供程序

识别的列类型名称

Byte,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;单(单)

<强>十进制
CURRENCY,DECIMAL,MONEY,NUMBER,NUMERIC

<强> BLOB
BINARY,BLOB,GENERAL,IMAGE,OLEOBJECT,RAW,VARBINARY

<强>日期/时间
DATE,DATETIME,SMALLDATE,TIME,TIMESTAMP

<强> GUID
GUID,UNIQUEIDENTIFIER

来源:SQLiteDbTypeMap SQLiteConvert.cs(版本1.0.103; 2016年9月)。

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

我无法在野外发现这一点,尽管它似乎是SQLite爱好者的常识。大多数网站只是重新格式化SQLite网站内容。它可能会记录在帮助文件中,但我的主题没有内容。根据列表,很容易意外地使用有效名称并发现它有效。

该列表包含其他数据库使用的最常见符号,以及一些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
);

有几点需要注意,有些DateTime选项很有用。

如何运作

该代码来自此代码:

/// <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)

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

  

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

DbType对此过程至关重要。

阅读数据

上面的SQLiteDbTypeMap将它识别的许多列名称与DbType相关联,后者用于确定要返回的.NET数据类型。该列表非常全面,可以为您转换除1或2之外的所有类型。

例如,请注意GUIDIMAG *都存储BLOB,但GUID类型名称与不同的DbType允许 BLOB以不同于IMAGE BLOB的方式返回。

您还可以通过连接对象指定类型。空间和范围不允许解释,但有点单调乏味,它允许您提供自定义类型名称的数据类型。

存储数据

存储数据时,无需担心应该如何存储数据。数据库提供程序将使用传递的DbType来查找要使用的SQLite类型(Affinity&#34;)。如果您使用AddWithValue或(过时的)Add(object, object)重载,则DBProvider将猜测该类型。它很擅长猜测,但不要这样做。

因此,不需要进行此转换:

cmd.Parameters.Add("@g", DbType.Binary).Value = myGuid.ToByteArray();

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

' // 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

注意:

  • 使用描述传递数据的DbType,而不是您认为 的保存方式(例如DbType.Guid,而不是Binary {{1} }})。提供商将执行大多数转化。
  • 没有Guid所以需要字节数组转换
  • 指定DbType.Image字段的大小不会限制保存的字符数。这似乎是一个错误,因为保存的字符多于定义的字符可以防止加载行。
  • Char()/VarChar()反向运行:尝试传递超出范围的值(例如,对于UInt16为-5)将导致UInt16。但它会返回Overflow Exception这样一个已存储的值。
  • 列的大小/精度参数(例如65531)似乎并不重要。内部表格提供固定的精度和尺寸。
  • 对于日期,请传递日期并指明Decimal(9,2)。无需传递特定格式的字符串。提供者知道事情。 (参见下面的日期时间选项。)
  • 要仅保存日期,请仅传递日期:DbType.DateTime

两个不同的查找表用于保存与读取数据,它们的共同点是.Value = DateTime.Now.Date,这就是重要的原因。使用正确的数据可确保数据可以进行往返。避免使用DbType

演示/结果

enter image description here 来自UI浏览器的数据视图

加载数据没有什么特别之处:

AddWithValue

enter image description here
DataGridView中的相同数据

DGV正确识别并显示GUID,图像和布尔列。每个 // 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; 的数据类型都符合预期:

DataColumn

请注意, Guid 图像项目均存储为 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 ,但返回方式不同。 活动BLOB)和 NotActive BOOL)使用了不同的类型名称,但返回相同的数据类型。一切都按预期工作。

DateTime&#34;问题&#34;和选项

YESNO作为列类型名称并不像预期的那样完全正常工作。它不会解析TIMEDateTime.Now.TimeofDay)。该表将TIME映射到Timespan

请勿使用 DbType.DateTimeDbType.DateTime2。转换器查找中缺少这些内容,因此数据以无效格式(版本1.0.103.0)存储为文本。

UTC,种类和标志

SQLite NET Provider不支持一种日期格式。保存为UTC时,数据包含指示符。但是,无论是保存为本地还是UTC,.DateTimeOffset始终都会将作为Kind返回。解决此问题的部分原因是将Unspecified添加到您的连接字符串中:

datetimekind

这会为所有`...;datetimekind=Utc;` `...;datetimekind=Local;` 返回设置Kind,但不转换值。

this 的补救措施是使用(相对)新的DateTime连接标志。当保存时,这将转换日期以匹配连接的BindDateTimeWithKind

DateTimeKind

虽然传递了本地日期,但Private LiteConnStr = "Data Source='C:\Temp\demo.db';Version=3;DateTimeKind=Utc;" ... Dim dt As New DateTime(2011, 2, 11, 11, 22, 33, 444, DateTimeKind.Local) Using dbCon = New SQLiteConnection(LiteConnStr) dbCon.Flags = SQLiteConnectionFlags.Default Or SQLiteConnectionFlags.BindDateTimeWithKind ... cmd.Parameters.Add("@dt", DbType.DateTime).Value = dt ' == 2011-02-11 17:22:33.444Z note the hour 会将其保存为UTC以匹配连接。由于&#34; DateTimeKind = Utc;&#34;返回UTC日期。连接设置。

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

手动使用

Kind可能很繁琐,要在连接字符串中指定它们:

ConnectionFlags

限制/问题

统一connx = "...;datetimekind=Utc;flags='Default, BindDateTimeWithKind';" 治疗适用于Kind,至少与Dapper一起使用。但是,在使用DbDataReader时,日期的DataTable仍为未指定。这显然是由于Kind中的DateTimeMode属性,并且微软可能不会假设列中的所有日期始终都是DataColumn。这也体现在其他数据库中。

使用UTC或本地连接时,提供程序单独保留Unspecified(这也适用于查询中的日期)。所以不应该有任何不受欢迎的额外转换:UTC日期阅读和伪装&#39;在Kind中未指定未在更新中再次转换

Ticks Format

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

DateTimeFormat

其他Private LiteConnStr = "...;datetimekind=Utc;DateTimeFormat=Ticks;..." 'e.g: 634939900800000000 选项包括 CurrentCulture ISO8601 (默认), JulianDay UnixEpoch 。无需更改列类型名称即可使用其中一种格式。它仍然是一个日期,SQLite Provider根据连接标志处理实现细节。

UI浏览器

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

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

允许你输入你想要的任何类型名称,所以获利!

摘要(tl; dr)

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

最重要的是,NET提供商使实际存储成为实现细节