如何防止VB6中的Microsoft ACE和JET复制Excel电子表格中第一行的数据?

我正在处理一个用VB6编写的遗留应用程序,它读取Excel电子表格并将它们插入数据库。
它大部分工作,但如果电子表格中的数据没有在第一行开始,则第一个数据行将被复制。
例如,电子表格中的前3行是空白的,前四行数据如下所示:

_| A | B | C | D | E | F | G | 1| | | | | | | | 2| | | | | | | | 3| | | | | | | | 4| 99 |Text1|Text2|Text3|Text4|Text5| 77 | 

应用程序连接到Excel电子表格并使用以下代码读取它:

 Public Function obtainConnectionExcel(sql_conn, uid) As Variant Dim cn As Object Set cn = CreateObject("ADODB.Connection") On Error Resume Next cn.Provider = "Microsoft.ACE.OLEDB.12.0" cn.Properties("Extended Properties").Value = "Excel 12.0;ReadOnly=True;HDR=No;IMEX=1" If (Err <> 0) Then cn.Provider = "Microsoft.Jet.OLEDB.4.0" cn.Properties("Extended Properties").Value = "Excel 8.0;ReadOnly=True;HDR=No;IMEX=1" End If On Error Resume Next cn.open getSpreadsheetPath(sql_conn, uid) Set obtainConnectionExcel = cn Exit Function End Function ..... Public Function extractAllData(parameters) As String ..... 'Variable declarations etc On Error Resume Next Set dbo_conn = obtainConnectionExcel(sql_conn, uid) If Err <> 0 Then ....'logs error, goes to error handler End If On Error GoTo ErrorHandler If (dbo_conn.State = 1) Then rownumber = 1 Do While rownumber <= numberOfRowsToGet For x = lettercount To lettercount + lettercount_offset letter = Chr(x) sSql = "SELECT * FROM [" & worksheet & "$" & letter & rownumber & ":" & letter & rownumber & "]" On Error Resume Next Set rs = dbo_conn.execute(sSql) If (Not rs.EOF) Then 'inserts the data into the db End If Next x rownumber = rownumber + 1 Loop .... 'Post processing Exit Function ....'Error handlers End Function 

这应该是相关的代码。 这个问题出现在以下几行:

 sSql = "SELECT * FROM [" & worksheet & "$" & letter & rownumber & ":" & letter & rownumber & "]" On Error Resume Next Set rs = dbo_conn.execute(sSql) 

当读入数据时,不pipe我们是使用JET还是ACE,数据都是这样返回的:

 _| A | B | C | D | E | F | G | 1| 99 | | | | | | 77 | 2| 99 | | | | | | 77 | 3| 99 |Text1|Text2|Text3|Text4|Text5| 77 | 4| 99 |Text1|Text2|Text3|Text4|Text5| 77 | 

我试图连接到电子表格,并以多种方式获取数据,但似乎没有任何工作 – 连接将失败,或者数据将只是空值。
我find了一些解决方法 – 例如,如果我在A1单元格中input一个空格字符,问题不再发生。 但是,我想要一个基于程序的解决scheme,而不是告诉用户做额外的步骤来避免这种情况。
它只复制第一行数据。 如果单元格中的数据是一个数字,那么它会将数据复制到该列上方的每个单元格中,如果是文本,那么它只会上升一个级别。
有趣的是,如果我改变电子表格来表示所有的数据都是文本,那么它就像每个数据都是数字一样拷贝每个单元格(即,在上面的每个单元格中,而不是一行)

总而言之,这是相当令人恼火的 – 因为在寻找这个问题时我没有什么运气,所以我只能断定我们做错了什么事,或者很less有人对这种testing数据感兴趣。

