Send-SQLDataToExcel.ps1

Function Send-SQLDataToExcel {
<#
    .Synopsis
        Runs a SQL query and inserts the results into an ExcelSheet, more efficiently than sending it via Export-Excel
    .Description
        This command takes either an object representing a session with a SQL server or ODBC database, or a connection String to make one.
        It the runs a SQL command, and inserts the rows of data returned into a worksheet.
        It takes most of the parameters of Export-Excel, but it is more efficient than getting dataRows and piping them into Export-Excel,
        data-rows have additional properties which need to be stripped off.
     .Example
       C:\> Send-SQLDataToExcel -MsSQLserver -Connection localhost -SQL "select name,type,type_desc from [master].[sys].[all_objects]" -Path .\temp.xlsx -WorkSheetname master -AutoSize -FreezeTopRow -AutoFilter -BoldTopRow
       Connects to the local SQL server and selects 3 columns from [Sys].[all_objects] and exports then to a sheet named master with some basic header manager
     .Example
        C:\> $SQL="SELECT top 25 DriverName, Count(RaceDate) as Races, Count(Win) as Wins, Count(Pole) as Poles, Count(FastestLap) as Fastlaps FROM Results GROUP BY DriverName ORDER BY (count(win)) DESC"
        C:\> $Connection = 'Driver={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};DriverId=790;ReadOnly=0;Dbq=C:\users\James\Documents\f1Results.xlsx;'
        C:\> Send-SQLDataToExcel -Connection $connection -SQL $sql -path .\demo4.xlsx -WorkSheetname "Winners" -AutoSize -AutoNameRange
 
        This declares a SQL statement and creates an ODBC connection string to read from an Excel file, it then runs the statement and outputs the resulting data to a new spreadsheet.
     .Example
        C:\> Send-SQLDataToExcel -path .\demo4.xlsx -WorkSheetname "LR" -Connection "DSN=LR" -sql "SELECT name AS CollectionName FROM AgLibraryCollection Collection ORDER BY CollectionName"
 
        This example uses an Existing ODBC datasource name "LR" which maps to an adobe lightroom database and gets a list of collection names into a worksheet
  
 
 
#>

    param (
        #Database connection string; either DSN=ODBC_Data_Source_Name, a full odbc or SQL Connection string, or the name of a SQL server
        [Parameter(ParameterSetName="SQLConnection", Mandatory=$true)]
        [Parameter(ParameterSetName="ODBCConnection",Mandatory=$true)]
        $Connection, 
        #A pre-existing database session object
        [Parameter(ParameterSetName="ExistingSession",Mandatory=$true)] 
        [System.Data.Common.DbConnection]$Session,
        #Specifies the connection string is for SQL server not ODBC
        [Parameter(ParameterSetName="SQLConnection",Mandatory=$true)]
        [switch]$MsSQLserver,
        #Switches to a specific database on a SQL server
        [Parameter(ParameterSetName="SQLConnection")]
        [String]$DataBase,
        #The SQL query to run
        [Parameter(Mandatory=$true)]
        [string]$SQL, 
        $Path, 
        [String]$WorkSheetname = 'Sheet1',   
        [Switch]$KillExcel, 
        #If Specified, open the file created.
        [Switch]$Show,
        [String]$Title,
        [OfficeOpenXml.Style.ExcelFillStyle]$TitleFillPattern = 'None',
        [Switch]$TitleBold,
        [Int]$TitleSize = 22,
        [System.Drawing.Color]$TitleBackgroundColor,
        [String]$Password,
        [String[]]$PivotRows,
        [String[]]$PivotColumns,
        $PivotData,
        [Switch]$PivotDataToColumn,        
        [Hashtable]$PivotTableDefinition,
        [Switch]$IncludePivotChart,
        [OfficeOpenXml.Drawing.Chart.eChartType]$ChartType = 'Pie',
        [Switch]$NoLegend,
        [Switch]$ShowCategory,
        [Switch]$ShowPercent,
        [Switch]$AutoSize,
        [Switch]$FreezeTopRow,
        [Switch]$FreezeFirstColumn,
        [Switch]$FreezeTopRowFirstColumn,
        [Int[]]$FreezePane,
        [Switch]$AutoFilter,
        [Switch]$BoldTopRow,
        [Switch]$NoHeader,
        [String]$RangeName,
        [String]$TableName,
        [OfficeOpenXml.Table.TableStyles]$TableStyle = 'Medium6',
        [Object[]]$ExcelChartDefinition,
        [Switch]$AutoNameRange,
        [Object[]]$ConditionalFormat,
        [Object[]]$ConditionalText,
        [ScriptBlock]$CellStyleSB,
        [Int]$StartRow    = 1,
        [Int]$StartColumn = 1,
        #If Specified, return an ExcelPackage object to allow further work to be done on the file.
        [Switch]$Passthru
    )
    
    if ($KillExcel) {
            Get-Process excel -ErrorAction Ignore | Stop-Process
            while (Get-Process excel -ErrorAction Ignore) {}
    }
    
    #We were either given a session object or a connection string (with, optionally a MSSQLServer parameter)
    # If we got -MSSQLServer, create a SQL connection, if we didn't but we got -Connection create an ODBC connection
    if     ($MsSQLserver) {
            if ($connection -notmatch "=") {$Connection = "server=$Connection;trusted_connection=true;timeout=60"} 
            $Session = New-Object -TypeName System.Data.SqlClient.SqlConnection  -ArgumentList $Connection
            if ($Session.State -ne 'Open') {$session.Open()} 
            if ($DataBase) {$Session.ChangeDatabase($DataBase) }
    }
    elseif ($Connection)  {
            $Session = New-Object -TypeName System.Data.Odbc.OdbcConnection      -ArgumentList $Connection ; $Session.ConnectionTimeout = 30
    }

    #A session was either passed in or just created. If it's a SQL one make a SQL DataAdapter, otherwise make an ODBC one
    if ($Session.gettype().name -match "SqlConnection") {  
        $dataAdapter = New-Object -TypeName System.Data.SqlClient.SqlDataAdapter -ArgumentList (
                       New-Object -TypeName System.Data.SqlClient.SqlCommand     -ArgumentList $sql, $Session)
    }
    else {
        $dataAdapter = New-Object -TypeName System.Data.Odbc.OdbcDataAdapter     -ArgumentList (
                       New-Object -TypeName System.Data.Odbc.OdbcCommand         -ArgumentList $sql, $Session ) 
    }
    
    #Both adapter types output the same kind of table, create one and fill it from the adapter
    $dataTable       = New-Object -TypeName System.Data.DataTable
    $rowCount        = $dataAdapter.fill($dataTable)
    Write-Verbose "Query returned $rowcount row(s)" 

    #ExportExcel user a -NoHeader parameter so that's what we use here, but needs to be the other way around.
    $PrintHeaders    = -not $NoHeader
    if ($Title)  {$r = $StartRow +1 } 
    else         {$r = $StartRow} 
    #Get our Excel sheet and fill it with the data
    $excelPackage    = Export-Excel -Path $Path -WorkSheetname $WorkSheetname  -PassThru
    $excelPackage.Workbook.Worksheets[$WorkSheetname].Cells[$r,$StartColumn].LoadFromDataTable($dataTable, $PrintHeaders )  | Out-Null
    
    #Call export-excel with any parameters which don't relate to the SQL query
    "Connection", "Database" , "Session", "MsSQLserver", "Destination" , "sql" ,"Path" | foreach-object {$null = $PSBoundParameters.Remove($_) }
    Export-Excel -ExcelPackage $excelPackage   @PSBoundParameters 

    #If we were not passed a session close the session we created.
    if ($Connection)  {$Session.close() } 
}