functions/Invoke-SqlQueries.ps1

function Invoke-DBScalarQuery {
    <#
    .SYNOPSIS
        Executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored.
 
    .DESCRIPTION
        Executes the query, and returns the first column of the first row in the result set returned by the query. Additional columns or rows are ignored.
 
    .PARAMETER conn
        The sql server connection to use when creating the command.
 
    .PARAMETER sql
        The sql to use for the query.
 
    .PARAMETER parameters
        An array of sql parameters to use for the query. Can be created using New-DBSqlParameter.
 
    .PARAMETER timeout
        The command timeout for the query in seconds.
 
    .OUTPUTS
        The results of the query.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)]
        [System.Data.SqlClient.SqlConnection]$conn,
        [Parameter(Mandatory=$true)]
        [string]$sql,
        [System.Data.CommandType]$CommandType = [System.Data.CommandType]::Text,
        [System.Data.SqlClient.SqlParameter[]]$parameters,
        [int]$timeout=30
    )

    process {
        try {
            $cmd = New-Object system.Data.SqlClient.SqlCommand($sql,$conn)
            $cmd.CommandType = $CommandType
            $cmd.CommandTimeout=$timeout
            foreach($p in $parameters){
                $cmd.Parameters.Add($p) | Out-Null
            }
            return $cmd.ExecuteScalar()
        } finally {
            if ($cmd) {
                $cmd.Dispose();
            }
        }
    }
}

function Invoke-DBNonQuery {
    <#
    .SYNOPSIS
        Executes a Transact-SQL statement against the connection and returns the number of rows affected.
 
    .DESCRIPTION
        Executes a Transact-SQL statement against the connection and returns the number of rows affected.
 
    .PARAMETER conn
        The sql server connection to use when creating the command.
 
    .PARAMETER sql
        The sql to use for the query.
 
    .PARAMETER parameters
        An array of sql parameters to use for the query. Can be created using New-DBSqlParameter.
 
    .PARAMETER timeout
        The command timeout for the query in seconds.
 
    .OUTPUTS
        The results of the query.
    #>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true)]
        [System.Data.SqlClient.SqlConnection]$conn,
        [Parameter(Mandatory=$true)]
        [string]$sql,
        [System.Data.CommandType]$CommandType = [System.Data.CommandType]::Text,
        [System.Data.SqlClient.SqlParameter[]]$parameters,
        [int]$timeout=30
    )

    process {
        try {
            $cmd = New-Object system.Data.SqlClient.SqlCommand($sql, $conn)
            $cmd.CommandType = $CommandType
            $cmd.CommandTimeout = $timeout
            foreach($p in $parameters){
                $cmd.Parameters.Add($p) | Out-Null
            }
            return $cmd.ExecuteNonQuery()
        } finally {
            if ($cmd) {
                $cmd.Dispose();
            }
        }
    }
}

function Invoke-DBReaderQuery {
    <#
    .SYNOPSIS
        Sends the CommandText to the Connection and builds a SqlDataReader.
 
    .DESCRIPTION
        Sends the CommandText to the Connection and builds a SqlDataReader.
 
    .PARAMETER conn
        The sql server connection to use when creating the command.
 
    .PARAMETER sql
        The sql to use for the query.
 
    .PARAMETER parameters
        An array of sql parameters to use for the query. Can be created using New-DBSqlParameter.
 
    .PARAMETER timeout
        The command timeout for the query in seconds.
 
    .OUTPUTS
        The SqlDataReader.
    #>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseOutputTypeCorrectly', '', Justification='Correct type')]
    [CmdletBinding()]
    [OutputType([System.Data.SqlClient.SqlDataReader])]
    param (
        [Parameter(Mandatory=$true)]
        [System.Data.SqlClient.SqlConnection]$conn,
        [Parameter(Mandatory=$true)]
        [string]$sql,
        [System.Data.CommandType]$CommandType = [System.Data.CommandType]::Text,
        [System.Data.SqlClient.SqlParameter[]]$parameters,
        [int]$timeout=30
    )

    process {
        try {
            $cmd = New-Object system.Data.SqlClient.SqlCommand($sql,$conn)
            $cmd.CommandType = $CommandType
            $cmd.CommandTimeout = $timeout
            foreach($p in $parameters){
                $cmd.Parameters.Add($p) | Out-Null
            }
            [System.Data.SqlClient.SqlDataReader]$reader = $cmd.ExecuteReader()
            # the comma before the reader object is on purpose to force powershell to return this object AS IS
            return ,$reader
        } finally {
            if ($cmd) {
                $cmd.Dispose();
            }
        }
    }
}

