【问题标题】:Manipulating Excel files from Windows Scripting Host从 Windows Scripting Host 操作 Excel 文件
【发布时间】:2025-12-08 09:10:01
【问题描述】:

有没有一种快速的方法来处理来自 Windows 脚本主机的现有 XLS 文件的内容?

我们有从客户那里收到的 Excel 模板。我们的任务是用我们从 Oracle 数据库中获取的数据填充这些模板。

目前的做法是使用Windows Scripting Host和VBScript:

  1. 使用 ADODB 从 Oracle 获取数据:

    Set db = CreateObject("ADODB.Connection")
    SQL = "SELECT ..."
    Set rs=db.execute(SQL)
    
  2. 使用 VBScript 在 Windows Scripting Host 中创建 Excel 对象:

    Set objExcel = CreateObject("Excel.Application")  
    Set objWorkbook = objExcel.Workbooks.Open(xls_final)  
    Set objSheet = objWorkBook.Sheets(1)
    
  3. 然后像这样逐个单元格地填写模板:

    If rs.EOF = False Then
       rs.MoveFirst
       Do Until rs.EOF
          objSheet.Cells(RowNumber, 1).Value = rs("COLUMN1")
          objSheet.Cells(RowNumber, 2).Value = rs("COLUMN2")
          objSheet.Cells(RowNumber, 3).Value = rs("COLUMN3")
          rs.MoveNext
       Loop
    End If
    objWorkbook.Save 
    rs.Close
    

问题是其中一些文件包含大量数据,并且需要数小时才能像这样填充它们。 有更快的方法吗?

【问题讨论】:

    标签: excel vbscript ado wsh


    【解决方案1】:

    我认为你在这里没问题:

    Set db = CreateObject("ADODB.Connection")
    SQL = "SELECT ..."
    Set rs=db.execute(SQL)
    
    Set objExcel = CreateObject("Excel.Application")  
    Set objWorkbook = objExcel.Workbooks.Open(xls_final)  
    Set objSheet = objWorkBook.Sheets(1)
    

    但正如您所发现的,其余部分的速度会慢得惊人。与工作表的交互具有很高的开销,您需要为每一行中的每一列付费。有一些方法可以解决这个问题。

    最简单的是

    objSheet.Cells(1,1).CopyFromRecordset rs
    

    我建议你先试试。

    【讨论】:

    • 很好的建议。我不知道这种方法存在。
    【解决方案2】:

    看看这个。
    http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=49926
    如果您使用 ADODB 连接到 excel,而不是操作 Excel,它也可能会有所帮助。如果您需要帮助,请阅读这篇文章。
    http://support.microsoft.com/kb/257819

    【讨论】:

    • 通过 OLEDB 访问 Excel 有一些怪癖,但它比打开 Excel 对象并像那样操作它要快得多。
    【解决方案3】:

    一种可能性是将其设为两阶段过程,但这取决于瓶颈在哪里。

    如果是 Excel,则只需将记录集行转换为 CSV 类型的文件,然后在完成后创建 Excel 对象并将整个文件导入固定位置。

    这可能会比逐个单元的操作更快。

    如果您无法将 CSV 导入工作表上的固定位置(单元格不在连续的行或列中),我会将 CSV 导入新工作表,然后从那里批量复制到您的模板表。

    移动范围也应该比逐个单元格的操作更快。

    批量导入和批量复制应该会给您带来一些不错的改进。当您使用更复杂的 Excel 功能(考虑使用 =sum(a1..a999) 而不是在 VBA 中添加每个单独的单元格并将该值放在某处时,我有处理单个单元格的工作表,这些单元格的速度提高了 10 倍)。

    关于如何从 VBA 导入,我总是依靠 "Record Macro" 功能来获得可以修改的基线(对于那些我不熟悉的人)。这会将c:\x.csv 导入到C7 的当前工作表中:

    With ActiveSheet.QueryTables.Add(Connection:="TEXT;C:\x.csv", _
        Destination:= Range("C7"))
        .Name = "x"
        .FieldNames = True
        .RowNumbers = False
        .FillAdjacentFormulas = False
        .PreserveFormatting = True
        .RefreshOnFileOpen = False
        .RefreshStyle = xlInsertDeleteCells
        .SavePassword = False
        .SaveData = True
        .AdjustColumnWidth = True
        .RefreshPeriod = 0
        .TextFilePromptOnRefresh = False
        .TextFilePlatform = 850
        .TextFileStartRow = 1
        .TextFileParseType = xlDelimited
        .TextFileTextQualifier = xlTextQualifierDoubleQuote
        .TextFileConsecutiveDelimiter = False
        .TextFileTabDelimiter = True
        .TextFileSemicolonDelimiter = False
        .TextFileCommaDelimiter = True
        .TextFileSpaceDelimiter = False
        .TextFileColumnDataTypes = Array(1, 1, 1)
        .TextFileTrailingMinusNumbers = True
        .Refresh BackgroundQuery:=False
    End With
    

    现在我确信其中的大部分垃圾都可以删除,但明智的做法是一次删除一个,以确保不会出现问题。

    您还可以使用以下内容对其进行修改以使用不同的工作表。

    dim ws as worksheet
    dim savealert as boolean
    set ws = Sheets.Add
    ws.select
    ' Put all that other code above in here. '
    ' Move all that data just loaded into a real sheet. '
    savealert = Application.DisplayAlerts
    Application.DisplayAlerts = False
    ws.delete
    Application.DisplayAlerts = savealert
    

    【讨论】:

    • 如何以编程方式将 CSV 导入现有 Excel 工作簿的新页面?你能举个例子吗?
    【解决方案4】:

    您可以通过 OLEDB 连接访问它,而且速度无限快。

    这是我用来将数据从电子表格导入数据库的脚本中的一些代码。显然你会想改变游标类型和锁类型,但你明白了。

    strExcelConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & objFile.Path & ";Extended Properties=""Excel 8.0;HDR=Yes"""
    strSQL = "SELECT * FROM [RegistrationList$] ORDER BY DateToRegister DESC"
    
    objExcel.Open strSQL, strExcelConn, adOpenForwardOnly, adLockReadOnly, adCmdText
    

    【讨论】: