Excel VBA 从关闭的工作簿中读取数据,带有 ADODB、动态范围和标题可选

Posted

技术标签:

【中文标题】Excel VBA 从关闭的工作簿中读取数据,带有 ADODB、动态范围和标题可选【英文标题】:Excel VBA read data from closed workbook with ADODB, Dynamic Range & header optional 【发布时间】:2014-06-03 19:33:37 【问题描述】:

我有一些 VBA 代码可以使用 VBA 从工作簿中提取数据,它可以工作。 我可以指定输入范围,并获取数据。 这对我来说很重要,因为我需要能够快速打开工作簿(正常打开是 3 分钟以上,ADODB 是 12 秒......) 这对我来说也很重要,因为我只想为自己获取数据,而其他用户正在处理它(这里有一些问题)

除了这段代码之外,还需要能够做到以下几点

我需要能够指定数据的去向(目标工作表、行和列) 我需要能够在不干扰其他用户的情况下执行此宏:

如果我以当前形式运行宏,当另一个用户打开工作簿时,我从中提取数据,另一个用户无法再保存,有什么建议吗? 另外:有时当另一个用户打开工作簿时,我无法从中提取数据。

任何人都可以帮助我使用 ADODB 打开它并且其他用户仍然可以保存,并帮助我能够在 sub 中指定目标范围? (所以我可以指定数据将落在哪里?:))

非常感谢!

(低于所有代码)

这个子是一个示例子,如何提取数据

Sub test()

    file_path = "C:\"
    file_name = "Example.xlsx"

    Call Pull_Data_from_Excel_with_ADODB(CStr(file_path & file_name), "The Worksheet", 1, 1, 600, 25)
End Sub

使用 ADODB 从 excel 文件中提取数据的子程序

Sub Pull_Data_from_Excel_with_ADODB(filename As String, sheetname As String, _
                                    startRow As Integer, StartColumn As Integer, _
                                    endRow As Integer, EndColumn As Integer)

'-----------------------------------------------------------------------------------
'I ********references are set to:********
'I * Visual Basic For Applications
'I * Microsoft Excel 12.0 ObjectLibrary
'I * Microsoft ADO Ext. 6.0 for DDL and Security
'I * Microsoft ActiveX Data Objects 6.1 Library
'I * Microsoft AcitveX Data Objects Recordset 6.0 Library
'-----------------------------------------------------------------------------------


