Invoke-sqlcmd output without headers - arrays

I know this question has already been asked but the answers don't quite fit the constrains i have
So here we go again (sorry for that) :
I have this line in my PowerShell script :
$WITHOUTCLIENT = Invoke-Sqlcmd -h -1 -Database CM_00A -Query "Select Members.Name From CollectionMembers Members Join Collections Coll on Members.SiteID = Coll.SiteID Where Coll.CollectionName = '_SCCM-Machine Sans Client'"
The problem being that although when i count the number of items in this collection, the result is accurate (i.e. $WITHOUTCLIENT.count ) but when i try to display any item in this collection, it keeps on displaying a bloody header (i.e. $WITHOUTCLIENT[0] )
Note that i DON'T want to use an external file to store the result and that i should use Invoke-Sqlcmd, not Sqlcmd
i'm begging for help
Thank you

If you execute query:
$WITHOUTCLIENT = Invoke-Sqlcmd -h -1 -Database CM_00A -Query "Select Members.Name From CollectionMembers Members Join Collections Coll on Members.SiteID = Coll.SiteID Where Coll.CollectionName = '_SCCM-Machine Sans Client'"
You are receiving list of objects. Each object has one property, but it's still an object. To get 'flat' collection, use select with `-ExpandProperty' argument:
$WITHOUTCLIENT = $WITHOUTCLIENT | select -ExpandProperty Name

Related

Powershell invoke-sqlcmd Printing The Wrong Output

