导入具有可变标题的excel文件

我有SSIS包,它将加载到数据库的Excel文件。 我已经创buildExcel源任务映射的Excel列名称数据库表列的名称和工作正常。

在极less数情况下,我们正在收到带有一些空格的excel文件列名(例如:列名是“ABC”,但我们正在接收“ABC”) ,导致映射问题和SSIS失败。

有没有可能修剪列名称而无需打开Excel。

注意:页面名称是dynamic的,列位置可能会改变(例如:“ABC可能存在于第一行或第二行或..”)。

首先,我的解决scheme是基于@DrHouseofSQL和@Bhouse的答案,所以你必须先阅读@DrHouseofSQL的答案,然后@BHouse答案,然后继续这个答案

问题

注:页面名称将是dynamic的,列位置可能会改变(例如:列“ABC可能存在于第一行或第二行或…

这种情况有点复杂,可以使用以下解决方法来解决:

解决scheme概述

  1. 在导入数据的数据stream任务之前添加一个脚本任务
  2. 您必须使用脚本任务打开Excel文件并获取工作表名称和标题行
  3. 构build查询并将其存储在一个variables中
  4. 在第二个数据stream任务中,您必须使用上面存储的查询作为源( 请注意,您必须将Delay Validation属性设置为true

解决scheme详情

  1. 首先创build一个stringtypes的SSISvariables(即@ [User :: strQuery])
  2. 添加另一个包含Excel文件path的variables(即@ [User :: ExcelFilePath])
  3. 添加一个脚本任务,并select@[User::strQuery]作为ReadWritevariables, @[User::ExcelFilePath]作为ReadOnlyvariables(在脚本任务窗口中)
  4. 将脚本语言设置为VB.Net并在脚本编辑器窗口中编写以下脚本:

注意:你必须导入System.Data.OleDb

在下面的代码中,我们searchex​​cel的前15行来查找标题,如果在15行之后可以find标题,则可以增加数字。 我还假定列范围是从AI

  m_strExcelPath = Dts.Variables.Item("ExcelFilePath").Value.ToString Dim strSheetname As String = String.Empty Dim intFirstRow As Integer = 0 m_strExcelConnectionString = Me.BuildConnectionString() Try Using OleDBCon As New OleDbConnection(m_strExcelConnectionString) If OleDBCon.State <> ConnectionState.Open Then OleDBCon.Open() End If 'Get all WorkSheets m_dtschemaTable = OleDBCon.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, New Object() {Nothing, Nothing, Nothing, "TABLE"}) 'Loop over work sheet to get the first one (the excel may contains temporary sheets or deleted ones For Each schRow As DataRow In m_dtschemaTable.Rows strSheetname = schRow("TABLE_NAME").ToString If Not strSheetname.EndsWith("_") AndAlso strSheetname.EndsWith("$") Then Using cmd As New OleDbCommand("SELECT * FROM [" & strSheetname & "A1:I15]", OleDBCon) Dim dtTable As New DataTable("Table1") cmd.CommandType = CommandType.Text Using daGetDataFromSheet As New OleDbDataAdapter(cmd) daGetDataFromSheet.Fill(dtTable) For intCount As Integer = 0 To 15 If Not String.IsNullOrEmpty(dtTable.Rows(intCount)(0).ToString) Then '+1 because datatable is zero based indexed, +1 because we want to start from the second row intFirstRow = intCount + 2 End If Next End Using If intFirstRow = 0 Then Throw New Exception("header not found") End Using 'when the first correct sheet is found there is no need to check others Exit For End If Next OleDBCon.Close() End Using Catch ex As Exception Throw New Exception(ex.Message, ex) End Try Dts.Variables.Item("strQuery").Value = "SELECT * FROM [" & strSheetname & "A" & intFirstRow.ToString & ":I]" Dts.TaskResult = ScriptResults.Success End Sub 
  1. 然后,您必须添加一个Excel连接pipe理器,然后select您想要导入的Excel文件(只需select一个样本即可首次定义元数据)
  2. Select * from [Sheet1$A2:I]Select * from [Sheet1$A2:I]的默认值Select * from [Sheet1$A2:I]variables@[User::strQuery]
  3. 在数据stream任务中添加一个Excel Source,从variables中selectSQL Command,并select@[User::strQuery]
  4. 转到“列”选项卡,并按照@BHousebuild议的相同方式命名列

图片 来自@BHouse答案的图片

  1. 将DataFlow任务Delay Validation属性设置为True
  2. 将其他组件添加到DataFlow任务

更新1:

从OP的评论: sometimes excel with empty data will come.(ie) we have only header row not not data... in that case it fails entire task

解:

如果你的excel文件不包含数据(只有标题),你必须执行这些步骤:

  1. 添加一个boolean *types的SSISvariables(即@[User::ImportFile]
  2. @[User::ImportFile]添加到脚本任务ReadWritevariables
  3. 在脚本任务中检查文件是否包含行
  4. 如果是设置@[User::ImportFile] = True,否则@[User::ImportFile] = False
  5. 双击将脚本任务连接到DataFlow的箭头(优先约束)
  6. 将其types设置为约束和expression式
  7. 写下面的expression式

     @[User::ImportFile] == True 

注意:新的脚本任务代码是:

  m_strExcelPath = Dts.Variables.Item("ExcelFilePath").Value.ToString Dim strSheetname As String = String.Empty Dim intFirstRow As Integer = 0 m_strExcelConnectionString = Me.BuildConnectionString() Try Using OleDBCon As New OleDbConnection(m_strExcelConnectionString) If OleDBCon.State <> ConnectionState.Open Then OleDBCon.Open() End If 'Get all WorkSheets m_dtschemaTable = OleDBCon.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, New Object() {Nothing, Nothing, Nothing, "TABLE"}) 'Loop over work sheet to get the first one (the excel may contains temporary sheets or deleted ones For Each schRow As DataRow In m_dtschemaTable.Rows strSheetname = schRow("TABLE_NAME").ToString If Not strSheetname.EndsWith("_") AndAlso strSheetname.EndsWith("$") Then Using cmd As New OleDbCommand("SELECT * FROM [" & strSheetname & "A1:I15]", OleDBCon) Dim dtTable As New DataTable("Table1") cmd.CommandType = CommandType.Text Using daGetDataFromSheet As New OleDbDataAdapter(cmd) daGetDataFromSheet.Fill(dtTable) For intCount As Integer = 0 To 15 If Not String.IsNullOrEmpty(dtTable.Rows(intCount)(0).ToString) Then '+1 because datatable is zero based indexed, +1 because we want to start from the second row intFirstRow = intCount + 2 End If Next End Using End Using 'when the first correct sheet is found there is no need to check others Exit For End If Next OleDBCon.Close() End Using Catch ex As Exception Throw New Exception(ex.Message, ex) End Try If intFirstRow = 0 OrElse _ intFirstRow > dtTable.Rows.Count Then Dts.Variables.Item("ImportFile").Value = False Else Dts.Variables.Item("ImportFile").Value = True End If Dts.Variables.Item("strQuery").Value = "SELECT * FROM [" & strSheetname & "A" & intFirstRow.ToString & ":I]" Dts.TaskResult = ScriptResults.Success End Sub 

更新2:

从OP的评论: is there any other work around available to process the data flow task without skipping all data flow task,Actually one of the task will log the filename and data count and all, which are missing here

解:

  1. 只需添加另一个数据stream任务
  2. 使用另一个连接器并使用expression式@[User::ImportFile] == False将此数据stream与脚本任务连接(与第一个连接器的步骤相同)
  3. 在DataFlow任务中添加SCript组件作为源
  4. 创build要导入到日志的输出列
  5. 创build一个包含您需要导入的信息的行
  6. 添加日志目标

或者不添加另一个Data Flow Task ,您可以添加一个Execute SQL Task在日志表中插入一行

文件是手动还是自动创build的? 无论哪种情况,您都可以从Excel文件中删除标题行(以编程方式或者告诉用户在保存文件之前将其删除)。 一旦你这样做,进入Excel连接pipe理器,并find指示“第一行有列名称”的框。 如果你可以清除那个盒子,然后将这些列再次映射到应该解决你的问题的目的地。 您不必担心列名称中拼写错误(或多余的空格)。

我想在SSIS中也有一个选项可以跳过第一行,但是我不记得那个选项在哪里。 如果你能find那么就跳过Excel文件的第一行。 相同的映射仍然存在。

谢谢

这已经在MSDN中得到很好的logging,通过类似于所提到的@houseofsql的步骤运行

步骤1:

在excel连接的第一行中排除列名,使用sql命令作为数据访问模式 在这里输入图像说明

第二步:将输出列中的列名别名匹配您的目的地,

select*从[Sheet1$A2:I]将从第二行中select

在这里输入图像说明

最后添加目的地作为OLEDB目的地

在这里输入图像说明

我对这个论坛相当陌生,所以如果你觉得这很愚蠢,那么就带上一点盐吧。

MS Access具有许多与Excel相同的VBAfunction,或者您可以编写一个新的存根Excel工作簿,在SQL导入之前parsing和格式化,然后导入(如果您愿意的话,可以导入一个中间件)。

对于尾随或领先空间的问题,我已经在很多场合使用了以下内容:

myString = trim(msytring) '这将删除所有的前导空格和尾部空格,但不会myString = trim(msytring)字符间的空格。 所以在导入时,您可以在导入列标题时对其进行修剪。

还有LTrim和RTrim,你可以猜出那些string左右的内容

https://support.office.com/en-us/article/LTrim-RTrim-and-Trim-Functions-e340ced1-67df-435f-b078-1527a4eddea2

对于大写字母,您可以使用UCase

 myString = UCase(Trim(myString)) 

如果经常遇到某种情况,有时用户可能会使用#字符,而有时候不会,replace总是派上用场。

例如:“Patterson#288”或“PatTeRson 288” myString = UCase(Trim(Replace(myString,"#","")消除了#符号,并且除去了前导和尾随空格,用户也犯了一个错误

非常方便运行这是循环导入和导出。

现在,如果文件名称正在更改(这是工作簿名称),或者如果Worksheet名称正在更改,则也可以让“中间件”始终将工作簿命名为相同的名称(包含要导入的工作簿的内容)与纸张相同,或者您可以统计纸张的数量并logging名称(再次有机会在“中间件”中对其进行标准化和重命名)

我想这不是一个SQL的答案,但因为我不是很好的SQL我会准备数据,在这种情况下,首先是一个Excel工作簿,并将其标准化为导入,所以代码不会在数据库端(服务器端)。

我使用excel作为访问SQL查询脚本的前端,它可以直接链接到SQL,但要困难得多。 像PostGre SQL这样的.CSV友好的数据库在这方面是有帮助的。

我希望这有帮助。 如果在导入之前需要帮助格式化工作簿,请复制并应用所有更改(命名,字段名称约定/列标题)告诉我。 我可以帮忙。

这与V在工作簿上运行预处理脚本的注释类似。 这就是我将如何处理它。

干杯,WWC