Powershell and Excel - Datatable slow with too many rows - sql-server

I'm trying to fill an Excel sheet using powershell :
0. Declaring Excel object
$Excel = New-Object -ComObject Excel.Application
$Excel.DisplayAlerts = $false
$Excel.ScreenUpdating = $false
$Excel.DisplayStatusBar = $false
$Excel.EnableEvents = $false
$Excel.Visible = $False
1. Reading data from a database table :
$dt1 = New-Object System.Data.Dataset
2. Getting the table :
$dt_table1 = $dt1.Tables[0]
3. Filling Excel file :
for ([Int]$m = 0; $m -lt $dt_table1.Rows.Count; $m++)
{
for ([Int]$r = 0; $r -lt $dt_table1.Columns.Count; $r++)
{
$incolumn = $r + 1;
$inrow = $inheaderlenght + 2 + $m;
if($incolumn -gt 2)
{
$Workbook.ActiveSheet.Cells.Item($inrow, $incolumn) = [System.Convert]::ToDecimal($dt_table1.Rows[$m].ItemArray[$r])
}
else
{
$Workbook.ActiveSheet.Cells.Item($inrow, $incolumn) = $dt_table1.Rows[$m].ItemArray[$r].ToString()
}
}
}
With a few hundreds of rows the sheet is filling in seconds, the problem is when i got thousands of rows, is very slow, for example to fill 21.500 rows it need 15 min at least.
I'm executing this code in my production server, with 32GB of RAM and an Intel Xeon processor.
I would like to improve the performance, i need to fill an Excel file with 32 sheets and only few sheets have thousands of rows.
UPDATE: I wanted to fill directly an array into the Excel sheet :
$excelArray = New-Object 'object[,]' $dt_table1.Rows.Count, $dt_table1.Columns.Count
$excelArray = ForEach($Row in $dt1.Tables[0].Rows){
$Record = New-Object PSObject
ForEach($Col in $dt1.Tables[0].Columns.ColumnName){
Add-Member -InputObject $Record -NotePropertyName $Col -NotePropertyValue $Row.$Col
}
$Record
}
But now, the next line fails:
$range = $WorkSheet.Range('A1', ([char](64 + $dt_table1.Columns.Count)).ToString() + ($dt_table1.Rows.Count).ToString() )
$range.Value2 = $excelArray

Shamelessly using some of f6a4's answer, I think this could work:
# 3. Filling Excel file :
# convert to array of objects
$tableData = $dt_table1 | Select-Object * -ExcludeProperty ItemArray, Table, RowError, RowState, HasErrors
# select the range in the worksheet
$endRange = '{0}{1}' -f ([char](64 + $dt_table1.Columns.Count)), $dt_table1.Rows.Count
$range = $Workbook.ActiveSheet.Range('A1', $endRange)
# copy the array to excel range
$range.Value2 = $tableData

Ok, now i found solution.
$excelArray | ConvertTo-CSV -NoType -Del "`t" | Select -Skip 1 | Clip
[void]$WorkSheet.columns.Item(1).cells.Item(2).PasteSpecial()
From 15 min to 2 min. This really helps me a lot, i have to produce like 500+ Excel files.

Related

Powershell populate word table from an array

