Как я могу предотвратить дублирование данных Microsoft ACE и JET в VB6 из первой строки электронной таблицы Excel? - PullRequest
2 голосов
/ 30 августа 2011

Я имею дело с унаследованным приложением, написанным на 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)

Когда данные считываются, независимо от того, используем ли мы 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 |

IЯ пытался подключиться к электронной таблице и получать данные множеством способов, но, похоже, ничего не работает - либо соединение не будет установлено, либо данные будут просто пустыми.Я нашел некоторые обходные пути - например, если я введу пробел в ячейку A1, проблема больше не возникает.Тем не менее, я хотел бы получить программное решение, вместо того, чтобы заставлять пользователей делать дополнительные шаги, чтобы избежать этого.Он только копирует первый ряд данных.Если данные в ячейке являются числом, то они копируют данные в каждую ячейку над ним в этом столбце, если это текст, то они повышаются только на один уровень.Интересно отметить, что, если я изменю электронную таблицу, чтобы сказать, что все данные являются текстовыми, он затем копирует каждую ячейку, как если бы они были числами (т.е. в каждую ячейку выше, а не в одну строку)

В целом это довольно раздражает - так как мне не везет при поиске этой проблемы, я могу только сделать вывод, что мы делаем что-то не так, или очень мало людей обеспокоены этим типом тестаданные.

[править] После некоторых исследований я добился определенных успехов в решении этой проблемы: «Поставщик предполагает, что ваша таблица данных начинается с самой верхней, самой левой, непустой ячейки на указанном рабочем листе»(http://support.microsoft.com/default.aspx?scid=kb;en-us;257819).Это подтверждается, если я использую инструкцию для выбора всего листа - он возвращает только блок данных.Поэтому, когда я выбираю любую ячейку, находящуюся за пределами этого диапазона, провайдер вместо того, чтобы делать что-то разумное, например, возвращать ноль, возвращает данные из самой верхней непустой ячейки из этого конкретного столбца.Я мог бы гипотетически изменить систему так, чтобы она просто собирала все данные и предполагала, что самая верхняя левая ячейка - это ячейка A1, но это нарушит совместимость с данными, которые уже существуют.Теперь мне нужен способ получения ссылок на ячейки для возвращаемых данных, чтобы я мог относиться к ним соответствующим образом, или способ заставить это больше не происходить.

Ответы [ 2 ]

1 голос
/ 30 августа 2011

В популярном блоге и ветке (первоначально OneDayWhen) о проблеме, аналогичной вашей, упоминается настройка реестра, которая меняет способ, которым Excel определяет тип данных ячейки.

Я полагаю, что это "угадайка" может быть корнем вашей проблемы.

Внешние данные - смешанные типы данных

Таким образом, используйте TypeGuessRows, чтобы получить Jet, чтобы определить, является ли ‘смешанным Ситуация с типами существует или используйте ее, чтобы «обмануть» Jet в обнаружении определенный тип данных как тип большинства. В случае Обнаружена ситуация «смешанных типов», используйте ImportMixedTypes, чтобы сообщить Jet, чтобы использовать мажоритарный тип или привести все значения как «текст» (максимум 255 символов).

Другие говорят о внесении изменений в строку подключения, чтобы включить MaxScanRows = 0, но это, похоже, не решает проблему. Я понимаю, что вы, возможно, ищете портативное решение, но я не могу найти такой обходной путь.

0 голосов
/ 12 сентября 2011

Чтобы ответить на мой собственный вопрос: похоже, вы не можете.Однако вы можете попытаться обойти эту проблему.

Поставщики JET и ACE принимают самую верхнюю непустую непустую ячейку в качестве начала набора данных (http://support.microsoft.com/default.aspx?scid=kb;en-us;257819). Итак,когда вы пытаетесь получить значение из ячейки, которая появляется до начала этого набора данных, вместо того, чтобы делать что-то разумное и возвращать ноль, поставщики возвращают догадку, основанную на верхнем ряду данных.

Я мог бы найтинет способа получить ссылки на ячейки возвращенного набора данных от провайдера - он помечал все как F1, F2 и т. д. («Поле 1», «Поле 2»)

Таким образом, было два оставшихся решения:

1) Захватите весь набор данных сразу, предположите, что данные начинаются с A1, и вставьте их в БД, используя это предположение.К сожалению, это приведет к нарушению совместимости с ранее существовавшими данными.

2) Программно определите ссылки на ячейки и правильно введите данные.Я сделал это, используя следующий сокращенный код

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
...