SQLite:从数字/实数列(.NET)读取字符串值

时间:2015-01-15 16:55:03

标签: c# database vb.net sqlite system.data.sqlite

我使用SQLite数据库在.NET项目中存储双精度浮点值。 我遇到了NaN和无穷大值的一个众所周知的问题,我正在尝试通过为这些值存储字符串“NaN”,“+ Inf”,“ - Inf”并将它们转换回相应的双值来解决问题。需要。我正在使用System.Data.SQLite类在我的项目中实现SQLite。

现在我看到了解决问题的不同方法。一种工作方法如下:我可以使用TEXT类型创建列。当我为每个值执行此操作时,即使有效的双数也会存储为字符串:

创建表格:

Using cmd As New SQLiteCommand("CREATE TABLE IF NOT EXISTS Test (vals TEXT);")
...
Using cmd As New SQLiteCommand("INSERT INTO Test (vals) VALUES(@v);")
...
p.Value = SQLite_DoubleToValue(Double.PositiveInfinity)

双倍到价值转换方法:

Private Function SQLite_DoubleToValue(D As Double) As Object
    If Double.IsNaN(D) Then
        Return "NaN"
    ElseIf D > 1.79769313486E+308 Then 'Some rounding, everything above this is treated as infinity by SQLite
        Return "+Inf"
    ElseIf D < -1.79769313485E+308 Then 'Same here
        Return "-Inf"
    Else
        Return D 'Return the actual double value, boxed in an Object
    End If
End Function

回读:

Using cmd As New SQLiteCommand("SELECT * FROM Test;")
Dim rdr As SQLiteDataReader = cmd.ExecuteReader
rdr.Read()
Dim val As Double = SQLite_ValueToDouble(rdr.GetValue(0))

双转换方法的第二个值:

Private Function SQLite_ValueToDouble(V As Object) As Double
If TypeOf (V) Is String Then
    Dim s As String = CStr(V)
    If s = "NaN" Then
        Return Double.NaN
    ElseIf s = "+Inf" Then
        Return Double.PositiveInfinity
    ElseIf s = "-Inf" Then
        Return Double.NegativeInfinity
    Else 'Value can only be a number representation here
        Dim val As Double = Double.NaN
        Double.TryParse(s, val) '<- I don't like to do this conversion
        Return val
    End If
ElseIf TypeOf (V) Is Double Then '<- This never happens when the column is created as TEXT
    Return CDbl(V)
Else
    Return Double.NaN
End If
End Function

正如我在后一种转换方法中所指出的,我不喜欢甚至有效数字存储为字符串的事实。当我声明TEXT列而不是REAL列时数据库的大小增加,并且由于SQLite没有真正强制执行数据类型,所以我应该能够将文本存储在数字列中。事实上我可以。

如果我像这样创建我的表:

Using cmd As New SQLiteCommand("CREATE TABLE IF NOT EXISTS Test (vals REAL);")
...
Using cmd As New SQLiteCommand("INSERT INTO Test (vals) VALUES(@v);")
...
p.Value = SQLite_DoubleToValue(Double.PositiveInfinity)

并在数据库浏览器中检查我的数据库中的SQLite我看到了:

DB in Database Browser

因此文本很好地存储在数字列中。 但是,如果我在.NET中使用GetValue读回来我没有得到字符串,但我得到的值为0.0(Double)。

似乎SQLite API或System.Data.SQLite实现会在自动遇到实际列时将每个值转换为double,即使使用GetValue而不是GetDouble也是如此。它可能不是API,因为数据库浏览器正确读取字符串值。

我是否会错过一些明显的东西,或者是否有一些错误,也许是一个修复程序? 我希望看到的行为是,如果我在表中写了一个double值,GetValue会返回一个double值(当然是盒装在一个对象中),如果我在列中写了一个字符串,则返回一个字符串。有没有办法实现这个目标?

我已经阅读了BindAndGetAllAsText标志,但我发现问题因为a)所有内容都被写成文本到数据库中b)字符串表示依赖于操作系统语言,这可能会导致阅读时出现问题数据回到不同的计算机上。

VB.NET和C#中的答案都非常受欢迎,因为这里不是特定于语言的问题。

1 个答案:

答案 0 :(得分:1)

我通过将三个特定的double值定义为表示三种特殊状态的常量来解决问题:

Public Const NaNValue As Double = 1.7E308
Public Const PInfValue As Double = 1.71E308
Public Const NInfValue As Double = -1.71E308

我还使用类似于我在问题中展示的函数来将数据库值转换为double值,反之亦然:

Protected Shared Function DoubleToValue(D As Double) As Double
    If Double.IsNaN(D) Then
        Return NaNValue
    ElseIf D >= PInfValue Then
        Return PInfValue
    ElseIf D <= NInfValue Then
        Return NInfValue
    Else
        Return D
    End If
End Function
Protected Shared Function ValueToDouble(V As Double) As Double
         If V = NaNValue Then
            Return Double.NaN
        ElseIf V = PInfValue Then
            Return Double.PositiveInfinity
        ElseIf V = NInfValue Then
            Return Double.NegativeInfinity
        Else
            Return V
        End If
End Function

这允许我直接从数据库读取和写入双值,同时保持特殊值的正确行为。您当然会将您定义的单个数字松散为常量,并稍微限制双值范围。在大多数用例中你应该没问题,你甚至可以比我在这里更进一步地移动限制。