Quantcast
Channel: The Official Scripting Guys Forum! forum
Viewing all articles
Browse latest Browse all 15028

SQL results -> PSObject; the right way

$
0
0

Hi

I've been writing a module that queries a table in SQL, it's not large, only a few hundred rows - but I don't want this to be a live view; a daily snapshot is fine in that the first time I run it per day, it refreshes the in-memory copy and then that's a persistent store. All my further commands, Get-ntserver etc run on the memory copy, the $script:ntservers variable.

What I found however, was that building that object takes up to 20 seconds for about 700 rows. Not a huge problem as I'm running it once per day. I had a look around for the "right" way to do this - the reason I want the data as proper objects is so I can pipeline them to other functions in the module

eg get-ntserver | where {$_.Role -eq 'CITRIX'} | get-serveruptime

get-ntserver just runs on the $script:ntservers variable, there's no further SQL lookup at this time beyond the daily refresh.

Currently, the object is being built with something like:

      $script:NTServers = New-Object -TypeName System.Collections.ArrayList
        $conn = New-Object System.Data.SqlClient.SqlConnection("(connection string here)")
        $conn.Open()
        $SelectCmd = "select * from ntserver where se_last_Seen > getdate() - 28"
        $adapter = new-object system.data.sqlclient.sqldataadapter ($selectcmd, $conn)
        $table = new-object system.data.datatable
        $adapter.Fill($table) | out-null
        $RowTemplate = New-Object -TypeName PSObject
        Write-Host "A few seconds to refresh the database...."
        for ($col = 0; $col -le $table.Columns.Count - 1; $col++) {
            $name = $table.Columns[$col].ColumnName.Replace("se_","")
            $RowTemplate | Add-Member Noteproperty $name -value 1
        }
        for ($row = 0; $row -le $table.Rows.Count - 1; $row++) {
                Write-Progress -activity "Refreshing database" -PercentComplete (($row / $table.rows.count) * 100)
                $obj = $RowTemplate.PSObject.Copy()
                for ($col = 0; $col -le $table.Columns.Count - 1; $col++) {
                    $Name = $table.Columns[$col].ColumnName.Replace("se_","")
                    $obj.$Name = (NullTrim $table.Rows[$row][$col])
            }
            $script:NTServers += $obj

        }

So basically, it's the old "loop round all the columns and rows" routine. That doesn't seem ideal...

For speed, I was quicker building an initial row object template, and then copying that. That works great - but I can't help but think there's a better way to this. I don't want to build the "where" clause into SQL - plan on still letting Powershell do the Where clause per my above example. I'm kinda intrigued about a best-practice for this - any suggestions? Also, it's dynamic, as the number of columns in the database will change from time to time, hence "select *".

I guess what I'm really looking for amounts to

$PSObject = SQLResultsToObject("select * from server")

$PSObject | Get-Member
ActiveBackup          NoteProperty System.String ActiveBackup=1
Address               NoteProperty System.String Address=
Asset                 NoteProperty System.String Asset=
AV_DefDate            NoteProperty System.DateTime AV_DefDate=28/07/2014 11:...

etc etc

Cheers

AW

Viewing all articles
Browse latest Browse all 15028

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>