Tip of the hat to Bacon Bits and AdminOfThings for their help.
Create the CSV header row manually, using plain-text processing.
The following assumes that the output CSV file path is stored in variable $outFile
:
if ((Get-Item -LiteralPath $outFile).Length -eq 0) { # Nothing was exported.
# Define the array of column names.
# Ideally, these should come from a different SQL query
# that returns the column names of MY_TABLE.
$columnNames = 'Foo', 'Bar', 'Baz'
# Transform the to a CSV header line and write to the target file.
$columnNames.ForEach({ "`"$_`"" }) -join "," |
Set-Content -LiteralPath $outFile
}
Note:
- If the query targets only a single table / view, as in your example, you can obtain its column names via
Read-SqlTableData
/ Read-SqlViewData
, as shown in the next section.
The above stores the following in the output file, if it was found to be empty after exporting (due to the query returning no rows):
"Foo","Bar","Baz"
A solution limited to reading data from a single table (unconditionally, though the count of rows can be limited), using Read-SqlTableData
(or, analogously, using Read-SqlViewData
for a single view):[1]
If you pass -OutputAs DataTable
(or -As DataTable
) to a Read-SqlTableData
call, it will return a System.Data.DataTable
instance, which provides access to not just the table's data (rows, via .Rows
), but also the table structure (column names, via .Columns.ColumnName
).
- Note: Regrettably, even though
Invoke-SqlCmd
also supports -OutputAs DataTables
(note the plural s
, though), it returns nothing rather than a DataTable
instance if the query happens to return no rows.
# Get table MY_TABLE as a DataTable instance.
$dataTable =
Read-SqlTableData -OutputAs DataTable -TableName MY_TABLE -ServerInstance "myserver.fake" -Database "db" -Username "user" -Password "password" -Querytimeout 65535
if ($dataTable.Rows.Count -gt 0) { # At least 1 row found, export it.
$dataTable.Rows | Export-Csv -Path "\\MyDirectory\MyResults.csv" -NoTypeInformation
} else { # No query results - write the header row (only).
# Create the header row manually, using plain-text processing.
$dataTable.Columns.ColumnName.ForEach({ "`"$_`"" }) -join "," |
Set-Content -LiteralPath $outFile
}
[1] As Bacon Bits points out, there's really no good reason for separate cmdlets to support tables vs. views.