I have a PS script that will import a csv into several arrays and I need it to populate a table in word. I am able to get the data into the arrays, and create a table with headers and the correct number of rows, but cannot get the data from the arrays into the table. Doing lots of google searches led me to the following code. Any help is greatly appreciated.
Sample of My_File.txt
Number of rows will vary, but the header row is always there.
component,id,iType,
VCT,AD-1234,Story,
VCT,Ad-4567,DR,
$component = #()
$id = #()
$iType =#()
$vFile = Import-CSV ("H:\My_file.txt")
$word = New-Object -ComObject "Word.Application"
$vFile | ForEach-Object {
$component += $_.components
$id += $_.id
$iType +=_.iType
}
$template = $word.Documents.Open ("H:\Test.docx")
$template = $word.Document.Add()
$word.Visible = $True
$Number_rows = ($vFile.count +1)
$Number_cols = 3
$range = $template.range()
$template.Tables.add($range, $Number_rows, $Number_cols) | out-null
$table = $template.Tables.Item(1)
$table.cell(1,1).Range.Text = "Component"
$table.cell(1,2).Range.Text = "ID"
$table.cell(1,3).Range.text = "Type"
for ($i=0; $i -lt; $vFile.count+2, $i++){
$table.cell(($i+2),1).Range.Text = $component[$i].components
$table.cell(($i+2),2).Range.Text = $id[$i].id
$table.cell(($i+2),3).Range.Text = $iType[$i].iType
}
$Table.Style = "Medium Shading 1 - Accent 1"
$template.SaveAs("H:\New_Doc.docx")
Don't separate the rows in the parsed CSV object array into three arrays, but leave the collection as-is and use the data to fill the table using the properties of that object array directly.
I took the liberty of renaming your variable $vFile into $data as to me at least this is more descriptive of what is in there.
Try
$data = Import-Csv -Path "H:\My_file.txt"
$word = New-Object -ComObject "Word.Application"
$word.Visible = $True
$template = $word.Documents.Open("H:\Test.docx")
$Number_rows = $data.Count +1 # +1 for the header
$Number_cols = 3
$range = $template.Range()
[void]$template.Tables.Add($range, $Number_rows, $Number_cols)
$table = $template.Tables.Item(1)
$table.Style = "Medium Shading 1 - Accent 1"
# write the headers
$table.cell(1,1).Range.Text = "Component"
$table.cell(1,2).Range.Text = "ID"
$table.cell(1,3).Range.text = "Type"
# next, add the data rows
for ($i=0; $i -lt $data.Count; $i++){
$table.cell(($i+2),1).Range.Text = $data[$i].component
$table.cell(($i+2),2).Range.Text = $data[$i].id
$table.cell(($i+2),3).Range.Text = $data[$i].iType
}
$template.SaveAs("H:\New_Doc.docx")
When done, do not forget to close the document, quit word and clean up the used COM objects:
$template.Close()
$word.Quit()
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($template)
$null = [System.Runtime.Interopservices.Marshal]::ReleaseComObject($word)
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()

Powershell: Excel combine worksheets into a single worksheet

