从 Excel 导入到 DataSet 时,为列值插入 NULL

NULL being inserted for column values when importing from Excel to DataSet

我正在将 excel 数据导入我的应用程序中的数据表,但遇到某些特定列值的问题。

excel sheet 列中的某些单元格 CustomerUniqID 显示角部带有绿色标记的警告。

The number is formatted as text or preceded by apostrophe.

当从 Excel sheet.

填充数据集时,这些单元格值未导入并显示空白值
Dim query As String = "SELECT CINT(CustomerUniqID),[Status] FROM [Sheet1$]"
Dim conn As New OleDbConnection(conStr)
If conn.State = ConnectionState.Closed Then
   conn.Open()
End If
Dim cmd As New OleDbCommand(query, conn)
Dim da As New OleDbDataAdapter(cmd)
Dim ds As New DataSet()
da.Fill(ds)

我的连接字符串是

<add name ="Excel07ConString" connectionString="Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};Extended Properties='Excel 12.0;HDR=YES;IMEX=2'"/>

CustomerUniqID 包含数字,我无法导入这些单元格值。如何做到这一点?

您的问题是 excel 的数据访问。喷气引擎通过前 8(或类似的)行解释列数据。因此,如果前 8 个单元格(不包括 headers)是数字,喷气发动机将为该列假定一个数字类型。与 data-type 不匹配的每个后续单元格将是 NULL.

您可以在此处找到更多信息:I need a workaround for Excel Guessing Data Types problem

如原始 post 中所述,唯一能让它失败的方法是 escaped/text 单元格比我最初测试的单元格更靠下。遗憾的是 OleDB 不会将 Schema.ini 与 excel 文件一起使用,因为这将提供一个非常干净和简单的解决方案,唉...

使用的示例数据:

Country    Capital     Population   Rank
France     Paris       2.25         7
Canada     Toronto     2.5          6
Egypt      Cairo       10.2         9
...