经过一番调查之后,我在解决这个问题上取得了一些进展 – “提供者假设你的数据表是从指定工作表上最上面的,最左边的,非空白的单元开始的”(http:// support .microsoft.com / default.aspx?scid = kb; en-us; 257819)。 如果我使用语句来select整个工作表,这是确认 – 它只返回数据块。
因此,当我select任何超出该范围的单元格时,提供程序(而不是像返回null这样明智的操作)会从该特定列的最上面的非空单元格中返回数据。
我可以假设地改变系统,以便抓取所有的数据,并假定最上面的最左边的单元格是单元格A1,但是这会破坏已经存在的数据的兼容性。
我现在需要的是一种获取返回数据的单元格引用的方法,所以我可以适当地对待它,或者迫使它不再发生。

一个stream行的博客post和线程(最初由OneDayWhen)就类似于你的问题提到了一个registry调整,它改变了Excel猜测单元格的数据types的方式。

我相信这种“猜测”行为可能是你的问题的根源。

外部数据 – 混合数据types

总之,使用TypeGuessRows来让Jet检测是否存在“混合types”的情况,或者使用它来“诱骗”Jet来检测某个数据types是多数types的。 在检测到“混合types”的情况下,使用ImportMixedTypes来告诉Jet要么使用多数types,要么强制所有值作为“文本”(最多255个字符)。

其他人谈论修改您的连接string,以包括MaxScanRows = 0,但这似乎不能解决问题。 我知道你可能正在寻找一个便携式的解决scheme,但我无法find这样的解决方法。

回答我自己的问题:看起来你不能。 但是,您可以尝试对此问题进行编码。

JET和ACE提供者都将最上面最左边的非空单元当作数据集的开始(http://support.microsoft.com/default.aspx?scid=kb;en-us;257819)因此,当您尝试从数据集开始之前发生的单元格中获取值时,而不是执行某些合理的操作并返回null,则提供程序会根据最上面一行数据返回猜测&#x3002;

我无法find从提供者返回的数据集的单元格引用 – 它将所有内容标记为F1,F2等(“字段1”,“字段2”)。

所以还有两个解决scheme:

1)一次抓取整个数据集,假设数据从A1开始,并使用该假设将其插入到数据库中。 不幸的是,这会导致与预先存在的数据相冲突。

2)以编程方式确定单元格引用,并正确input数据。 我用下面的缩写代码做了这个

 sSql = "SELECT * FROM [" & worksheet & "$]" Set rs = dbo_conn.execute(sSql) rownumber = 1 If Not rs.EOF Then oledata_array = rs.GetRows() Else ReDim oledata_array(0, 0) End If Do While rownumber <= numberOfRowsToGet col_number = 1 For x = lettercount To lettercount + lettercount_offset letter = Chr(x) sSql = "SELECT * FROM [" & worksheet & "$" & letter & rownumber & ":" & letter & rownumber & "]" On Error Resume Next Set rs = dbo_conn.execute(sSql) If Not rs.EOF Then If rs(0) <> "" Then If x < furthest_left Then furthest_left = x End If If x > furthest_right Then furthest_right = x End If If rownumber > bottom_of_set Then bottom_of_set = rownumber End If Else End If End If col_number = col_number + 1 Next x rs.MoveNext rownumber = rownumber + 1 Loop rs.Close top_of_set = bottom_of_set - UBound(oledata_array, 2) If CLng(UBound(oledata_array, 1)) <> CLng(furthest_right - furthest_left) Then 'log the fact that there is a discrepency, and continue End If 'now have the co-ords of the "square" of data as it occurs in the spreadsheet rownumber = 1 row_index = 0 Do While rownumber <= numberOfRowsToGet col_number = 1 For x = lettercount To lettercount + lettercount_offset letter = Chr(x) 'construct the first chunk of the sql insert string If (x <= furthest_right) And (x >= furthest_left) And (rownumber <= bottom_of_set) And (rownumber >= top_of_set) Then sSql = sSql & "'" & oledata_array(col_number - 1, row_index) & "'" col_number = col_number + 1 Else sSql = sSql & "''" End If 'finish the sql string and execute Next x