SQLite:从 numeric/real 列读取字符串值 (.NET)

SQLite: Read string value from numeric/real column (.NET)

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

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

创建table:

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)

double转值的方法:

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

第二种数值转double的方法:

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 并不真正强制执行数据类型,所以我也应该能够将文本存储在数字列中。事实上我可以。

如果我这样创建 table:

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 我看到:

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

如果自动遇到真正的列,似乎 SQLite API 或 System.Data.SQLite 实现会将每个值转换为双精度值,即使 GetValue 而不是 GetDouble 用来。它可能不是 API,因为数据库浏览器正确读取字符串值。

我是否遗漏了一些明显的东西,或者是否存在一些错误,也许可以修复它? 我希望看到的行为是,如果我将一个双精度值写入 table,GetValue returns 是一个双精度值(当然是装在一个对象中),如果我将一个字符串写入柱子。有什么办法可以实现吗?

我已经阅读了有关 BindAndGetAllAsText 标志的信息,但我看到了那里的问题,因为 a) 同样,所有内容都以文本形式写入数据库,并且 b) 字符串表示形式依赖于 OS 语言,这在不同的计算机上读回数据时可能会出现问题。

非常感谢 VB.NET 和 C# 的回答,因为这不是特定于语言的问题。

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

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

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

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

这使我能够直接从数据库读取双精度值和向数据库写入双精度值,同时保持特殊值的正确行为。您当然会松开定义为常量的单个数字,并在某种程度上限制双精度值范围。在大多数情况下,你应该没问题,你甚至可以比我在这里做的更远。