Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is writing Excel cell values fast in VBScript but slow in PowerShell?

Why is it that writing cell values to Excel is a lot faster in VBScript than in PowerShell? Isn't PowerShell the new thing, and VBScript the deprecated MS scripting language?

VBScript example (save to filename.vbs) This runs in a split second.

Set objExcel = CreateObject("Excel.Application")
objExcel.Visible = false
Set objWorkbook = objExcel.Workbooks.Add()

' Edit: increased number of writes to 500 to make speed difference more noticeable
For row = 1 To 500
     'Edit: using .cells(row,1) instead of .cells(50,1) - this was a mistake
     objWorkbook.workSheets(1).cells(row,1).value = "test"
Next

objWorkbook.SaveAs(CreateObject("Scripting.FileSystemObject").GetParentFolderName(WScript.ScriptFullName) & "\test.xlsx")
objExcel.Quit
msgbox "Done."

PowerShell example (save to filename.ps1) This takes multiple seconds to run (problematic on thousands of records)

#need this to work around bug if you use a non-US locale: http://support.microsoft.com/default.aspx?scid=kb;en-us;320369
[System.Threading.Thread]::CurrentThread.CurrentCulture = "en-US" 

$excel = New-Object -ComObject Excel.Application
$excel.Visible = $False
$xls_workbook = $excel.Workbooks.Add()

# Edit: using foreach instead of for
# Edit: increased number of writes to 500 to make speed difference more noticeable
foreach ($row in 1..500) {
    # Edit: Commented out print-line, slows down the script
    #"Row " + $row
    # This is very slow! - http://forums.redmondmag.com/forums/forum_posts.asp?tid=4037&pn=7
    $xls_workbook.sheets.item(1).cells.item($row,1) = "test"
}

$xls_workbook.SaveAs($MyInvocation.MyCommand.Definition.Replace($MyInvocation.MyCommand.Name, "") + "test.xlsx")
$excel.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($excel)

I want to use this for thousands of records. If there is no fast way to do this, PowerShell is not an option. Are there better alternatives?

like image 784
Wouter Avatar asked Oct 16 '12 15:10

Wouter


3 Answers

some things don't add up here:

your VBScript, writes on ONE cell over and over, while your PowerShell code writes into 100 cells

objWorkbook.workSheets(1).cells(50,1).value = "test"

$xls_workbook.sheets.item(1).cells.item($row,1) = "test"

you are executing "Row " + $row on PowerShell - this might offset comparison too.

If you want to write into multiple cells, you should think about using arrays and wrinting onto whole ranges, because this has better performance.

like image 27
Jook Avatar answered Nov 14 '22 03:11

Jook


You can speed things up by not looping through individual cells:

$excel = New-Object -ComObject Excel.Application
$excel.Visible = $True
$xls_workbook = $excel.Workbooks.Add()

$range = $xls_workbook.sheets.item(1).Range("A1:A100")
$range.Value2 = "test"

If you want to write an array of values to a range, here is a nice blog post that demonstrates similar technique:

How to Get Data into an Excel Spreadsheet Very Quickly with PowerShell

like image 192
Anonimista Avatar answered Nov 14 '22 01:11

Anonimista


You can shave a little time off the PowerShell version by eliminating the for loop test and using a foreach.

for ($row = 1; $row -le 100; $row++)

goes to:

foreach ($row in 1..100)

By doing this you eliminate the comparison and increment.

But aside from that, my observations match yours (see my comments on Jook's answer).

like image 1
alroc Avatar answered Nov 14 '22 01:11

alroc