它实际上使用了 16 行,最后 3 "Rank" 项被转义为文本(例如 '2)。这些都显示在Excel.

中的绿色角落警告标志

由于 OleDB 没有 read/use 架构,它决定前 N 行(在我的注册表中定义为 8)的每一列的数据类型。当转义单元格不匹配时,它 returns 一个 DBNull 值。尝试通过 SQL(CIntVal)转换列失败,因为 OleDB 在应用转换之前已经确定那里的数据不匹配。

在某些情况下,我会读两遍 sheet。首先将 "good" 列的正确数据类型转换为一个 DataTable;然后再次将 'dirty' 列作为文本获取并手动转换数据。这在数据集中有其他数字列并且您不希望将它们转换为 text/string.

的情况下很有用。

对于 posted 的情况,如果真的只涉及 2 列,您应该可以使用一个 table 作为文本读入;并添加一个数字列以接收转换后的值。与其从一个 table 转换为另一个,不如从一列转换为另一列。 (随便问,如果你想要一个例子,但它只是以下的一个子集)。

无论哪种情况,"trick" 都是使用不同的连接字符串来强制 OleDB 将数据读取为文本。显然这需要 HDR=NoIMEX=1,至少在我的配置中是这样:

Dim TextConStr = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=C:\Temp\capitals.xls;Extended Properties='Excel 8.0;HDR=NO;IMEX=1';"

此example/text代码使用2table方法来验证其他数字(Population)未被转换,只是Rank:

' ConStr to allow OleDB to guess the datatypes   
Dim TypedConStr = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=C:\Temp\capitals.xls;Extended Properties='Excel 8.0;HDR=Yes;IMEX=2';"

' ConStr to force OleDB to read it all as Text 
Dim TextConStr = "Provider=Microsoft.ACE.OLEDB.12.0; Data Source=C:\Temp\capitals.xls;Extended Properties='Excel 8.0;HDR=NO;IMEX=1';"

' get the typed columns into a DT - skip Rank as dirty column
Dim SQL = "SELECT Country, Capital, Population FROM [Capitals$]"
Using con As New OleDbConnection(TypedConStr),
    da As New OleDbDataAdapter(SQL, con)

    dsPop.Tables.Add("Pop")
    da.Fill(dsPop.Tables("Pop"))
End Using

' create a new temp DT containing just the naughty column
' use the generic F/Field index in the SQL (we told Ole there was no header)
SQL = "SELECT F4 As RankText FROM [Capitals$]"
' create connection forcing the contents to text:
Using con As New OleDbConnection(TextConStr),
    da As New OleDbDataAdapter(SQL, con)

    dsPop.Tables.Add("RankText")
    da.Fill(dsPop.Tables("RankText"))
End Using
' remove the header row
dsPop.Tables("RankText").Rows.RemoveAt(0)

'create a new INT col in Dt(0)
dsPop.Tables("Pop").Columns.Add("Rank", GetType(Int32))

' convert Tbl(1) text to Int and store in Tbl(0) 
For n As Integer = 0 To dsPop.Tables(1).Rows.Count - 1
    dsPop.Tables("Pop").Rows(n).Item("Rank") = 
           Convert.ToInt32(dsPop.Tables("RankText").Rows(n).Item(0).ToString)
Next

'optional: remove the [RankText] tbl since we are done with it
dgv.DataSource = dsPop.Tables("Pop")

' report the datatype of the last row rank:
tbDataType.Text = dsPop.Tables("Pop").Rows(14).Item("Rank").GetType.ToString

在立即 window 中,报告的类型符合预期:

? dspop.Tables("Pop").Rows(0).Item(2)       ' (population - paris)
2.25 {Double}
? dspop.Tables("RankText").Rows(0).Item(0)  ' temp table text
"7" {String}
? dspop.Tables("Pop").Rows(0).Item(3)       ' converted, merged value
7 {Integer}

对我来说,OleDB 会自动将 '3 转换为 "3"。换句话说,它在转换为文本时省略了前导 tick/apostrophe。由于 Excel 版本与 OleDB.ACE 和 OleDb.Jet 的组合可能产生多种可能性,我们可能需要一个回退转换器(我在添加 back[ 之后写了这个=66=] 偶然跳到 Excel,也许对某些人有价值):

Private Function GetNumericValue(s As String) As Integer
    ' ToDo add exception handling
    If Char.IsDigit(s(0)) Then
        Return Convert.ToInt32(s)
    Else
        Return Convert.ToInt32(
            New String(s.ToCharArray(1, s.Length - 1))
            )
    End If
End Function

它只会检查 non-numeral 的第一个字符,否则它可能会将 "1234 Main Street Suite 56" 转换为 123456,这可能是不可取的。结果:


俄罗斯、日本和葡萄牙是将排名数据转义为文本的行。

资源:

问题可能是由于您试图将非数字值转换为整数值。对于表达式,如果我们使用以下表达式:=CInt(“ABC”),我们将在报告中得到#Error 值。

请参考下面的自定义代码来解决此问题:

Public Function Conv(ByVal A as String) 
Dim B as string
Dim C as Integer
If isnumeric(A)  Then
C=CInt(A)
Return C
else
B=CStr(A)
Return B
End If 
End Function

我熟悉您所说的这个错误,"The number is formatted as text or preceded by apostrophe." 我在工作中使用了一个应用程序,将所有数据存储为文本值,当我导出到 excel每个数字字段都会发生这种情况。它与数据的基础类型有关。将数字(例如 123)输入单元格(甚至输入数据库)都没关系;重要的是它被解释为的类型。所以 123 <> "123" (这是一个字符串)。 问题可能是您尝试将数据加载到的数据库中包含数字类型(例如 int)的字段,但导入数据的程序无法将其识别为数字,而是文本价值;因此它抛出 NULLS 来补偿。

但这在某些情况下很好,例如,这些数字中是否有任何前导零?如果是这样,您可能希望将它们存储为文本值以保留前导零。如果没有,解决方法是(在 excel 2010 年)转到数据选项卡 => 文本到列,然后 运行 通过向导获取正确的数据类型。这实质上是解析值。如果其他所有内容都正确加载,那么看起来问题不在于您的连接。这应该可以直接在 Excel 中修复。

我以前遇到过这个问题,解决这个问题的唯一方法是将单元格转换为数字,而不是从格式的菜单中!我是这样做的,如下图

请看一下this link,希望对您有所帮助

我遇到了同样的问题,几乎放弃了,但我试过了:

扩展属性=\"Excel 12.0 Xml;HDR=Yes;IMEX=1;ImportMixedTypes=Text;TypeGuessRows=0\"";

它奏效了。 这个来自 Jet.OLEDB 但它适用于 ACE。

"Check out the [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet.0\Engines\Excel] located registry REG_DWORD "TypeGuessRows”。这是不让 Excel 仅使用前 8 行来猜测列数据类型的关键。将此值设置为 0 以扫描所有行。这可能会造成伤害性能。另请注意,添加 IMEX=1 选项可能会导致 IMEX 功能在仅 8 行后设置。改用 IMEX=0 以确保强制注册表 TypeGuessRows=0(扫描所有行)工作。“。” =11=]

取自https://www.connectionstrings.com/excel/