public/Invoke-SqlQuery.ps1
Function Invoke-SqlQuery { <# .SYNOPSIS Executes a SQL query. .DESCRIPTION Executes a SQL query and returns the result in a format for easy manipulation afterwards. This function is not intended for DDL statements. .EXAMPLE Invoke-SqlQuery -ServerInstance '.' -Database master -Query 'select * from sys.tables FOR JSON PATH' -QueryResultFormat JSON Invoke-SqlQuery -ServerInstance '.' -Database master -Query "select * from sys.tables FOR XML AUTO, ROOT('MyRoot')" -QueryResultFormat XML Invoke-SqlQuery -ServerInstance '.' -Database master -Query "select count(*) from sys.tables " -QueryResultFormat Scalar Invoke-SqlQuery -ServerInstance '.' -Database master -Query "select * from sys.tables " -QueryResultFormat Rows | Format-Table -AutoSize .PARAMETER Query The SQL query to execute. .PARAMETER ServerInstance The SQL server instance to execute the query against. .PARAMETER Database The SQL datanase to execute the query in. .PARAMETER QueryTimeout A timeout for the query. .PARAMETER QueryResultFormat Specifies the expected output of the query. .INPUTS TO DO .OUTPUTS Scalar: returns the scalar result, and keeps the datatype returned from the query. JSON: returns the result as a json object. XML: returns the result as a xml object. Rows: Returns each row as a pscustomobject .LINK https://github.com/DennisWagner/SQLServerDevOpsTools .NOTES Written by (c) Dennis Wagner Kristensen, 2021 https://github.com/DennisWagner/SQLServerDevOpsTools This PowerShell script is released under the MIT license http://www.opensource.org/licenses/MIT #> [CmdletBinding()] Param ( [Parameter(Mandatory=$true)]$Query, [Parameter(Mandatory=$true)]$ServerInstance, [Parameter(Mandatory=$true)]$Database, [Parameter(Mandatory=$false)]$QueryTimeout, [Parameter(Mandatory=$false)] [ValidateSet("Rows","Scalar","JSON","XML")] $QueryResultFormat = "Rows" ) BEGIN { } PROCESS { $DataTable = New-Object System.Data.DataTable $Connection = New-Object System.Data.SQLClient.SQLConnection $Connection.ConnectionString = "server='$ServerInstance';database='$Database';trusted_connection=true;" $Connection.Open() $Command = New-Object System.Data.SQLClient.SQLCommand $Command.Connection = $Connection $Command.CommandText = $Query If ($QueryTimeout) { $Command.CommandTimeout = $QueryTimeout } $Reader = $Command.ExecuteReader() If (($QueryResultFormat -eq "JSON") -or ($QueryResultFormat -eq "XML")) { $DataTable.Load($Reader) If ($DataTable.Columns.Count -gt 1) { Throw "Query results of format: $QueryResultFormat may only return a single column. Number of columns returned: $($DataTable.Columns.Count)" } # When using for xml or for json, the ExecuteReader splits the result into multiple records of 2033 characters # https://docs.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.executereader?view=dotnet-plat-ext-5.0 $MergedString = "" ForEach ($record in $DataTable.Rows) { $MergedString += $record.ItemArray[0].ToString() } If ($QueryResultFormat -eq "JSON" ) { Try { $Json = ConvertFrom-Json $MergedString } Catch { Throw "The returned result did not contain valid JSON." } Write-Output $Json } ElseIf ($QueryResultFormat -eq "XML" ) { Try { $Xml = [xml]$MergedString } Catch { Throw "The returned result did not contain valid XML. Perhaps it's missing a root element? Error message: $($_.Exception.Message)" } Write-Output $Xml } } ElseIf ($QueryResultFormat -eq "Scalar" ) { $DataTable.Load($Reader) # validate result If ($DataTable.Rows.Count -gt 1) { Throw "Query results of format: $QueryResultFormat may only return a single row. Number of rows returned: $($DataTable.Rows.Count)" } ElseIf ($DataTable.Columns.Count -gt 1) { Throw "Query results of format: $QueryResultFormat may only return a single column. Number of columns returned: $($DataTable.Columns.Count)" } $Scalar = $DataTable.Rows[0].ItemArray[0] Write-Output $Scalar } ElseIf ($QueryResultFormat -eq "Rows" ) { # do not use the easier data table, to prevent looping through the rows twice - marginally faste than using a data table While ($Reader.Read()) { # convert each row to a pscustomobject - use an ordered hashtable to preserve the column order $RowObject = [ordered]@{} For ($i = 0; $i -lt $Reader.FieldCount; $i++) { $ColumnName = $Reader.GetName($i) $RowObject.add($ColumnName, $Reader.GetValue($i)) } Write-Output (New-Object -TypeName PSOBJECT -Property $RowObject ) } } } END { $Connection.Close() } } |