function Invoke-DBDataTableQuery {
    <#
    .SYNOPSIS
        Sends the CommandText to the Connection and builds a DataTable.
 
    .DESCRIPTION
        Sends the CommandText to the Connection and builds a DataTable.
 
    .PARAMETER conn
        The sql server connection to use when creating the command.
 
    .PARAMETER sql
        The sql to use for the query.
 
    .PARAMETER parameters
        An array of sql parameters to use for the query. Can be created using New-DBSqlParameter.
 
    .PARAMETER timeout
        The command timeout for the query in seconds.
 
    .OUTPUTS
        The DataTable.
    #>

    [CmdletBinding()]
    [OutputType([System.Data.DataTable])]
    param (
        [Parameter(Mandatory=$true)]
        [System.Data.SqlClient.SqlConnection]$conn,
        [Parameter(Mandatory=$true)]
        [string]$sql,
        [System.Data.CommandType]$CommandType = [System.Data.CommandType]::Text,
        [System.Data.SqlClient.SqlParameter[]]$parameters,
        [int]$timeout=30
    )

    process {
        try {
            $reader = Invoke-DBReaderQuery -conn $conn -sql $sql -CommandType $CommandType -parameters $parameters -timeout $timeout
            $table = New-Object System.Data.DataTable
            $table.Load($reader)
            return $table
        } finally {
            if ($reader) {
                $reader.Dispose();
            }
        }
    }
}

function Invoke-DBDataSetQuery {
    <#
    .SYNOPSIS
        Executes a Transact-SQL statement against the connection and returns a DataSet containing a
        DataTable for each result set returned.
 
    .DESCRIPTION
        Executes a Transact-SQL statement against the connection and returns a DataSet containing a
        DataTable for each result set returned.
 
    .PARAMETER conn
        The sql server connection to use when creating the command.
 
    .PARAMETER sql
        The sql to use for the query.
 
    .PARAMETER parameters
        An array of sql parameters to use for the query. Can be created using New-DBSqlParameter.
 
    .PARAMETER timeout
        The command timeout for the query in seconds.
 
    .OUTPUTS
        The results of the query.
    #>

    [CmdletBinding()]
    [OutputType([System.Data.DataSet])]
    param (
        [Parameter(Mandatory=$true)]
        [System.Data.SqlClient.SqlConnection]$conn,
        [Parameter(Mandatory=$true)]
        [string]$sql,
        [System.Data.CommandType]$CommandType = [System.Data.CommandType]::Text,
        [System.Data.SqlClient.SqlParameter[]]$parameters,
        [int]$timeout = 30
    )

    process {
        try {
            $cmd = New-Object System.Data.SqlClient.SqlCommand($sql, $conn)
            $cmd.CommandType = $CommandType
            $cmd.CommandTimeout=$timeout
            foreach($p in $parameters){
                $cmd.Parameters.Add($p) | Out-Null
            }
            $ds = New-Object System.Data.DataSet
            $da = New-Object System.Data.SqlClient.SqlDataAdapter($cmd)
            $da.Fill($ds) | Out-Null

            return $ds
        } finally {
            if ($cmd) {
                $cmd.Dispose();
            }
        }
    }
}

function New-DBSqlParameter {
    <#
    .SYNOPSIS
        Creates a new instance of a SqlParameter object.
 
    .DESCRIPTION
        Creates a new instance of a SqlParameter object.
 
    .PARAMETER name
        The name of the SqlParameter.
 
    .PARAMETER type
        The SqlDbType of the parameter.
 
    .PARAMETER size
        The maximum size, in bytes, of the data within the column.
 
    .PARAMETER scale
        The number of decimal places to which Value is resolved.
 
    .PARAMETER precision
        The maximum number of digits used to represent the Value property.
 
    .OUTPUTS
        The SqlParameter.
    #>

    [CmdletBinding()]
    [OutputType([System.Data.SqlClient.SqlParameter])]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseShouldProcessForStateChangingFunctions', '', Justification='Not needed')]
    param (
        [Parameter(Mandatory=$true)]
        [string]$name,
        [Parameter(Mandatory=$true)]
        [System.Data.SqlDbType]$type,
        $value,
        [int]$size ,
        [int]$scale,
        [int]$precision
    )

    process {
        if ($name[0] -ne "@") { $name = "@$name" }

        $param = New-Object System.Data.SqlClient.SqlParameter($name, $type)

        if ($null -ne $value) { $param.Value = $value }
        if ($null -ne $size)  {
            $param.Size = $size
        } else {
            if ($null -ne $scale) { $param.Scale = $scale }
            if ($null -ne $precision) { $param.Precision = $precision }
        }

        return $param
    }
}

