【问题标题】:NULL being inserted for column values when importing from Excel to DataSet从 Excel 导入数据集时为列值插入 NULL
【发布时间】:2015-01-09 07:41:23
【问题描述】:

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

Excel 工作表列中的某些单元格 CustomerUniqID 在角落显示带有绿色标记的警告。

数字格式为文本或以撇号开头。

当从 Excel 工作表中填充数据集时,这些单元格的值不会被导入并显示空白值。

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 列包含数字,我无法导入这些单元格值。如何做到这一点?

【问题讨论】:

  • 删除CINT后是否加载?
  • CINT 抛出异常,因为正在为这些单元格插入 NULL 值。
  • 我也使用过VAL(CustomerUniqID),但由于这些有问题的单元格的值为空,这也会引发异常。
  • 这些单元格真的是空的吗?
  • 可以分享一个excel示例吗?

标签: asp.net vb.net excel oledb


【解决方案1】:

我可以让它失败的唯一方法是原始帖子中描述的,如果转义/文本单元格比我最初测试的单元格更深。 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 个“排名”项被转义为文本(例如 '2)。这些都在 Excel 中显示绿色角落警告标志。

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

在某些情况下,我会读两遍。首先将正确数据类型中的“好”列放入一个DataTable;然后再次将“脏”列作为文本并手动转换数据。这在数据集中有其他数字列并且您不希望将它们转换为文本/字符串的情况下很有用。

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

在任何一种情况下,“技巧”都是使用不同的连接字符串来强制 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';"

此示例/文本代码使用2表的方法来验证其他数字(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

在即时窗口中,报告的类型与预期的一样:

? 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"。换句话说,它在转换为文本时省略了前导刻度/撇号。由于 Excel 版本与 OleDB.ACE 和 OleDb.Jet 的组合可能会产生多种可能性,因此我们可能需要一个备用转换器(我在向 Excel 添加 back 记号后偶然写了这个,也许它对某人有价值):

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

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


俄罗斯、日本和葡萄牙是 Rank 数据以文本形式转义的行。

资源:

【讨论】:

    【解决方案2】:

    我对您所说的这个错误很熟悉,“数字格式为文本或前面带有撇号。”我在工作中使用了一个应用程序,它将所有数据存储为文本值,当我导出到 excel 时,每个数字字段都会发生这种情况。它与数据的基础类型有关。如果您在一个单元格(或者甚至是数据库)中输入一个数字(例如 123)并不重要;重要的是它被解释为的类型。所以 123 "123" (这是一个字符串)。 问题可能是您尝试将数据加载到其中的数据库具有数字类型(例如 int)的字段,但导入数据的程序未将其识别为数字,而是将其识别为文本值;因此它抛出 NULLS 来补偿。

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

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

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

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

        "查看位于注册表 REG_DWORD "TypeGuessRows" 的 [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Jet\4.0\Engines\Excel]。这是不让 Excel 仅使用前 8 行来猜测列数据类型的关键。设置将此值设置为 0 以扫描所有行。这可能会损害性能。还请注意,添加 IMEX=1 选项可能会导致仅在 8 行之后设置 IMEX 功能。使用 IMEX=0 代替以确保强制注册表 TypeGuessRows =0(扫描所有行)才能工作。”

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

        【讨论】:

          【解决方案5】:

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

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

          【讨论】:

            【解决方案6】:

            问题可能是由于您尝试将非数字值转换为整数值。对于表达式,如果我们使用以下表达式:=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
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2016-06-26
              • 1970-01-01
              • 1970-01-01
              • 2020-02-24
              • 1970-01-01
              相关资源
              最近更新 更多