On Error Resume Next
    Dim cnStr As String
    Dim rs As ADODB.Recordset
    Dim query As String
    Application.ScreenUpdating = False
    my_range = CellRange_to_nameRange(startRow, StartColumn, endRow, EndColumn)
    sheetrange = my_range
    'Dim filename As String
    'filename = "C:\temp\file1.xlsm"

    cnStr = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
               "Data Source=" & filename & ";" & _
               "Extended Properties=Excel 12.0"

    'query = "SELECT * FROM [Sheet1$D1:D15]"
    query = "SELECT * FROM [" & sheetname & "$" & sheetrange & "]"

    Set rs = New ADODB.Recordset
    rs.Open query, cnStr, adOpenStatic, adLockReadOnly


    '------------------------------------------------------------------------'------------------------------------------------------------------------
    '   Ways of opening the data & their explaination - CursorTypeEnum Values
    '------------------------------------------------------------------------'------------------------------------------------------------------------
    '   Constant        -   Value   -   Description
    '------------------------------------------------------------------------'------------------------------------------------------------------------
    '   adOpenUnspecified   -   -1  -   Unspecified type of cursor
    '   adOpenForwardOnly   -   0   -   Default. A forward-only cursor. This improves performance when you need to make only one pass through a Recordset
    '   adOpenKeyset        -   1   -   A keyset cursor. Like a dynamic cursor, except that you can't see records that other users add, although records that other users delete are inaccessible from your Recordset. Data changes by other users are still visible.
    '   adOpenDynamic       -   2   -   A dynamic cursor. Additions, changes, and deletions by other users are visible, and all types of movement through the Recordset are allowed
    '   adOpenStatic        -   3   -   A static cursor. A static copy of a set of records that you can use to find data or generate reports. Additions, changes, or deletions by other users are not visible.
    '------------------------------------------------------------------------'------------------------------------------------------------------------


    '------------------------------------------------------------------------'------------------------------------------------------------------------
    '   Lock Types & their explaination - LockTypeEnum Values
    '------------------------------------------------------------------------'------------------------------------------------------------------------
    '   Constant            -   Value   -   Description
    '------------------------------------------------------------------------'------------------------------------------------------------------------
    '   adLockUnspecified       -   -1  -   Unspecified type of lock. Clones inherits lock type from the original Recordset.
    '   adLockReadOnly          -   1   -   Read-only records
    '   adLockPessimistic       -   2   -   Pessimistic locking, record by record. The provider lock records immediately after editing
    '   adLockOptimistic        -   3   -   Optimistic locking, record by record. The provider lock records only when calling update
    '   adLockBatchOptimistic   -   4   -   Optimistic batch updates. Required for batch update mode
    '------------------------------------------------------------------------'------------------------------------------------------------------------


    '------------------------------------------------------------------------'------------------------------------------------------------------------
    '   ????????? - CommandTypeEnum Values
    '------------------------------------------------------------------------'------------------------------------------------------------------------
    '   Constant            -   Value   -   Description
    '------------------------------------------------------------------------'------------------------------------------------------------------------
    '   Constant            -   Value   -   Description
    '   adCmdUnspecified    -   -1      -   Unspecified type of command
    '   adCmdText           -   1       -   Evaluates CommandText as a textual definition of a command or stored procedure call
    '   adCmdTable          -   2       -   Evaluates CommandText as a table name whose columns are returned by an SQL query
    '   adCmdStoredProc     -   4       -   Evaluates CommandText as a stored procedure name
    '   adCmdUnknown        -   8       -   Default. Unknown type of command
    '   adCmdFile           -   256     -   Evaluates CommandText as the file name of a persistently stored Recordset. Used with Recordset.Open or Requery only.
    '------------------------------------------------------------------------'------------------------------------------------------------------------

    '------------------------------------------------------------------------'------------------------------------------------------------------------
    '   Some Extra Options, i guess - ExecuteOptionEnum Values
    '------------------------------------------------------------------------'------------------------------------------------------------------------
    '   Constant                -   Value   -   Description
    '------------------------------------------------------------------------'------------------------------------------------------------------------
    '   adOptionUnspecified     -   -1      -   Unspecified command
    '   adAsyncExecute          -   16      -   The command should execute asynchronously. Cannot be combined with the CommandTypeEnum value adCmdTableDirect
    '   adAsyncFetch            -   32      -   The remaining rows after the initial quantity specified in the CacheSize property should be retrieved asynchronously
    '   adAsyncFetchNonBlocking -   64      -   The main thread never blocks while retrieving. If the requested row has not been retrieved, the current row automatically moves to the end of the file. If you open a Recordset from a Stream containing a persistently stored Recordset, adAsyncFetchNonBlocking will not have an effect; the operation will be synchronous and blocking. adAsynchFetchNonBlocking has no effect when the adCmdTableDirect option is used to open the Recordset
    '   adExecuteNoRecords      -   128     -   The command text is a command or stored procedure that does not return rows. If any rows are retrieved, they are discarded and not returned. adExecuteNoRecords can only be passed as an optional parameter to the Command or Connection Execute method
    '   adExecuteStream         -   256     -   The results of a command execution should be returned as a stream. adExecuteStream can only be passed as an optional parameter to the Command Execute method
    '   adExecuteRecord         -   512     -   The CommandText is a command or stored procedure that returns a single row which should be returned as a Record object
    '------------------------------------------------------------------------'------------------------------------------------------------------------

    Cells.Clear
    Range("A3").CopyFromRecordset rs
    Dim cell As Range, i As Long
    'headers

    With Range("A1").CurrentRegion
        For i = 0 To rs.Fields.Count - 1
            .Cells(2, i + 1).Value = rs.Fields(i).Name
        Next i
        .EntireColumn.AutoFit
    End With
    rs.Close
    'Unload rs
    Application.ScreenUpdating = True
End Sub

下面的函数转换例如1,1,2,2 到 "A1:B2"