function Get-DBInClauseParams {
    <#
    .SYNOPSIS
        Can be used to create a set of parameters that can be used with an IN clause.
 
    .DESCRIPTION
        Can be used to create a set of parameters that can be used with an IN clause.
 
    .PARAMETER prefix
        The prefix to place in front of the parameter name. Must make the parameter name unique.
 
    .PARAMETER values
        The list of values to place into the parameters.
 
    .PARAMETER type
        The SqlDbType of the parameters.
 
    .PARAMETER size
        The maximum size, in bytes, of the data within the column.
 
    .PARAMETER scale
        The number of decimal places to which Value is resolved.
 
    .PARAMETER precision
        The maximum number of digits used to represent the Value property.
 
    .OUTPUTS
        The results of the query.
 
    .EXAMPLE
        PS> $list = 1..15
        PS> $params = Get-DBInClauseParams -prefix "p" -values $list -type Int
 
$params
 
    #>

    [CmdletBinding()]
    [OutputType([System.Data.SqlClient.SqlParameter[]])]
    param (
        [Parameter(Mandatory=$true)]
        [string]$prefix,
        [Parameter(Mandatory=$true)]
        $values,
        [Parameter(Mandatory=$true)]
        [System.Data.SqlDbType]$type,
        [int]$size,
        [int]$scale,
        [int]$precision
    )

    process {
        $params = New-Object 'System.Collections.Generic.List[System.Data.SqlClient.SqlParameter]'
        for  ($i=0; $i -le $values.Length -1; $i++) {
            $param = New-DBSqlParameter -name "@$prefix$i" -type $type -value $values[$i] -size $size -scale $scale -precision $precision
            $params.Add($param) | Out-Null
        }
        return $params.ToArray()
    }
}

function Get-DBInClauseString {
    <#
    .SYNOPSIS
        Creates the string representation of the parameters that can be used with an IN clause.
 
    .DESCRIPTION
        Creates the string representation of the parameters that can be used with an IN clause.
 
    .PARAMETER parameters
        The IN clause parameters created by using Get-DBInClauseParams.
 
    .PARAMETER delimiter
        The delimiter to use between the parameter names. Defaults to ",".
 
    .OUTPUTS
        A string representation of the parameters that can be used with an IN clause by concatenating the result into your query.
 
    .EXAMPLE
        PS> $params = Get-DBInClauseParams -prefix "p_" -values $someList -type [System.Data.SqlDbType]::VarChar -size 50
        PS> $paramString = Get-DBInClauseString -parameters $params
 
        Assuming the list has 3 values in it, the function should return "@p_0, @p_1, @p_2". This string can now be concatenated
        to the original query like so: "SELECT * FROM dbo.SomeTable AS [t] WHERE [t].id IN (@p_0, @p_1, @p_2)"
 
        If multiple parameter lists are needed for multiple IN clauses, then different prefixes should be utilized for each list.
 
        By using a parameterized query you both block SQL Injection, and you also allow for execution plan re-use.
 
    .EXAMPLE
        PS> $list = 1..15
        PS> $params = Get-DBInClauseParams -prefix "p" -values $list -type Int
        PS> $paramStr = Get-DBInClauseString -parameters $params
        PS> $params
        PS> $paramStr
 
    #>

    [CmdletBinding()]
    [OutputType([System.String])]
    param (
        [Parameter(Mandatory=$true)]
        [System.Data.SqlClient.SqlParameter[]]$parameters,
        [string]$delimiter = ","
    )

    process {
        $names = $parameters | ForEach-Object { $_.ParameterName }
        return $names -join $delimiter
    }
}