2

小計や強調表示などの他のオプションよりも多くの機能を含むSQLデータをExcelにエクスポートするCLRプロシージャを作成しようとしています。

これにはdllを参照する必要がありますがMicrosoft.Office.Interop.Excel、コードをコンパイルするときに実際にアセンブリを含める方法がわかりません。

CLRプロシージャにExcelアセンブリを含めるにはどうすればよいですか?

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);
        }
    }
}
}
4

3 に答える 3

3

SQLServerで任意のアセンブリを使用することはできません。フレームワークのサブセットのみを参照し、いくつかの基本的な規律を行使する必要があります。Excelのような歴史の層を持つモンスターアプリケーションがこの環境にロードされるとは思えません。

この基準を満たす、より単純な代替機能を確認することをお勧めします。

それ以上が必要な場合は、クライアント側でExcelを使用することを検討してください。

于 2012-06-18T20:56:35.130 に答える
1

ほぼすべての種類のエラーが発生する可能性があることを1日試した後、問題の解決策を考え出しました。

与えられた回答/コメントに感謝します。ソリューションを実装するためのより効率的で安全な方法があるかもしれないことに同意しますが、相互運用機能アセンブリを使用することは、このプロジェクトを完了するのに最も速く、最も馴染み深い方法でした。

私が石炭に夢中になる前に、このプロジェクトには、従来のSQLServerエクスポート機能の範囲外の自動フィルタリングやその他のフォーマットが絶対に必要であったことを理解してください。

解決

というタイトルのクラスライブラリ出力タイプのVisualStudioプロジェクトをSqlProcedures作成し、という新しいクラスを作成して、参照ExportToExcelに追加Microsoft.Office.Interop.Excelしました。

これが私のExportToExcel.csファイルのコードです:

using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
using System.Runtime.InteropServices;

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];
            _app.DisplayAlerts = false;

            //set the headers and freeze the panes
            _range = _sheet.get_Range(_sheet.Cells[1, 1], _sheet.Cells[1, fieldCount]);
            _range.NumberFormat = "@";
            _range.HorizontalAlignment = Excel.XlHAlign.xlHAlignLeft;
            _range.Value2 = headers;
            _range.Font.Bold = true;
            _range = _sheet.get_Range(_sheet.Cells[2, 1], _sheet.Cells[2, 1]);
            _range.EntireRow.Select();
            _range.Application.ActiveWindow.FreezePanes = true;

            _range = _sheet.get_Range(_sheet.Cells[2, 1], _sheet.Cells[results.Count, fieldCount]);
            _range.Value2 = exportVals;

            _range = _sheet.get_Range(_sheet.Cells[1, 1], _sheet.Cells[exportVals.Length, fieldCount]);
            _range.AutoFilter(1, Type.Missing, Excel.XlAutoFilterOperator.xlAnd, Type.Missing, true);
            _sheet.Cells.Columns.AutoFit();
            _sheet.Range["A1"].Select();

            _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.xlExcel5
                , Missing.Value
                , Missing.Value
                , false
                , false
                , Excel.XlSaveAsAccessMode.xlNoChange
                , Missing.Value
                , Missing.Value
                , Missing.Value
                , Missing.Value
                , Missing.Value);

            //_book.Close(Missing.Value, Missing.Value, Missing.Value);
            _app.Application.Quit();

            GC.Collect();
            GC.WaitForPendingFinalizers();

            Marshal.ReleaseComObject(_range);
            Marshal.ReleaseComObject(_sheet);
            Marshal.ReleaseComObject(_book);
            Marshal.ReleaseComObject(_app);

            _range = null;
            _sheet = null;
            _book = null;
            _app = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }
}
}

DLLの構築に成功した後、SQLサーバーのローカルディレクトリにDLLをコピーしました。

プロシージャを実行するには、依存するExportQueryResultsSQLサーバーにいくつかのアセンブリを追加する必要がありました。Microsoft.Office.Interop.Excel.dll

これが私のSQLコードです:

ALTER DATABASE main SET TRUSTWORTHY ON;

create assembly [stdole] from
'C:\Program Files\Microsoft.NET\Primary Interop Assemblies\stdole.dll'
WITH PERMISSION_SET = unsafe
create assembly [Office] from
'C:\WINDOWS\assembly\GAC\office\12.0.0.0__71e9bce111e9429c\OFFICE.DLL'
WITH PERMISSION_SET = unsafe

create assembly [Vbe] 
    FROM 'C:\WINDOWS\assembly\GAC\Microsoft.Vbe.Interop\12.0.0.0__71e9bce111e9429c\Microsoft.Vbe.Interop.dll'
WITH PERMISSION_SET = unsafe


create assembly [Microsoft.Office.Interop.Excel.dll] 
from 'C:\WINDOWS\assembly\GAC\Microsoft.Office.Interop.Excel\12.0.0.0__71e9bce111e9429c\Microsoft.Office.Interop.Excel.dll'
WITH PERMISSION_SET = unsafe

create assembly SqlProcedures from 'c:\sql_data_reporting\SqlProcedures.dll'
WITH PERMISSION_SET = unsafe
go
create procedure ExportToExcel @queryText nvarchar(4000), @worksheetName nvarchar(32),     @fileName nvarchar(250)
as external name SqlProcedures.ExportToExcel.ExportQueryResults
go

今では、使用with permission_set = unsafeがノノであることを知っていますが、これは「今すぐ実行」プロジェクトであり、これは私が思いついた最速のソリューションでした。

うまくいけば、このソリューションは、同様の機能を実装する必要がある他の人のために時間を節約するでしょう。

于 2012-06-19T17:56:20.767 に答える
0

コアアセンブリを追加したのと同じ方法で、参照されたアセンブリを簡単に追加できると思います。問題は、相互運用機能アセンブリがExcelCOMオブジェクトの薄いラッパーであるということです。つまり、SQLServerにMicrosoftExcelもインストールしない限り、相互運用は無意味になります。これが可能かどうかさえわかりませんが、それは本当に、本当に悪い考えのように聞こえます。

于 2012-06-18T21:03:13.177 に答える