簡體   English   中英

將xlsx文件導入SQL Server時出現問題

[英]Problems importing xlsx file into SQL Server

我收到一個月度XLSX文件,需要使用SSIS包導入SQL Server。 遺憾的是,發件人不遵循UNC命名文件名或工作表,我們最近遷移到SQL Server 2012導致程序包失敗 - 即使使用Excel連接管理器也是如此。 我們也嘗試向他們發送模板,但他們拒絕遵循它,我們沒有任何動力迫使他們這樣做。

我一直在嘗試更新包,它將使用腳本任務將每個Excel工作表導入到每個的System.Object中,然后我可以查詢或循環,將數據導入目標SQL服務器表。

到目前為止,使用的例子,從微軟在這里我已經成功地導入Excel文件路徑/文件名,這兩個工作表的名稱,到對象變量。 但是,這不會創建包含任一工作表中的實際數據集的Object。

基於此處和網絡上的其他示例,我已經啟動了一個C#腳本,我相信它會將工作表數據輸出到一個Object變量中,但是我對C#並不是很熟練,並且在沒有完整示例的情況下很難調試它復制自。 到目前為止這是我的代碼:

using System;
using System.Data;
using System.Data.OleDb; 
using Microsoft.SqlServer.Dts.Runtime;
using System.Windows.Forms;

[Microsoft.SqlServer.Dts.Tasks.ScriptTask.SSISScriptTaskEntryPointAttribute]
public partial class ScriptMain : Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase
{
        public DataSet Main()
        {
            string fileName;
            string connectionString;

            fileName = Dts.Variables["ExcelFile"].Value.ToString();
            Console.WriteLine(fileName);

            connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;" +
                "Data Source=" + fileName + ";Extended Properties=Excel 12.0 Xml";
            Console.WriteLine(connectionString);           
            DataSet data = new DataSet();
            using (OleDbConnection con = new OleDbConnection(connectionString))
            {
                con.Open();
                OleDbDataAdapter adapter = new OleDbDataAdapter("SELECT * FROM [Sheet1$]", connectionString);
                adapter.Fill(data);
            }

            return data;
        }
}

代碼構建成功,但是當我運行包時,我收到一個非描述錯誤

錯誤:腳本任務0x1:調用目標拋出了異常。
任務失敗:腳本任務

我沒有從我的任何Console.WriteLine命令獲得任何輸出,所以我相信腳本任務立即失敗。 我確實有延遲驗證=真,雖然改變它並沒有什么不同。 你在我的腳本中看到任何明顯的/新手錯誤嗎? 我已經使用SQL和SSIS多年了,但我的C#/ VB / Java /等。 知識和經驗有限。

此外,如果我在SSIS中忽略了一種更好的方法來實現這一點(除了Excel連接,這不起作用),請告訴我。

更新 - 2016年5月31日:我認為今天我有一點時間在這個項目上工作,並取得了一些進展。 我已更新我的腳本任務以包括以下內容:

        DataSet data = new DataSet();
        using (OleDbConnection con = new OleDbConnection(connectionString))
        {
            con.Open();
            OleDbDataAdapter adapter = new OleDbDataAdapter(query, con);
            //OleDbDataAdapter adapter = new OleDbDataAdapter("SELECT * FROM [Indemnity Scores$]", con);
            adapter.Fill(data);
            Dts.Variables["ExcelDataTable_IndemnityScores"].Value = data;
        }

腳本任務現在成功完成,因此我之后添加了一個Foreach循環容器,將其設置為Foreach From Variable Enumerator,並選擇ExcelDataTable_IndemnityScores作為集合。

但是,現在我很難從這個Object變量中提取數據。 它有(或者至少應該有)兩列,我在變量映射中設置了它,並使用Execute SQL命令將值插入表中。 不幸的是,每列只插入一個空白值。

接下來,我用一個簡單的腳本任務替換了執行SQL,以返回每個變量的值。 不幸的是,它返回“Microsoft.SqlServer.Dts.Runtime.Variable”而不是值。 我認為這對我來說是一個新手錯誤,但我還沒有找到任何在線解釋錯誤的內容嗎?

更新2016年6月14日:我終於完成了包裹,它昨天成功投入生產。 我最后使用了這里提出的建議,以及其他地方的例子。 我的一般工作流程需要三重嵌套的Foreach循環來獲取從源工作簿導入的兩個工作表 - 我只期望每月一個,但沒有任何內容與此任務100%一致。

我的最外層循環只是枚舉我的導入目錄,以查找FTP進程下載的文件。 它包含兩個腳本任務。 第一個只是確認FTP進程下載的第一個電子表格的文件名。 我使用上面的Microsoft鏈接代碼,只對我的變量名進行了少量修改。

第二個任務從第一個電子表格中獲取所有工作表名稱,並使用上面的Microsoft鏈接構建。 但是,我使用“#”排除任何工作表名稱,以防止將XML數據庫分配給我的變量。

第二個循環(第一個內循環)枚舉在第一個循環中解析的每個工作表名稱。 它包含三個腳本任務,第一個腳本任務將第一個工作表中的數據導入到我的對象變量中。