Public Function CellRange_to_nameRange(startRow As Integer, StartColumn As Integer, endRow As Integer, EndColumn As Integer)
Dim exportstring As String
exportstring = CStr(RowAndCollumnToName(startRow, StartColumn)) + ":" + CStr(RowAndCollumnToName(endRow, EndColumn))
CellRange_to_nameRange = exportstring
End Function

下面的函数将例如:1,1,转换为“A1”

Public Function RowAndCollumnToName(row_number As Integer, column_number As Integer)
Dim constr As String
constr = CStr(ColumnNumber_to_ColumnName(CInt(column_number)))
constr = constr & CStr(row_number)
RowAndCollumnToName = constr
End Function

下面的函数接受从 1 到 26 的任意数字,从字母表中吐出匹配的字母:例如3 --> C

Public Function number_to_alphabet_letter(number As Integer)

Dim MyArray(1 To 26) As String

    For intLoop = 1 To 26
        MyArray(intLoop) = Chr$(64 + intLoop)
    Next


    number_to_alphabet_letter = MyArray(number)
End Function

下面的函数将 ColumnNumber 转换为名称(最多 3 位...)

Public Function ColumnNumber_to_ColumnName(number As Integer)
On Error Resume Next

Dim first_digit As Integer
Dim first_letter As String

first_digit = number Mod 26
first_letter = number_to_alphabet_letter(first_digit)
'-----------------------------------------------------------
Dim second_digit As Integer
Dim second_letter As String

second_digit = (((number - (number Mod 26)) / 26) Mod 26)
second_letter = number_to_alphabet_letter(second_digit)
'-----------------------------------------------------------
Dim third_digit As Integer
Dim third_letter As String

third_digit = number - ((((number Mod 26) + ((((number - (number Mod 26)) / 26) Mod 26) * 26))))
third_digit = third_digit / (26 * 26)
third_letter = number_to_alphabet_letter(third_digit)
'-----------------------------------------------------------
'number_to_alphabet_letter_advanced = CStr(third_digit) + "-" + CStr(second_digit) + "-" + CStr(first_digit) 'test
ColumnNumber_to_ColumnName = third_letter + second_letter + first_letter
End Function

更新 无论哪种方式,我都会收到错误消息“运行时错误'-2147467259(90004005)':来自外部数据库驱动程序()的意外错误。”或者使用 Excel 工作表的人在尝试保存时收到错误消息“无法保存”

【问题讨论】:

尝试使用adOpenStaticadLockReadOnly 而不是您当前的设置? 我认为其他用户无法将更改保存到其工作簿的原因只有两个。要么 1) 你仍然打开它,要么 2) 你做了一些改变它的事情或让 Excel 将它标记为已更改。您已禁用错误处理并且没有检查错误状态,因此您的 rs.Close 完全有可能失败。而且您没有以只读方式打开它,因此您无法确定它没有被标记为已更改。 @TimWilliams 我按照您的建议实施了更改,但是:我收到错误消息“运行时错误'-2147467259 (90004005)':来自外部数据库驱动程序 () 的意外错误。”或者使用 Excel 工作表的人在尝试保存时收到错误消息“无法保存”... 【参考方案1】:

不是答案,但太大而无法放入评论......

最后四个被 this 调用的方法:

my_range = CellRange_to_nameRange(startRow, StartColumn, endRow, EndColumn) 

如果你改用类似的东西,可以删除:

With ActiveSheet
    my_range = .Range(.Cells(startRow, StartColumn), _
                      .Cells(endRow, EndColumn)).Address()
End With

【讨论】:

以上是关于Excel VBA 从关闭的工作簿中读取数据,带有 ADODB、动态范围和标题可选的主要内容,如果未能解决你的问题,请参考以下文章

Excel VBA如何从另一个工作簿中获取工作表对象

在 VBA 中使用 SQL 连接两个 Excel 工作簿中的数据(只读错误)

excel里面我想 用VBA实现调用另一个工作簿中的数据怎么解决

在单个文件中的所有打开的工作簿中重新使用带有“Worksheet_Change”事件的 VBA 宏

VBA 将Excel工作簿中所有表格进行一键排版

VBA代码根据excel工作簿中的值从webform下拉列表中选择一个值