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

问题描述 投票:1回答:6

我正在将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包含数字,我无法导入这些单元格的值。怎么做?

asp.net vb.net excel oledb
6个回答
6
投票

如原始文章中所述,使它失败的唯一方法是,转义/文本单元格是否比我最初测试的单元格低得多。 OleDB不能在Excel文件中使用Schema.ini,这太糟糕了,因为这样可以提供一个非常干净简单的解决方案,a ...

使用的样本数据:

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

它实际上使用16行,最后3个“行”项作为文本转义(例如'2)。这些都在Excel中显示绿色角警告标志。

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

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

对于发布的案例,如果实际上只涉及两列,则应该能够使用一个表作为文本读入;并添加一个数字列以接收转换后的值。不是从一个表转换为另一表,而是从一列转换为另一列。 (问一下,是否要举个例子,但这只是以下内容的一部分)。

在任何情况下,“技巧”都是使用不同的连接字符串来强制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的组合可能带来多种可能性,因此我们可能需要一个后备转换器(我在将back滴答偶然地添加到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

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

<< img src =“ https://image.soinside.com/eyJ1cmwiOiAiaHR0cHM6Ly9pLnN0YWNrLmltZ3VyLmNvbS9BYVNESC5wbmcifQ==” alt =“在此处输入图像描述”>俄罗斯,日本和葡萄牙是排名数据以文本形式转义的行。

资源:


1
投票

我对您正在谈论的这个错误很熟悉,“数字被设置为文本格式或以撇号开头。”我在工作中使用了一个应用程序,该应用程序将所有数据存储为文本值,当我导出为ex​​cel时,每个数字字段都会发生这种情况。它与数据的基础类型有关。是否在单元格(甚至是数据库)中键入数字(例如123)都没有关系;重要的是它被解释为哪种类型。因此123 <>“ 123”(是一个字符串)。 问题很可能是您尝试将数据加载到的数据库中的字段具有数字类型(例如int),但是导入数据的程序无法将其识别为数字,而是文本值;因此它会抛出NULLS来进行补偿。

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


1
投票

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

“在此处输入图像描述”

请查看this link,希望有帮助


0
投票

您的问题是对excel的数据访问。喷射引擎按前8行(或类似的行)解释列数据。因此,如果前8个单元格(不包括标题)是数字,那么Jet引擎将对该列采用数字类型。随后的每个与该数据类型都不匹配的单元格将为NULL

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


0
投票

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

0
投票

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

扩展属性= \“ Excel 12.0 Xml; HDR =是; IMEX = 1; ImportMixedTypes = Text; TypeGuessRows = 0 \”“;

并且有效。这是来自Jet.OLEDB的文件,但它可与ACE一起使用。

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

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

© www.soinside.com 2019 - 2024. All rights reserved.