I have a SQL query that is running as expected however when I try to use it in PowerShell 'Invoke-SqlCmd' module, the output comes out different than when querying the database. I noticed that there are quite a few questions regarding this module but I couldn't find one that is applicable to my case.
Query:
$SQLServer = "localhost"
$query = "SELECT Groups.[Name] AS AGname FROM sys.dm_hadr_availability_group_states States INNER JOIN master.sys.availability_groups Groups ON States.group_id = Groups.group_id WHERE primary_replica = ##Servername"
$HAGName = Invoke-Sqlcmd -query $query -ServerInstance $SQLServer -Database 'database'
if ($HAGName = !$null) {
write-host "Availability group name is $HAGName"
exit 0
}
else {
write-host "Failed to retrieve High Availability group name = [$HAGName]"
exit 1
}
Output in PowerShell: 'Availability group name is True'
Like I mentioned, when querying SQL Server directly I get the correct output. I tried using the 'OutputAs' switch but it didn't help.
Any help will be greatly appreciated.
All the pointers are in the comments on the question, but let me break it down systematically:
!$null is always $true in PowerShell: ! / -not, the logical NOT operator coerces $null to a Boolean, and since [bool] $null is $false, ! $null is $true.
$HAGName = !$null, due to using =, the assignment operator, therefore assigns $true to variable $HAGName.
To instead perform an equality comparison, use -eq, the equality operator.
Therefore, $null -eq $HAGName is what you meant to use (placing the $null on the LHS, for robustness - see the docs).
However, given PowerShell's implicit to-Boolean coercion rules (see the bottom section of this answer), you could simplify to if ($HAGName) { ... } in this case.
Therefore, a more PowerShell-idiomatic reformulation of your code is:
$SQLServer = 'localhost'
$query = 'SELECT Groups.[Name] AS AGname FROM sys.dm_hadr_availability_group_states States INNER JOIN master.sys.availability_groups Groups ON States.group_id = Groups.group_id WHERE primary_replica = ##Servername'
$HAGName = Invoke-Sqlcmd -Query $query -ServerInstance $SQLServer -Database database
if ($HAGName) {
Write-Verbose -Verbose "Availability group name is: "
# Output the System.Data.DataRow instance as-is,
# which also results in proper for-display formatting.
# If you just want the value of the .AGname property (column), use
# $HAGName.AGname instead.
$HAGName
exit 0
}
else {
Write-Warning "Failed to retrieve High Availability group name."
exit 1
}
Note:
The success case implicitly outputs the result, to the success output stream.
Write-Host is typically the wrong tool to use, unless the intent is to write to the display only, bypassing the success output stream and with it the ability to send output to other commands, capture it in a variable, or redirect it to a file. To output a value, use it by itself; e.g., $value instead of Write-Host $value (or use Write-Output $value, though that is rarely needed); see this answer
I've used a Write-Verbose call (whose output is quiet by default, here I've used -Verbose to force it to show) to provide optional supplemental / status information.
$HAGName now (implicitly) outputs the [System.Data.DataRow] instance returned by the Invoke-SqlCmd call as-is, which also results in proper display formatting - such instances do not stringify meaningfully when used in an expandable (interpolating string); they unhelpfully stringify to their type name, i.e. to verbatim System.Data.DataRow.
However, if you access a specific property (column) of the row, its value may stringify meaningfully, depending on its data type; in your case: `"Availability group name is $($HAGName.AGname)"
To include the usual for-display formatting inside a string - use something like "Availability group name is $($HAGName | Out-String)"

Passing Powershell variable into a SQL INSERT

I'm quite new when it comes to Powershell and I'm not sure if my method is the best.
How can I pass Powershell variables into SQL using Invoke SQL or the .Net method? All I'm trying to do is to Insert data from the Get-AzureADGroup Display Name and Object Id into a SQL table.
$Groups = Get-AzureADGroup -All $true
foreach($Group in $Groups){
$DN = $Group.DisplayName
$ID = $Group.ObjectId
$insertquery="
INSERT INTO [dbo].[Table]([DsiplayName],[ObjectId])
VALUES(''$DN'',''$ID'')
"
Invoke-Sqlcmd -ServerInstance "Server" -Database "Database" -Query $insertquery
}
However, it comes up with the error 'Incorrect syntax near 'GroupDisplayName'. I've tried using " and ' interchangeably in the query with no luck. Am I missing something?

Invoke-Sqlcmd ran SQL script capture verbose output

I need to do health check on various servers on daily basis. I have SQL script which includes missing indexes, duplicate indexes, CPU and memory, recovery, failed job, last backup etc in one script.
I am running this script manually on sever and changing result to text (I have made script which use print statement so I can directly copy and paste result).
Now I want to run this script with PowerShell but I am not getting result to text format with row-column format.
Code:
$RESULT = (Invoke-Sqlcmd -InputFile $sqlscript -ServerInstance $server -Database $databse -Verbose 4>&1) |
Out-File $outfile
I am getting all the print statement but not the result in text file.
A more appropriate way to do this is as follows.
[string]$SqlsSript = '..\path\'
[string]$ConnectionString = '...'
$DataSet = Invoke-SqlCmd -ConnectionString $Connectionstring -InputFile $SqlScript -As DataSet
You can then explore the $DataSet collection, i.e
$DataSet.Tables[0].Rows
Which you can then transform / output to a file if you like.
Do ensure that your SqlScript returns a table, e.g. a table variable.

Powershell Concatenate Variables Result in System.Data.DataRow

I need to get Availability Group and Listener name, concatenate both for a list of servers and then use it to get resource cluster.
What I've done so far:
foreach ($cluster in GC "D:\TEST\Servers_List_TEST.txt")
{
$AGName = invoke-sqlcmd -Serverinstance $cluster -query "select left(name,25) as ' ' from sys.availability_groups"
$LNName = invoke-sqlcmd -Serverinstance $cluster -query "select left(dns_name,25) as ' ' from sys.availability_group_listeners"
$NetworkName = "$AGName_$LNName"
Get-ClusterResource -cluster $cluster -name $NetworkName | Get-ClusterParameter HostRecordTTL,RegisterAllProvidersIP }
The main issue is on $NetworkName. It's returning System.data.DataRow, instead of concatenating $AGName_$LNName ( underscore is necessary between both ).
Your main issue is that $AGName and its partner variable $LNName are System.Data.DataRow objects which is what Invoke-SQLcmd returns. They are not just simple strings. Since you are forcing them to strings PowerShell calls the ToString() method of those objects which, in this case, is just the object name.
You have also given that object a property of in your queries (which is just a space). The resulting object is using that property name.
You should add proper column names in your query but you will be able to pull out the relevant data by calling that property either way.
$NetworkName = "$($AGName." ")_$($LNName." ")"
So using subexpressions we have gotten the value of the property [space]

Using Powershell get Values from sql table

we know that we can use sql query window to get values from Database like "select * from....". Is there any way to get a value through powershell way. I found the way to database table itself but not to the values..
Ex:
Set-location SQLserver:\sql\default\localhost\databases\database\tables
Get-childitem
Above command gives me tables in the DB , but how can i get the values from it.
Most concise and straightforward option is Invoke-SqlCommand
$results = Invoke-Sqlcmd -ServerInstance "mySqlServer" -Database "myDatabase" -Query "SELECT * FROM MyTable"
You can use this
$connectionString = “Server=$dataSource;uid=$user; pwd=$pwd;Database=$database;Integrated Security=False;”
$connection = New-Object System.Data.SqlClient.SqlConnection
$connection.ConnectionString = $connectionString
$connection.Open()
$query = “SELECT * FROM Tab”
$command = $connection.CreateCommand()
$command.CommandText = $query
$result = $command.ExecuteReader()
The SQL Server Powershell provider uses SMO to expose the database object hierarchy. Once you get a SMO child item, you can invoke the corresponding SMO methods on the object.
The SMO Database ExecuteQueryWithResults method can be used to execute a query in the context of a given database. Get the desired database item and invoke this method to return a DataSet object containing the results. Then retrieve the data as desired from the DataSet.
Below is an example gleaned from the SMO reference ((https://msdn.microsoft.com/en-us/library/ms205775.aspx) that can be run from the context of the Databases node:
$db = Get-Item SomeDatabase
$dt = $db.ExecuteWithResults("SELECT * FROM sys.objects;")
$dt.Tables[0] | Format-Table
Do you means the powershell command like below:
$MyVar = New-Object Microsoft.SqlServer.Management.SMO.Table
$MyVar | Get-Member -Type Properties
For more information you could visit https://technet.microsoft.com/en-us/library/cc281939(v=sql.105).aspx
If you want to get the value of table,
//Connect to the local, default instance of SQL Server.
{
Server srv = default(Server);
srv = new Server();
//Call the EnumCollations method and return collation information to DataTable variable.
DataTable d = default(DataTable);
//Select the returned data into an array of DataRow.
d = srv.EnumCollations;
//Iterate through the rows and display collation details for the instance of SQL Server.
DataRow r = default(DataRow);
DataColumn c = default(DataColumn);
foreach ( r in d.Rows) {
Console.WriteLine("=====================================");
foreach ( c in r.Table.Columns) {
Console.WriteLine(c.ColumnName + " = " + r(c).ToString);
}
}
}
The above is how to do the job via C# but how to do that in powershell: You have got $MyVar values, and you run "$MyVar |Get-Member" is to show all method of all method available for $MyVar, I believe there should have some method allow you iterator all row or column for thr table. The powerShell even can invoke C# method, but this is the last choose.
There is now an sqlserver module available for powershell
You now have available the following two additional commands and options
Read-SqlTableData [[-ServerInstance] <String[]> ] [-ColumnName <String[]> ] [-ColumnOrder <String[]> ] [-ColumnOrderType <OrderType[]> ] [-ConnectionTimeout <Int32> ] [-Credential <PSCredential> ] [-DatabaseName <String> ] [-IgnoreProviderContext] [-OutputAs <OutputTypeSingleTable> {DataSet | DataTable | DataRows} ] [-SchemaName <String> ] [-SuppressProviderContextWarning] [-TableName <String> ] [-TopN <Int64> ] [ <CommonParameters>]
Write-SqlTableData [[-ServerInstance] <String[]> ] -InputData <PSObject> [-ConnectionTimeout <Int32> ] [-Credential <PSCredential> ] [-DatabaseName <String> ] [-Force] [-IgnoreProviderContext] [-Passthru] [-SchemaName <String> ] [-SuppressProviderContextWarning] [-TableName <String> ] [-Timeout <Int32> ] [ <CommonParameters>
You can use the AdoLib module in SQLPSX (SQLPSX.codeplex.com).
With that, you can use cmdlets and do things like:
invoke-sqlquery -sql "select * from tab" -server SQLServer01 -database MyDatabase

Resources