I have some web script that I've adapted to run 7 T-SQL queries and output the results into 1 Excel workbook, one worksheet per query. I've just been asked if I can combine all 7 worksheets into one.
Here's my sample code which does copy a worksheet, however the entire column(s) are selected instead of just the UsedData. Also, the first worksheet's data on the destination worksheet is replaced by the second worksheets data.
Questions: Would it be simpler to get Powershell to output the 7 queries into One Excel Worksheet separated by two blank rows? Or modify the existing Powershell script to create the 7 worksheets then combine them into one?
Code is not pretty! I also have been really lost using $Excel = New-Object -ComObject excel.application followed by $Excel | Get-Member to explore how to get PowerShell to work with Excel. References on MSDN are usually for VB or C languages and I can't translate that into PowerShell.
--Edit, add code that stores 7 Query results in an array and outputs to the console. The data is correct but I'm just unsure how to approach piping that data into a single Excel Worksheet.
$docs = "C:\Temp\SQL\test.xlsx"
If (Test-Path $docs){Remove-Item $docs}
Function First-Query {
param([string[]]$queries)
$xlsObj = New-Object -ComObject Excel.Application
$xlsObj.DisplayAlerts = $false
## - Create new Workbook and Sheet (Visible = 1 / 0 not visible)
$xlsObj.Visible = 0
$xlsWb = $xlsobj.Workbooks.Add(1)
$xlsSh = $xlsWb.Worksheets.Add([System.Reflection.Missing]::Value, $xlsWb.Worksheets.Item($xlsWb.Worksheets.Coun))
$xlsSh.Name = 'Test'
for ($i = 0; $i -lt $queries.Count; $i++){
$query = $queries[$i]
$SQLServer = 'Server'
$Database = 'DataBase'
## - Connect to SQL Server using non-SMO class 'System.Data':
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection
$SqlConnection.ConnectionString = "Server = $SQLServer; Database = $Database; Integrated Security = True"
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand
$SqlCmd.CommandText = $query
$SqlCmd.Connection = $SqlConnection
## - Extract and build the SQL data object '$DataSetTable':
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$SqlAdapter.SelectCommand = $SqlCmd;
$tables = New-Object System.Data.DataSet;
$SqlAdapter.Fill($tables)
$TableArray = #($tables)
$SqlConnection.Close()
$DataSetTable = $TableArray.Tables[0]
}#End For Loop
## - Build the Excel column heading:
[Array] $getColumnNames = $DataSetTable.Columns | Select ColumnName;
## - Build column header:
[Int] $RowHeader = 1;
foreach ($ColH in $getColumnNames){
$xlsSh.Cells.item(1, $RowHeader).font.bold = $true;
$xlsSh.Cells.item(1, $RowHeader) = $ColH.ColumnName;
$RowHeader++;
}
## - Adding the data start in row 2 column 1:
[Int] $rowData = 2;
[Int] $colData = 1;
foreach ($rec in $DataSetTable.Rows){
foreach ($Coln in $getColumnNames){
## - Next line convert cell to be text only:
$xlsSh.Cells.NumberFormat = "#";
## - Populating columns:
$xlsSh.Cells.Item($rowData, $colData) = `
$rec.$($Coln.ColumnName).ToString()
$ColData++
}
$rowData++; $ColData = 1
}
## - Adjusting columns in the Excel sheet:
$xlsRng = $xlsSH.usedRange
$xlsRng.EntireColumn.AutoFit() | Out-Null
#End for loop.
#Delete unwanted Sheet1.
$xlsWb.Sheets.Item('Sheet1').Delete()
#Set Monday to Active Sheet upon opening Workbook.
$xlsWb.Sheets.Item('Monday').Activate()
## ---------- Saving file and Terminating Excel Application ---------- ##
$xlsFile = "C:\Temp\SQL\test.xlsx"
$xlsObj.ActiveWorkbook.SaveAs($xlsFile) | Out-Null
$xlsObj.Quit()
## - End of Script - ##
start-sleep 2
While ([System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlsRng)) {'cleanup xlsRng'}
While ([System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlsSh)) {'cleanup xlsSh'}
While ([System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlsWb)) {'cleanup xlsWb'}
While ([System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlsObj)) {'cleanup xlsObj'}
[gc]::collect() | Out-Null
[gc]::WaitForPendingFinalizers() | Out-Null
}#End Function
$queries = #()
$queries += #'
SELECT DISTINCT
'#
First-Query -queries $queries
Not sure I really understand what your problem is but below is a "template" that might help you to do what you want. It shows you how you can create sheets and handle them. You'll have to fill the blanks (see the commented section, where you have to call your query function).
param (
[string] $ExcelFile = (Read-Host "Enter full path for Excel file")
)
try
{
$Error.Clear()
# http://support.microsoft.com/kb/320369
[System.Threading.Thread]::CurrentThread.CurrentCulture = [System.Globalization.CultureInfo] "en-US"
Push-Location
$scriptPath = Split-Path -parent $MyInvocation.MyCommand.Path
Set-Location $scriptPath
$Excel = New-Object -comobject Excel.Application
$Excel.Visible = $True
$WorksheetCount = 7
$Workbook = $Excel.Workbooks.Add()
$Workbook.Title = 'My Workbook'
$weekdays = #("Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday")
($WorksheetCount - 1)..0 | %{
$sheet = $Excel.Worksheets.Add()
$sheet.Name = $weekdays[$_]
# $dataTable = Execute-Your-Query-Here-Returning-A-Data-Table
# $x = 0
# $dataTable | %{
# $sheet.cells.item($x, 1) = ...
# $sheet.cells.item($x, 2) = ...
# $x++
# }
}
$excel.ActiveWorkbook.SaveAs("$ExcelFile")
}
catch
{
"$($MyInvocation.InvocationName): $Error"
}
finally
{
Pop-Location
$Excel.Quit()
$Excel = $null
[gc]::collect()
[gc]::WaitForPendingFinalizers()
}

Powershell function that accepts multidimensional arrays as parameters

I'm trying to take a web script and make it a function that accepts an array of strings as arguments. Currently I work around this by making two functions from the same script. Then I use My-Function1 -arr $1 -arr2 $2 and after that use My-Function2 -arr $1 -arr2 $2 to get around the array problem.
There has to be a much cleaner method for passing this type of data to only one function but in my searching I have not see anything. I also saw a question on using the Tables refresh method to run a new query but with my limited scripting experience, I wasn't sure how to use that with my script.
The reason for two functions is: 1) First query runs and the results are inserted into a New Excel WorkBook and Worksheet. 2) Next the 2nd function is called to run the same T-SQL query however this time, Open workbook from #1 then insert a new named worksheet.
Original script from http://www.maxtblog.com/2014/06/powershell-extracting-sql-server-data-into-excel/
$docs = "D:\Scripts\Reboots2.xlsx"
If (Test-Path $docs){Remove-Item "D:\Scripts\Reboots2.xlsx"} Else {Continue}
Function First-Query {
param([string[]]$arr,$arr2)
### SQL query results sent to Excel
$SQLServer = 'SERVERNAME'
$Database = 'DATABASENAME'
## - Connect to SQL Server using non-SMO class 'System.Data':
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection;
$SqlConnection.ConnectionString = "Server = $SQLServer; Database = $Database; Integrated Security = True";
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand;
$SqlCmd.CommandText = $arr;
$SqlCmd.Connection = $SqlConnection;
## - Extract and build the SQL data object '$DataSetTable':
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter;
$SqlAdapter.SelectCommand = $SqlCmd;
$DataSet = New-Object System.Data.DataSet;
$SqlAdapter.Fill($DataSet);
$SqlConnection.Close()
$DataSetTable = $DataSet.Tables["Table"];
## - Create an Excel Application instance:
$xlsObj = New-Object -ComObject Excel.Application;
## - Create new Workbook and Sheet (Visible = 1 / 0 not visible)
$xlsObj.Visible = 0;
$xlsWb = $xlsobj.Workbooks.Add(1);
$xlsSh = $xlsWb.Worksheets.item(1);
$xlsSh.Name = $($arr2)
## - Build the Excel column heading:
[Array] $getColumnNames = $DataSetTable.Columns | Select ColumnName;
## - Build column header:
[Int] $RowHeader = 1;
foreach ($ColH in $getColumnNames)
{
$xlsSh.Cells.item(1, $RowHeader).font.bold = $true;
$xlsSh.Cells.item(1, $RowHeader) = $ColH.ColumnName;
$RowHeader++;
};
## - Adding the data start in row 2 column 1:
[Int] $rowData = 2;
[Int] $colData = 1;
foreach ($rec in $DataSetTable.Rows)
{
foreach ($Coln in $getColumnNames)
{
## - Next line convert cell to be text only:
$xlsSh.Cells.NumberFormat = "#";
## - Populating columns:
$xlsSh.Cells.Item($rowData, $colData) = $rec.$($Coln.ColumnName).ToString();
$ColData++;
};
$rowData++; $ColData = 1;
};
## - Adjusting columns in the Excel sheet:
$xlsRng = $xlsSH.usedRange;
$xlsRng.EntireColumn.AutoFit() | Out-Null
## ---------- Saving file and Terminating Excel Application ---------- ##
$xlsFile = "D:\Scripts\Reboots.xlsx"
$xlsObj.ActiveWorkbook.SaveAs($xlsFile) | Out-Null
$xlsObj.Quit()
## - End of Script - ##
While([System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlsObj)){Remove-Variable xlsObj}
start-sleep 1
}#End Function
Function Rest-Query {
param([string[]]$arr,$arr2)
### SQL query results sent to Excel
$SQLServer = 'SERVERNAME'
$Database = 'DATABASENAME'
## - Connect to SQL Server using non-SMO class 'System.Data':
$SqlConnection = New-Object System.Data.SqlClient.SqlConnection;
$SqlConnection.ConnectionString = "Server = $SQLServer; Database = $Database; Integrated Security = True";
$SqlCmd = New-Object System.Data.SqlClient.SqlCommand;
$SqlCmd.CommandText = $arr;
$SqlCmd.Connection = $SqlConnection;
## - Extract and build the SQL data object '$DataSetTable':
$SqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter;
$SqlAdapter.SelectCommand = $SqlCmd;
$DataSet = New-Object System.Data.DataSet;
$SqlAdapter.Fill($DataSet);
$SqlConnection.Close()
$DataSetTable = $DataSet.Tables["Table"];
## - Create an Excel Application instance:
$xlsObj = New-Object -ComObject Excel.Application;
## - Create new Workbook and Sheet (Visible = 1 / 0 not visible)
$xlsObj.Visible = 0;
$xlsWb = $xlsObj.Workbooks.Open("D:\Scripts\Reboots.xlsx")
$xlsSh = $xlsWb.Worksheets.Add([System.Reflection.Missing]::Value, $xlsWb.Worksheets.Item($xlsWb.Worksheets.Count))
$xlsSh.Name = $($arr2)
$xlsObj.DisplayAlerts = $false
## - Build the Excel column heading:
[Array] $getColumnNames = $DataSetTable.Columns | Select ColumnName;
## - Build column header:
[Int] $RowHeader = 1;
foreach ($ColH in $getColumnNames)
{
$xlsSh.Cells.item(1, $RowHeader).font.bold = $true;
$xlsSh.Cells.item(1, $RowHeader) = $ColH.ColumnName;
$RowHeader++;
};
## - Adding the data start in row 2 column 1:
[Int] $rowData = 2;
[Int] $colData = 1;
foreach ($rec in $DataSetTable.Rows)
{
foreach ($Coln in $getColumnNames)
{
## - Next line convert cell to be text only:
$xlsSh.Cells.NumberFormat = "#";
## - Populating columns:
$xlsSh.Cells.Item($rowData, $colData) = $rec.$($Coln.ColumnName).ToString();
$ColData++;
};
$rowData++; $ColData = 1;
};
## - Adjusting columns in the Excel sheet:
$xlsRng = $xlsSH.usedRange;
$xlsRng.EntireColumn.AutoFit() | Out-Null
## ---------- Saving file and Terminating Excel Application ---------- ##
$xlsFile = "D:\Scripts\Reboots.xlsx"
$xlsObj.ActiveWorkbook.SaveAs($xlsFile) | Out-Null
$xlsObj.Quit()
$xlsObj.DisplayAlerts = $true
While([System.Runtime.Interopservices.Marshal]::ReleaseComObject($xlsObj)){Remove-Variable xlsObj}
[gc]::collect()
[gc]::WaitForPendingFinalizers()
start-sleep 1
}#End Function2
$a = #'
SELECT DISTINCT
'#
$b = #'
SELECT DISTINCT
'#
$c = #'
SELECT DISTINCT
'#
$d = #'
SELECT DISTINCT
'#
$e = #'
SELECT DISTINCT
'#
$f = #'
SELECT DISTINCT
'#
$g = #'
SELECT DISTINCT
'#
First-Query -arr $a -arr2 Monday
Rest-Query -arr $b -arr2 Tuesday
Rest-Query -arr $c -arr2 Wednesday
Rest-Query -arr $d -arr2 Thursday
Rest-Query -arr $e -arr2 Friday
Rest-Query -arr $f -arr2 Saturday
Rest-Query -arr $g -arr2 Sunday
You can indeed write a single function for what you wanted to do, however you need to rearrange your code and add some structure to it.
I have tried to give some structure to the example below which you will need to adapt to your script.
Basically, and from what I could understand from the code you posted, you are running a specific SQL query for each week day and want to save the results in Excel.
In the example below I'm using a for loop to ensure the index used in each array is the same.
In the comments are the commands you need to add from your code.
Function First-Query {
param([string[]]$arr,[string[]]$arr2)
#initialize Excel
# do your Excel commands like opening the file, adding the workbook
#Now perform the query by using the appropriate element in each array (1 query / day)
for ($i = 0; $i -lt $arr.Count; $i++)
{
$day = $arr2[$i]
$query = $arr[$i]
# then add the worksheet
$xlsSh.Name = $day
# run the query
$SqlCmd.CommandText = $query;
# Save the results in the Excel columns
}
# Save and Quit
}#End Function
$arr = #()
$arr += "SELECT DISTINCT"
$arr += "ANOTHER QUERY SELECT DISTINCT"
# additional queries added with $arr +=
$weekdays = #("Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday")
First-Query -arr $arr -arr2 $weekdays

Powershell Function import multiple CSVs into Excel workbook

Ok, time to start this question over. I found the following script http://blogs.technet.com/b/heyscriptingguy/archive/2010/09/09/copy-csv-columns-to-an-excel-spreadsheet-by-using-powershell.aspx
My question is how would I get powershell to loop that script for any number of CSV files. The speed of the script is not important. I've received some answers of which, in some way, have their problems. Using the Technet script provides the correct output.
I have come up with this but am having a tough time getting the code to loop through multiple CSV files.
Function Excel-Stuff {
[cmdletBinding()]
Param([Parameter(ValueFromPipeline=$true)][string]$junk)
$excel.cells.item(1,1) = "Server"
$excel.cells.item(1,2) = "Rack"
$excel.cells.item(1,3) = "Environment"
$excel.cells.item(1,4) = "RebootTime"
$excel.cells.item(1,5) = "Schedule"
$i = 2
$processes = Import-Csv 'C:\Monday.csv'
foreach ($process in $processes){
$excel.cells.item($i,1) = $process.Server
$excel.cells.item($i,2) = $process.Rack
$excel.cells.item($i,3) = $process.Environment
$excel.cells.item($i,4) = $process.RebootTime
$excel.cells.item($i,5) = $process.Schedule
$i++
} #end foreach process
$autofit = $Global:worksheet.UsedRange
$autofit.EntireColumn.AutoFit() | Out-Null
}#End Function.
$Excel = New-Object -ComObject excel.application
$workbook = $Excel.workbooks.add(1)
$Global:worksheet = $workbook.WorkSheets.Item(1)
$Global:worksheet.Name='Monday'
Excel-Stuff
$Excel.visible = $True
Ok, so let's not do this the hard way. Copy/paste is your friend, and you can do it easily here.
You have Monday-Friday tabs, and I assume 7 CSV files. Loop this thing 7 times, and in each loop Create a tab, name it, then copy the entire CSV, convert it to a tab delimited CSV, and pipe it to CLIP.exe. Then just select A1 on the current sheet and paste. Start the next loop with the next CSV file.
$path = "c:\tmp\mytest.xlsx"
$day = #("Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday")
$Excel = New-Object -ComObject excel.application
$Excel.visible = $false
$workbook = $Excel.workbooks.add(1)
$day | %{
$processes = Import-Csv -Path "D:\Scripts\work\$_.csv"
$worksheet = $workbook.WorkSheets.add()
$processes | ConvertTo-Csv -Delimiter "`t" -NoTypeInformation | Clip.exe
$worksheet.select()
$worksheet.Name = $_
[void]$Excel.ActiveSheet.Range("A1:A1").Select()
[void]$Excel.ActiveCell.PasteSpecial()
[void]$worksheet.UsedRange.EntireColumn.AutoFit()
}
#Clean up extra sheets
$Excel.DisplayAlerts = $false
$workbook.Worksheets|?{$day -notcontains $_.name}|%{$_.Delete()}
$Excel.DisplayAlerts = $true
[void]$Excel.ActiveSheet.Range("A1:A1").Select()
$workbook.saveas($path)
$Excel.Quit()
Remove-Variable -Name excel
[gc]::collect()
[gc]::WaitForPendingFinalizers()
Then your only concern is that the CSV files are in the right order. If you named your CSV files Monday.csv, Tuesday.csv etc this could probably be simpler, but so long as they're named so that Monday's is the first, and it goes in alpha-numeric order you'll be just fine with that code.
(Edited to reflect comment suggestions for single cell selection before saving and changed -notin to -notcontains)

Powershell | Combining multiple Arrays and compare them to a table

I have encountered a new problem and I don't even know where exactly to start explaining. I will try my best, if something is unclear just ask me please.
I have an Excel workbook with informations (multiple rows) about DNS records - pretty similar to the powershell DNS syntax. e.g:
HostName RecordType TimeStamp TimeToLive RecordData
# A 0 00:05:00 127.0.0.1
I read them as arrays with the following little code - not very fast, but it works!:
#Read Excel
$row = [int]2
do {
if ($Sheet4.Cells.Item($Row,1).Text) {$ZoneName += $Sheet4.Cells.Item($Row,1).Text}
$HostName += $Sheet4.Cells.Item($Row,2).Text
$RecordType += $Sheet4.Cells.Item($Row,3).Text
$TimeStamp += $Sheet4.Cells.Item($Row,4).Text
$TimeToLive += $Sheet4.Cells.Item($Row,5).Text
$RecordData += $Sheet4.Cells.Item($Row,6).Text
$row = $row + [int] 1
} until (!$Sheet4.Cless.Item($row,2))
Now I have 6 arrays all stuck with information in different arrays, but all with the same amount of lines.
And now the tricky (atleast for me!) part:
I would like to stuff those 6 arrays into some special array I do not know, or in some sort of table I do not know how to create.
Why?
Because I want to compare those lines to this code ($Records to be specific):
$ZoneNames = (Get-DnsServerZone -ComputerName $DnsServer).zonename
$ZoneNames | foreach {$Records = (Get-DnsServerResourceRecord -ComputerName $DnsServer -ZoneName $_)}
$Records[0] would show me this (e.g.):
HostName RecordType Timestamp TimeToLive RecordData
-------- ---------- --------- ---------- ----------
# A 0 00:05:00 127.0.0.1
BUT: If I go deeper: $Records[0].RecordData:
IPv4Address PSComputerName
----------- --------------
127.0.0.1
So I would need to recreate this (above) sort of hierarchy to compare them (If I am right?).
I have tried it with a table like this (didn't work):
#Create Table object
$table = New-Object system.Data.DataTable “$ExcelRecords”
#Define Columns
$col2 = New-Object system.Data.DataColumn HostName,([string])
$col3 = New-Object system.Data.DataColumn RecordType,([string])
$col4 = New-Object system.Data.DataColumn TimeStamp,([string])
$col5 = New-Object system.Data.DataColumn TimeToLive,([string])
$col6 = New-Object system.Data.DataColumn RecordData,([string])
#Add the Columns
$table.columns.add($col2)
$table.columns.add($col3)
$table.columns.add($col4)
$table.columns.add($col5)
$table.columns.add($col6)
#Create a row
$r = $table.NewRow()
#Enter data in the row
$r.HostName = $HostName[$counter]
$r.RecordType = $RecordType[$counter]
$r.TimeStamp = $TimeStamp[$counter]
$r.TimeToLive = $TimeToLive[$counter]
$r.RecordData = $RecordData[$counter]
$RecordData
#Add the row to the table
$table.Rows.Add($r)
Tried comparing like this (didn't work):
if ($records[0] -like $table[0]) {write-host "works"}
This did work:
if ($records[0].hostname -like $table[0].hostname) {write-host "works"}
works
This did not (I guess this is the root of my problems):
if ($Records[0].RecordData -like $table[0].RecordData) {write-host "works"}
My main objective:
Check if there are Records on the DNS-Server, which aren't stated in the Excel sheet and delete them from the DNS-Server!
If you read through all the text, thanks for doing that! Appreciate every help.
Thanks in advance!
I'd start by creating an array of PS objects from your spreadsheet data.
#Read Excel
$row = [int]2
$DNS_Records =
do {
if ($Sheet4.Cells.Item($Row,1).Text) {
New-Object PSObject -Property #{
ZoneName = $Sheet4.Cells.Item($Row,1).Text
HostName = $Sheet4.Cells.Item($Row,2).Text
RecordType = $Sheet4.Cells.Item($Row,3).Text
TimeStamp = $Sheet4.Cells.Item($Row,4).Text
TimeToLive = $Sheet4.Cells.Item($Row,5).Text
RecordData = $Sheet4.Cells.Item($Row,6).Text
}
}
} until (!$Sheet4.Cless.Item($row,2))

Resources