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

Mud*_*san 1 vb.net asp.net oledb excel

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

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

该数字的格式为文本或前缀为撇号.

从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)
Run Code Online (Sandbox Code Playgroud)

我的连接字符串是

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

CustomerUniqID列包含数字,我无法导入这些单元格值.这该怎么做 ?

Ňɏs*_*arp 6

如原始帖子中所述,我唯一可以让它失败的方法是,如果转义/文本单元格比我最初测试的更低.OleDB不会使用Schema.iniexcel文件太糟糕了,因为这样可以提供一个非常简洁的解决方案,唉......

使用的样本数据:

Country    Capital     Population   Rank
France     Paris       2.25         7
Canada     Toronto     2.5          6
Egypt      Cairo       10.2         9
...
Run Code Online (Sandbox Code Playgroud)

它实际上使用了16行,最后3个"Rank"项目被转义为文本(例如'2).这些都显示Excel中的绿色角警告标志.

由于OleDB不读取/使用Schema,它会从前N行(在我的注册表中定义为8)中确定每列的数据类型.当转义的单元格与之匹配时,它将返回DBNull值.尝试通过SQL(CInt,Val)转换列失败,因为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';"
Run Code Online (Sandbox Code Playgroud)

此示例/文本代码使用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
Run Code Online (Sandbox Code Playgroud)

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

? 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}
Run Code Online (Sandbox Code Playgroud)

对我来说,OleDB会自动转换'3"3".换句话说,它在转换为文本时省略了前导tick /撇号.由于Excel版本和OleDB.ACE以及OleDb.Jet的组合可能会产生很多可能性,我们可能需要一个后备转换器(我在意外地将Excel 添加 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
Run Code Online (Sandbox Code Playgroud)

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

在此输入图像描述
俄罗斯,日本和葡萄牙是将Rank数据作为文本转义的行.

资源: