Words: 1018
Time to read: ~ 5 minutes
Tl;Dr: Make sure you’re calling the property, not just the variable i.e. $Var.ColumnName, not just $Var
Expert Opinion.
I had being sitting on this blog post for a while but then came a recent blog post by Mike Fal ( b | t ) that defended the use of Invoke-Sqlcmd
. Well, it turns out that Mike’s post was in response to Drew Furgiuele’s ( b | t ) blog post condeming it!
If that wasn’t bad enough, I then came across an article by Steven Swenson ( b | t ) that was in response to Mike’s article. Guess what? Another condemnation of Invoke-Sqlcmd
!
It seems that Invoke-Sqlcmd
is the Marmite of the PowerShell/SQL Server world. That’s the equivalent of the Crunchy Peanut Butter versus Smooth Peanut Butter debate for my American readers. (Hi Aunt Kate and Uncle Tom!)
Now if you want some real concise, knowledgeable, and professional opinions on the pros and cons of this command, I encourage you to check out those blog posts. I’ve linked to them and I’ve read them all, each with a blend of “oh yeah” and “huh, good point” comments thrown in.
Let’s Get Personal.
The reason that I wanted to throw in my thoughts in this debate is because, as much as I love Mike’s article, it doesn’t deal with the biggest problem that I had with Invoke-Sqlcmd
.
Dealing with those stupid, annoying System.Data.DataRow

I eventually figured out how to deal with these and wanted to pass the information on.
The Set Up.
For all those playing along at home, I’ve got a SQL Server 2016 Developer Edition with a copy of WideWorldImporters, as well as PowerShell version 5.
Let’s see how many customesr we have…
SELECT COUNT(*) FROM Sales.Customers;

Now I don’t know about you but when I query stuff in a SQL database, it’s to do something to/with the results. They could be a list of servers that I monitor, they could be a list of databases that I want to check the recovery model of, or it could be a list of tables that I want to see how much space they are using. The main point is that I want to do something with the results.
But for this simple case, I just want to list out the customer name from this table. Simple? Yes, but this is just a test case to prove a point.
So let’s PowerShell this!
And so our problems begin.
Now, the basic premise is this:
For each customer name, I just want to output the line “Currently working on” & the customer name.
Now this is based on a real world example where it was a list of servers and I wanted to include this in Write-Debug
.
Pain 1.
Invoke-Sqlcmd -ServerInstance localhost -Database WideWorldImporters -Query @" SELECT CustomerName AS Name FROM Sales.Customers; "@ | ForEach-Object { "Currently working on $_" }
Nice and simple PowerShell command, what I would call a “Ronseal” but when we run it…

I’m just going to follow this up with code and pictures of what I tried to do to get this to work…Hopefully you’ll get some amusement out of this…
Pain 2.
In this case I figured maybe I should put the results into a variable first and then see if it could work.
$Employees = Invoke-Sqlcmd -ServerInstance localhost -Database WideWorldImporters -Query @" SELECT CustomerName AS Name FROM Sales.Customers; "@ foreach ($employee in $Employees) { "Currently working on $employee" }

Pain 3.
Well I know that PowerShell arrays start at 0, and I know that I can get the count of elements in an array by using <variable>.count
so maybe that will work?
0..$Employees.Count | ForEach-Object { [int]$i = $_ $employeeRange = $Employees[$i] "Currently working on $employeeRange" }

Pain 4.
A quick check on Google points me to using ItemArray with my loops so I try that.
0..$Employees.Count | ForEach-Object { [int]$i = $_ $employeeRange = $Employees[$i].ItemArray "Currently working on $employeeRange" }

Ahhh! I know that PowerShell is 0 based but I didn’t realize that means the count is going to give me 1 extra row! Plus that’s a bit too much lines for my liking. All that just to output a customer name? Nah let me try again.
Pain 5.
for ($i = 0; $i -lt ($Employees.Count);, $i++) { $EmployeeFor = $Employees[$i].ItemArray "Currently working on $EmployeeFor" }

The Real Solution.
If only I had run this…
$Employees | Get-Member
You know, there’s a reason that they say the 3 best commands are Get-Help
, Get-Command
, and Get-Member.
It’s because they save so much time if you just look at them.
$Employees | Get-Member

As it turns out there is such an easier way to get the data values back from Invoke-Sqlcmd
,
if you want the data, just change $_ to $_.<property>
Let’s see if it works for us.
Pleasure 1.
If we “correct” our original code…
Invoke-Sqlcmd -ServerInstance localhost -Database WideWorldImporters -Query @" SELECT CustomerName AS Name FROM Sales.Customers; "@ | ForEach-Object { "Currently working on $($_.Name)" }

Pleasure 2.
And what about with variables?
foreach ($employee in $Employees.Name) { "Currently working on $employee" }

It looks like we finally have a proper Ronseal moment.
Final Thoughts.
I have absolutely no problem with Invoke-Sqlcmd
, so I suppose I fall into Mike’s side of the camp.
Do I use it all the time though? Not really.
The SMO objects have an amazing amount of information that is just too difficult to get with Invoke-Sqlcmd
so I’ve started to use the SMO more and more.
But Invoke-Sqlcmd
is a tool, just like everything else. There’s no point in throwing away a tool just because it isn’t the most optimal anymore, especially when it is so useful in adhoc situations.
There are some cases where a small handheld screwdriver is more useful than an electric one, just like there are some cases where Invoke-Sqlcmd
is more useful than the SMO objects.
Just know your tools…
Hey i got your point here! Beautiful as it is, i am not sure how to implement this to solve my issue.
Issue-
I have established an ODBC connection, run one query and stored the output in a datatable ($dt). But wen i try to do ‘ $sql4 = “SELECT $dt.ICU from $dt ” it just prints System.Data.datarow . Can you help me find how i can use a datatable in the select query. The datatable has various rows within.
THank you . I was able to solve my problem.
Thanks, this helped a bunch
F yes! Burned way too much time dealing with datarow and trying to get the data out; been using invoke-sqlcmd2, which fortunately these days has the ability to return in “usable” form, but I’ve got too much old code where I can’t use it. Many thanks!