我有SSIS包,它会将excel文件加载到数据库中。我创建了Excel Source任务,将excel列名映射到Database表列名,并且工作正常。
在极少数情况下,我们收到的excel文件列名称带有一些空格(例如:列名是" ABC"但我们正在接收" ABC") 导致映射问题,SSIS失败。
是否可以在不打开Excel的情况下修剪列名。
注意:页面名称将是动态的,列位置可能会更改(例如:列" ABC可能存在于第一行或第二行或..")。
答案 0 :(得分:4)
这已在MSDN中很好地记录,运行类似于@houseofsql提到的步骤
步骤1:
在excel连接的第一行中排除列名,使用sql命令作为数据访问模式
步骤2:输出列中的别名列名称与您的目的地匹配,
从[Sheet1$A2:I]
中选择*将从第二行中选择
最后将目的地添加为OLEDB目的地
答案 1 :(得分:4)
首先,我的解决方案基于@DrHouseofSQL和@Bhouse答案,所以你必须首先阅读@DrHouseofSQL答案然后@BHouse回答然后继续这个答案
注意:页面名称将是动态的,列位置可能会更改(例如:列“ABC”可能存在于第一行或第二行或...
这种情况有点复杂,可以使用以下解决方法解决:
Delay Validation
属性设置为true )@[User::strQuery]
作为ReadWrite变量,@[User::ExcelFilePath]
作为ReadOnly Variable (在脚本任务窗口中) 注意:您必须导入System.Data.OleDb
在下面的代码中,我们搜索excel前15行以查找标题,如果在15行之后可以找到标题,则可以增加数量。此外,我假设列范围从A
到I
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
Select * from [Sheet1$A2:I]
分配给变量@[User::strQuery]
@[User::strQuery]
Delay Validation
属性设置为True
来自OP评论:sometimes excel with empty data will come.(i.e) we have only header row not not data... in that case it fails entire task
<强>解决方案:强>
如果您的Excel文件不包含任何数据(仅限标题),则必须执行以下步骤:
@[User::ImportFile]
)@[User::ImportFile]
添加到脚本任务ReadWrite variables @[User::ImportFile]
= True,否则@[User::ImportFile]
= False 编写以下表达式
@[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
来自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
<强>解决方案:强>
@[User::ImportFile] == False
(第一个连接器的相同步骤)将此数据流与脚本任务连接 或者您可以添加Data Flow Task
以在日志表中插入行
Execute SQL Task
。
答案 2 :(得分:2)
是手动还是自动创建文件? 在任何一种情况下,您都可以从Excel文件中删除标题行(以编程方式或告诉人们在保存文件之前将其删除)。 完成后,进入Excel连接管理器,找到指示“第一行有列名”的框。如果您可以清除该框,则将列再次映射到应解决问题的目标位置。您永远不必担心列名称中拼写错误(或多余的空格)。
我认为SSIS中还有一个选项可以完全跳过第一行,但我不记得那个选项在哪里。如果您能找到,那么只需跳过Excel文件的第一行。同样的映射仍然存在。
谢谢
答案 3 :(得分:1)
我对这个论坛很新,所以如果你认为这很愚蠢,那就把它当作一粒盐。
MS Access具有与Excel相同的VBA功能,或者您可以编写一个新的存根Excel工作簿,该工作簿在SQL导入之前进行解析和格式化,然后导入(如果您愿意,则为中间件)。
关于尾随或领先空间的问题,我在很多场合使用了以下内容:
myString = trim(msytring)
'这将删除所有前导和尾随空格,但不会乱用字符之间的任何空格。因此,在导入时,您可以在导入它们时对列标题运行修剪。
还有LTrim和RTrim'你可以猜到那些字符串的左右两边
对于大写,您可以使用UCase
myString = UCase(Trim(myString))
如果存在我经常处理的情况,那么替换总是派上用场,有时候用户可能会使用#char,有时候不会。
示例:“Patterson#288”或“PatTeRson 288”
myString = UCase(Trim(Replace(myString,"#","")
'消除#符号并删除前导和尾随空格,并在用户也犯错误的情况下将字母大写
非常方便地运行它是循环导入和导出。
现在,如果文件名正在更改(这是工作簿名称)或者工作表名称正在更改,您也可以让“中间件”始终将工作簿命名为相同的名称(包含您的工作簿内容)要导入)与工作表相同,或者你可以计算工作表数并记录名称(再次有机会在“中间件”中标准化和重命名)
我认为这不是SQL的答案,但是因为我对SQL不太好,我会准备数据,在这种情况下首先是excel工作簿并将其标准化以便导入,这样代码就不会在数据库方面中断(服务器端)。
我使用excel作为Access with SQL查询脚本的前端,它可以直接链接到SQL但是要困难得多。像PostGre SQL这样的.CSV友好数据库在这方面有所帮助。
我希望这会有所帮助。如果在导入之前需要帮助格式化工作簿,请复制并应用所有更改(命名,字段名称约定//列标题),请告诉我。我可能会帮忙。
这类似于V在工作簿上运行预处理脚本的注释。这就是我接近它的方式。
干杯, WWC