I am trying to create a CLR procedure to export SQL data to Excel that will contain more features than other options such as subtotals and highlighting.
This requires me to reference the Microsoft.Office.Interop.Excel dll, but I’m not sure how to actually include the assembly when I compile my code.
How can I include the Excel assembly in my CLR procedure?
using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
public class ExportToExcel
{
[Microsoft.SqlServer.Server.SqlProcedure]
public static void ExportQueryResults(string queryText, string worksheetName, string fileName)
{
using (SqlConnection cnn = new SqlConnection("context connection=true"))
{
//the temp list to hold the results in
List<object[]> results = new List<object[]>();
cnn.Open();
//create the sql command
SqlCommand cmd = new SqlCommand(queryText, cnn);
using (SqlDataReader reader = cmd.ExecuteReader())
{
int fieldCount = reader.FieldCount;
object[] headers = new object[fieldCount];
for(int i = 0; i < fieldCount; i++)
{
headers[i] = reader.GetName(i);
}
//read the results
while (reader.Read())
{
object[] values = new object[fieldCount];
for (int i = 0; i < fieldCount; i++)
{
values[i] = reader[i];
}
results.Add(values);
}
//convert the results into a 2-d array to export into Excel
object[,] exportVals = new object[results.Count, fieldCount];
for (int row = 0; row < results.Count; row++)
{
for (int col = 0; col < fieldCount; col++)
{
exportVals[row, col] = results[row][col];
}
}
Excel.Application _app = new Excel.Application();
Excel.Workbook _book = _app.Workbooks.Add(Missing.Value);
Excel.Worksheet _sheet = (Excel.Worksheet)_book.ActiveSheet;
Excel.Range _range = (Excel.Range)_sheet.Cells[1, 1];
_range = _sheet.get_Range(_sheet.Cells[1, 1], _sheet.Cells[results.Count, fieldCount]);
_range.Value2 = exportVals;
_sheet.Name = worksheetName;
//remove any extra worksheets
foreach(Excel.Worksheet sht in _book.Worksheets)
{
if (sht.Name != worksheetName)
sht.Delete();
}
_book.SaveAs(fileName
, Excel.XlFileFormat.xlWorkbookDefault
, Missing.Value
, Missing.Value
, false
, false
, Excel.XlSaveAsAccessMode.xlNoChange
, Missing.Value
, Missing.Value
, Missing.Value
, Missing.Value
, Missing.Value);
}
}
}
}
After a day of fumbling around with nearly every type of error possible, I came up with a solution for my problems.
I appreciate the answers/comments given, and although I agree there may be more efficient / secure ways of implementing a solution, using the interop assembly was the fastest and most familiar to complete this project.
Before I get raked over the coals, please realize that auto-filtering and other formatting outside the scope of the more traditional SQL Server export functionality were absolutely required for this project.
Solution
I created a class library output type visual studio project titled
SqlProcedures, created a new class calledExportToExceland addedMicrosoft.Office.Interop.Excelto my references.Here is the code in my
ExportToExcel.csfile:After sucessfully building the DLL, I copied it to a local directory on my SQL server.
In order to run the procedure
ExportQueryResults, I needed to add several assemblies in the SQL server that theMicrosoft.Office.Interop.Excel.dlldepends on.Here is my SQL code:
Now I know that using
with permission_set = unsafeis a nono, but this was a “get it done now” project, and this was the fastest solution I could come up with.Hopefully this solution will save some time for others who need to implement similar functionality.