导入xlsx文件到SQL Server的问题

我收到一个需要使用SSIS包导入SQL Server的每月XLSX文件。 不幸的是,发件人没有按照UNC命名文件名或工作表,而且我们最近迁移到SQL Server 2012时,即使在使用Excel连接pipe理器时也会导致软件包失败。 我们也试着给他们发一个模板,但他们拒绝遵守,我们也没有办法强迫他们这样做。

我一直在尝试更新包,这将使用脚本任务将每个两个Excel工作表导入到每个的System.Object ,然后我可以查询或循环,将数据导入到目标SQL服务器表。

到目前为止,使用来自Microsoft的示例,我已经成功地将Excel文件path/名称和两个工作表名称导入到对象variables中。 但是,这不会创build包含工作表中的实际数据集的对象。

根据这里和其他地方的例子,我已经启动了一个C#脚本,我相信这个脚本会把工作表数据输出到一个Objectvariables中,但是我不是很熟悉C#,并且没有一个完整的例子来debugging它,复制。 这是我的代码到目前为止:

 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任务:调用的目标引发了exception。
任务失败:脚本任务

我没有得到任何我的Console.WriteLine命令的任何输出,所以我相信脚本任务是立即失败。 我确实有延迟validation=真,虽然改变它没有什么差别。 你看到我的脚本中有任何明显/新手的错误? 我已经使用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,并selectExcelDataTable_IndemnityScores作为集合。

但是,现在我很难从这个Objectvariables中提取数据。 它有(或至less应该有)两列,我已经在variables映射中设置,并使用执行SQL命令将值插入到表中。 不幸的是,每个列只能插入一个空白值。

所以接下来我用一个简单的脚本任务replace执行SQL来返回每个variables的值。 不幸的是,而不是返回值“Microsoft.SqlServer.Dts.Runtime.Variable”。 我认为这是我的一个新手的错误,但我还没有find任何网上解释错误呢?

更新6/14/2016:我终于完成了这个软件包,它昨天在生产中成功运行。 我结束了使用这里提出的build议,以及其他地方发现的例子。 我的一般工作stream程需要三重嵌套的Foreach循环才能从源工作簿中导入两个工作表 – 我只希望每个月都有一个工作表,但没有任何内容与此任务完全一致。

我的最外层循环只是枚举我的导入目录来查找FTP进程下载的文件。 它包含两个脚本任务。 第一个只是确认由FTP进程下载的第一个电子表格的文件名。 我使用上面的Microsoft链接代码,只对我的variables名称进行了小的修改。

第二个任务从第一个电子表格获取所有工作表名称,并使用上面的Microsoft链接构build。 但是,我排除任何工作表名称“#”,以防止将XML数据库分配给我的variables。

第二个循环(第一个内部循环)枚举了在第一个循环内parsing的每个工作表名称。 它包含三个脚本任务,其中第一个将第一个工作表中的数据导入到我的对象variables中。

公共无效的主要(){尝试{

  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中删除任何空行。 我可以将它与上面的脚本结合起来,但是我保留了它的可移植性,以便将来在其他地方重新使用。

此循环中的第三个脚本任务使用工作表名称来设置在下一个循环中使用的variables,以确定我的目标表。

第三个循环(第二个内循环)枚举包含工作表中数据的对象variables中的行。 它包含一个执行SQL任务,该任务根据上述工作表名称设置的variables值将来自两个源列的数据导入到正确的目标表中。 由于工作表名称并不总是一致的,所以这个循环直接连接到我的对象variables,这就消除了按名称调用源列的需要。 相反,我只是在Foreach循环中将每一个赋值给一个目标variables,并将这些数据逐行传递到我的表中。

再次感谢大家的帮助和build议!

通常,当我立即得到这个消息,这意味着我拼写错误的ExcelFile中的variables名称。 我也遇到这个错误,当我执行一个SQL查询,它返回null。 最好的办法是注释掉代码的各个部分,直到执行完成,然后你至less知道代码是什么导致了这个问题。

我不明白为什么excel连接不起作用。 如果文件位于UNCpath上并且导致了问题,则可以使用脚本任务将文件移动到可以工作的位置。

Joe C可能是对的,你可能会用错误的名字引用一个variables。 您是否将variables/parameter passing给脚本任务?

不过,我不明白为什么你不使用其中一个数据stream脚本任务。 您可以定义input和输出列,然后将其填充到脚本任务的代码中:

  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". */ } 

这些输出行可以转移到数据stream中的下一个任务 – 就像SSIS喜欢它一样。 而且,使用这些variables要容易得多。 你可以通过this.Variables.ExcelFile (fe)像通常的属性来访问它们。

另一个注意:不要忘记设置(控制stream的)脚本任务的结果。 您的任务可能会成功完成,但在以下顺序stream程中不会有任何约束。

Dts.TaskResult = (int)ScriptResults.Success;

当你的工作变得像这样的人复杂,你不讨厌它! 所以有很多方法可以解决你的问题我的个人意见都是在一个脚本任务内,可能会更容易遵循逻辑和完成,但@Johannes也提出了另一个好方法。 有两个脚本任务的地方,他们是非常不同的编码和思维过程的方法。 一个是在“控制stream”中可用的“脚本任务”,这是您正在编码的地方,并将该对象添加到variables中。 在这里输入图像说明
第二个是“数据stream任务”中提供的“脚本组件”。 在这里输入图像说明 前者需要被认为是独立于其他任何事物的独立脚本,而后者embedded在数据stream任务中,并作为源,目的地或转换。 这意味着它可以用来填充要被使用的logging集variables(对象)。

因此,在选项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有一个链接铺设或有人可以在这里发表评论。 但是在这个方法中,你应该可以重用很多你的代码,然后把它移到“脚本组件”。 然后在对象上定义logging集模式,并像使用数据stream任务中的其他源一样使用它。

有两个问题需要考虑,需要进一步的逻辑。 1)如果使用选项1,则需要在使用批量复制或dynamicpipe理列映射之前,将表/数据集重命名为期望值。 2)在数据stream选项中,在填充最终logging集variables之前,需要转换数据集,使其始终具有相同的列和数据types。

这两个选项都有性能和数据有效性方面的考虑。 第一个可能是更好的性能,但是数据有效性/错误检查不是由SSIS处理的。 选项2,您将获得大数据集的SSIS错误检查和性能优势。 如果你的数据集非常大,那么这两个选项都需要调整。 还有其他的考虑,如线程,但我不相信这个意志适用于你。

我希望这有帮助。

我编辑了我原来的问题,并总结了为我工作的解决scheme。 如果有人有问题或想了解更多的细节/例子,请让我知道。