我有一个带有名为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
我已经检查了正确的字段类型“整数”,我也尝试了“ GetType(Single)”和“ GetType(Decimal)”,但仍然相同。谁能指出我正确的方向?谢谢。
来自评论:
SQLite中没有其他类型。在SQLite中也只有“ Text”,“ Integer”,“ Real”和“ Blob”,它表示整数可以有小数。
最佳答案
您没有指出正在使用的数据库提供程序,但是标准提供程序(来自SQLite开发人员)将看到Integer
并将数据映射到不允许小数的NET Int32
类型。 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识别的列类型名称
字节,字节
INT8,INTEGER8,TINYSINT(字节)
UINT8,UNSIGNEDINTEGER8,TINYINT(字节)
整数(短,长,有符号,无符号等)
BIGINT,BIGUINT,COUNTER,IDENTITY,INT,INT16,INT32,INT64,INTEGER,INTEGER16,INTEGER32,INTEGER64,LONG,SMALLINT,SMALLUINT,UINT,UINT16,UINT32,UINT64,ULONG,UNSIGNEDINTEGER,UNSIGNEDINTEGEREGER16,UN64EDINTEDEGEREGER16,UN64
布尔型
BIT,布尔,布尔,逻辑,是
文字/字串
CHAR,CLOB,LONGCHAR,LONGTEXT,LONGVARCHAR,MEMO,NCHAR,NOTE,NTEXT,NVARCHAR,STRING,TEXT,VARCHAR,VARCHAR2
数字
双重,浮动,真实;单(单)
小数
货币,十进制,货币,数字,数字
BLOB
二进制,BLOB,常规,图像,OLEOBJECT,RAW,VARBINARY
约会时间
DATE,DATETIME,SMALLDATE,TIME,TIMESTAMP
图形用户界面
GUID,唯一身份验证者
来源:SQLiteDbTypeMap
中的SQLiteConvert.cs
(版本1.0.103; 2016年9月)。
本质上,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
);
有几件事要注意,还有一些有用的
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种类型之外的所有类型。例如,请注意
GUID
和IMAG
*都存储为BLOB
,但是GUID
类型名称与不同的DbType
关联,这使得BLOB的返回不同于IMAGE
BLOB的返回。 。您还可以通过连接对象指定类型。空格和作用域不允许解释,但是虽然有点乏味,但它允许您为自定义类型名称提供数据类型。
储存资料
存储数据时,您不必担心如何存储数据。 DB Provider将使用传递的
DbType
来查找要使用的SQLite类型(“相似性”)。如果使用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
表示Guid
)。提供者将执行大多数转化。没有
DbType.Image
,因此需要字节数组转换。为
Char()/VarChar()
字段指定大小不会限制保存的字符数。这似乎是一个错误,因为保存的字符数超过定义的字符数可能会阻止该行加载。UInt16
的作用相反:尝试传递超出范围的值,例如UInt16的-5,将导致Overflow Exception
。但是对于已存储的值,它将返回65531
。列的大小/精度参数(例如
Decimal(9,2)
)似乎无关紧要。内部表提供固定的精度和大小。对于日期,请传递日期并指示
DbType.DateTime
。永远都不需要传递特定格式的字符串。提供者知道事情。 (请参阅下面的DateTime选项。)要仅保存日期,请仅传递日期:
.Value = DateTime.Now.Date
。两个不同的查询表用于保存和读取数据,它们的共同点是
DbType
,这就是为什么它很重要。使用正确的数据可确保数据可以往返。避免使用AddWithValue
。演示/结果
来自UI浏览器的数据视图
加载数据不需要任何特殊操作:
// 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;
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
请注意,Guid和Image项都存储为
BLOB
,但返回的方式不同。活动(BOOL
)和非活动(YESNO
)使用不同的类型名称,但返回相同的数据类型。一切都按要求工作。DateTime“问题”和选项
TIME
作为列类型名称不能按预期方式工作。它不会解析DateTime.Now.TimeofDay
(Timespan
)。该表将TIME映射到DbType.DateTime
。不要使用
DbType.DateTime2
或.DateTimeOffset
。这些在转换器查找中丢失,因此数据以无效格式(版本1.0.103.0)存储为文本。UTC,种类和标志
SQLite NET提供程序不只支持一种日期格式。当另存为UTC时,数据包括一个指示符。但是,无论保存为本地还是UTC,
Kind
始终返回为Unspecified
。解决此问题的方法之一是将datetimekind
添加到您的连接字符串中:`...;datetimekind=Utc;`
`...;datetimekind=Local;`
这将为所有返回的
Kind
值设置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
尽管已传递本地日期,但
BindDateTimeWithKind
会将其保存为UTC以匹配连接。由于“ DateTimeKind = Utc;”而返回UTC日期。连接设置。请注意,
DateTimeKind
用于读取的日期,而BindDateTimeWithKind
在保存日期时起作用。个别而言,它们似乎会使情况更糟。整个数据库一起成为基于UTC(或本地)的日期,日期统一保存并读取为同一Kind
-您无需执行任何操作。ConnectionFlags
手动操作很麻烦,要在连接字符串中指定它们:connx = "...;datetimekind=Utc;flags='Default, BindDateTimeWithKind';"
限制/问题
统一的
Kind
处理对于DbDataReader
至少适用于Dapper。但是,当使用DataTable
时,日期的Kind
仍未指定。显然,这是由于DateTimeMode
中的DataColumn
属性引起的,并且可能是Microsoft的一项设计决策,即不假定列中的所有日期始终都是相同的Kind
。这也体现在其他数据库中。使用UTC或本地连接时,提供程序将不指定未指定(这也适用于查询中的日期)。因此,不应有任何不希望有的额外转换:在
DataTable
中读取并“伪装”为未指定的UTC日期不会在更新中再次转换。报价格式
与“传统智慧”日期相反,日期并不总是且仅保存为TEXT。为了节省一点空间,您可以保存刻度值。由于这些不能具有时区指示器,因此
Kind
相关选项可能非常有用。要启用Ticks,请使用DateTimeFormat
连接字符串选项:Private LiteConnStr = "...;datetimekind=Utc;DateTimeFormat=Ticks;..."
'e.g: 634939900800000000
其他
DateTimeFormat
选项包括CurrentCulture,ISO8601(默认设置),JulianDay和UnixEpoch。无需更改列类型名称即可使用这些格式之一。现在仍然是日期,SQLite Provider根据连接标志处理实现细节。UI浏览器
许多SQLite UI浏览器似乎只知道四种规范类型。也许这是故意的,但这限制了它们对NET开发人员的实用性,并隐藏了NET提供程序的功能。
SQLiteStudio(版本:3.1.0)提供了更多功能,但它似乎并不知道完整列表,因为缺少了一些非常有用的功能(例如GUID,IMAGE,SINGLE,整数变体)。
它确实允许您输入所需的任何类型名称,因此,Profit!
摘要(tl; dr)
NET Provider通过支持各种类型的列名称来为SQLite添加功能,
每个受支持的名称都与
DBType
关联,该DbType
确定实际的返回数据类型保存数据时使用正确的
DateTimeKind
可确保数据往返NET Provider将为您执行大多数转换
BindDateTimeWithKind
和首先,NET提供程序使实际存储成为实现细节。
关于vb.net - SQLite不能正确存储小数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44298684/