public void Main(){try {

            string fileName;
            string connectionString;
            string worksheetName;
            string query;

            fileName = Dts.Variables["ExcelFile"].Value.ToString();
            //MessageBox.Show("InsertWorksheetDataIntoObject - Filename: " + fileName);

            connectionString = String.Format("Provider=Microsoft.ACE.OLEDB.12.0;" +
                "Data Source={0};Extended Properties=Excel 12.0 Xml;", fileName);
            //MessageBox.Show("Connection: " + connectionString);

            worksheetName = Dts.Variables["ExcelTable"].Value.ToString();
            worksheetName = worksheetName.Replace("'", "");
            //MessageBox.Show("InsertWorksheetDataIntoObject - Worksheet: " + worksheetName);

            query = string.Format("SELECT * FROM [" + worksheetName + "]");
            //MessageBox.Show("Query: " + query);

            DataSet data = new DataSet();
            using (OleDbConnection con = new OleDbConnection(connectionString))
            {
                con.Open();
                OleDbDataAdapter adapter = new OleDbDataAdapter(query, con);
                adapter.Fill(data);
                Dts.Variables["ExcelDataTable"].Value = data;
            }

            Dts.TaskResult = (int)ScriptResults.Success;
        }

        catch (Exception ex)
        {
            Dts.Events.FireError(-1, "ErrorMessage", ex.ToString(), "", 0);
            Dts.TaskResult = (int)ScriptResults.Failure;
        }


        //return data;

    }

此循環中的第二個腳本任務只是從Excel中刪除任何空白行。 我可以將它與上面的腳本合並,但我保持它的可移植性,以便將來在其他地方重用。

此循環中的第三個腳本任務使用工作表名稱來設置在下一個循環中用於確定目標表的變量。

第三個循環(第二個內循環)枚舉包含工作表中數據的對象變量中的行。 它包含單個執行SQL任務,該任務根據上面工作表名稱設置的變量值將兩個源列中的數據導入到正確的目標表中。 由於工作表名稱並不總是一致的,因此該循環直接連接到我的對象變量,這樣就無需按名稱調用源列。 相反,我只是將每一個分配給Foreach循環中的目標變量,並將該數據逐行傳遞到我的表中。

再次感謝大家的幫助和建議!

通常當我立即收到該消息時,這意味着我在您的案例ExcelFile中拼寫了錯誤的變量名稱。 我執行sql查詢時也會收到此錯誤,並返回null。 你最好的辦法就是在你的代碼執行之前注釋掉你的代碼部分,然后你至少知道是什么代碼導致了這個問題。

我不明白為什么excel連接不起作用。 如果文件位於UNC路徑上並且導致問題,則可以使用腳本任務將文件移動到可行的位置。

Joe C可能是對的,您可能通過錯誤的名稱引用變量。 您是否將變量/參數傳遞給腳本任務?

不過,我不明白為什么你沒有使用其中一個數據流腳本任務。 您可以定義輸入和輸出列,然后將它們填入腳本任務的代碼中:

    public override void CreateNewOutputRows()
    {
        /*
          Add rows by calling the AddRow method on the member variable named "<Output Name>Buffer".
          For example, call MyOutputBuffer.AddRow() if your output was named "MyOutput".
        */
    }

這些輸出行可以轉移到數據流中的下一個任務 - 就像SSIS喜歡它一樣。 此外,使用變量要容易得多。 您可以通過this.Variables.ExcelFile (fe)訪問它們,就像通常的屬性一樣。

另請注意:不要忘記設置(控制流程)腳本任務的結果。 您的任務可能會成功完成,但在以下順序流程中不會有任何約束。

Dts.TaskResult = (int)ScriptResults.Success;

哇,當你的工作變得那樣復雜的時候,你不討厭它! 所以有很多方法可以解決你的問題我的個人意見都在一個腳本任務中,你可能更容易遵循邏輯並完成,但@Johannes也提出了另一個好的方法。 腳本任務有兩個位置,它們是完全不同的編碼和思考過程方法。 一個是控制流中可用的“腳本任務”,它似乎是您編碼並將對象添加到變量的位置。 在此輸入圖像描述
第二個是“腳本組件”,它在數據流任務中可用。 在此輸入圖像描述 前者需要將其視為一個獨立於其他所有內容的獨立腳本,后者嵌入在數據流任務中,並充當源,目標或轉換。 這意味着它可以用於填充要使用的記錄集變量(對象)。

因此,在選項1中,您當前所有需要完成代碼的方法是添加一些c#來更新/填充您想要的SQL表。 以下是我從我的一個軟件包中竊取的一些代碼:

            SqlConnection sqlConnection = new SqlConnection(sqlConnectionString);
        sqlConnection.Open();

        SqlBulkCopy bulkCopy = new SqlBulkCopy(sqlConnection);
        bulkCopy.DestinationTableName = _stagingTableName;
        foreach (DataColumn col in _jobRecDT.Columns)
        {
            //System.Windows.Forms.MessageBox.Show(col.ColumnName);
            bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
        }


        bulkCopy.WriteToServer(_jobRecDT);

        sqlConnection.Close();

對於選項2,我曾經對此有所了解或許@Johannes有一個鏈接或者有人可以在這里評論它。 但是在這種方法中,您應該能夠重用大量代碼,然后將其移動到“腳本組件”。 然后在對象上定義記錄集模式,並像使用數據流任務中的任何其他源一樣使用它。

有兩個問題需要考慮,需要進一步的邏輯。 1)如果使用選項1,則需要在使用批量復制或動態管理列映射之前將表/數據集重命名為預期值。 2)在數據流選項中,您需要在填充最終記錄集變量之前轉換數據集,以始終具有相同的列和數據類型。

兩個選項都有性能和數據有效性考慮因素。 第一種可能是更好的性能,但SSIS不處理數據有效性/錯誤檢查。 選項2,您將獲得大數據集的SSIS錯誤檢查和性能的好處。 如果您的數據集非常大,則需要調整兩個選項。 還有其他一些注意事項,例如線程,但我不相信這些注意事項適用於您。

我希望這有幫助。

我編輯了我的原始問題,概述了最終為我工作的解決方案。 如果有人有任何疑問或想了解更多細節/示例,請告知我們。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM