正在导入具有变量 headers 的 excel 个文件

Importing excel files having variable headers

我有 SSIS 包,它将 excel 文件加载到数据库中。我已经创建了 Excel 源任务以将 excel 列名称映射到数据库 table 列名称及其工作正常。

在极少数情况下,我们收到 excel 文件列名和一些 space (例如:列名是 "ABC" 但我们收到 "ABC ") 导致映射问题和 SSIS 失败。

是否可以在不打开 excel.

的情况下 trim 列名

注意:页面名称将是动态的,列位置可能会更改(例如:列 "ABC may exist in first row or second row or ..")。

文件是手动创建的还是自动创建的? 在任何一种情况下,您都可以从 Excel 文件中完全删除 header 行(以编程方式或告诉人们在保存文件之前将其删除)。 完成后,进入 Excel 连接管理器并找到指示 'First row has column names' 的框。如果您可以清除该框,然后将列再次映射到应该解决您的问题的目的地。您永远不必担心列名拼写错误(或多余空格)。

我认为 SSIS 中还有一个选项可以完全跳过第一行,但我不记得那个选项在哪里。如果你能找到,那么就跳过 Excel 文件的第一行。相同的映射仍然存在。

谢谢

这在 MSDN 中有很好的记录,运行 通过类似于@houseofsql 提到的步骤

第一步:

排除excel连接中第一行的列名,使用sql命令作为数据访问模式

第 2 步: 将输出列中的列别名与您的目标相匹配,

Select * 来自 [Sheet1$A2:I] 将 select 来自第二行

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

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

问题

Note : Page name will be dynamic and Column position may change (eg: Column "ABC may exist in first row or second row or ...

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

解决方案概述

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

解决方案详细信息

  1. 首先创建一个字符串类型的SSIS变量(即@[User::strQuery])
  2. 添加另一个包含 Excel 文件路径的变量 (即 @[User::ExcelFilePath])
  3. 添加脚本任务,select @[User::strQuery] 作为读写变量,@[User::ExcelFilePath] 作为只读变量 (在脚本任务中 window)
  4. 将脚本语言设置为 VB.Net 并在脚本编辑器中 window 编写以下脚本:

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

在下面的代码中,我们搜索excel的前15行找到header,如果可以找到header则可以增加数字在 15 行之后。我还假设列的范围是从 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 连接管理器,并选择要导入的 excel 文件 (只是 select 一个示例来定义仅限首次元数据)
  2. 将默认值Select * from [Sheet1$A2:I]赋给变量@[User::strQuery]
  3. 在数据流任务中添加一个Excel源,从变量中选择SQL命令,然后select@[User::strQuery]
  4. 转到列选项卡并按照@BHouse 建议的方式命名列

图片来自@BHouse 回答

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

更新 1:

来自 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 文件不包含任何数据(仅 header),您必须执行以下步骤:

  1. 添加布尔类型的 SSIS 变量 *(即 @[User::ImportFile]
  2. 添加@[User::ImportFile]到脚本任务读写变量
  3. 在脚本任务中检查文件是否包含行
  4. 如果是,则设置 @[User::ImportFile] = True,否则 @[User::ImportFile] = False
  5. 双击将脚本任务连接到 DataFlow 的箭头(优先约束)
  6. 将其类型设置为约束和表达式
  7. 写出下面的表达式

    @[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. 只需添加另一个数据流任务
  2. 使用另一个连接器和表达式 @[User::ImportFile] == False (与第一个连接器相同的步骤)将此数据流与脚本任务连接起来
  3. 在数据流任务中添加脚本组件作为源
  4. 创建要导入日志的输出列
  5. 创建包含您需要导入的信息的行
  6. 添加日志目标

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

我是论坛的新手,所以如果您认为这很愚蠢,请持保留意见。

MS Access 与 Excel 具有许多相同的 VBA 功能,或者您可以编写一个新的存根 Excel 工作簿,在 SQL 导入和然后导入那个(如果你愿意的话,一个中间件)。

关于尾随或前导空格的问题,我曾多次使用以下内容:

myString = trim(msytring) '这将删除所有前导和尾随空格,但不会混淆字符之间的任何空格。因此,在导入时,您可以在导入时在 header 列上 运行 trim。

还有 LTrim 和 RTrim '你可以猜猜它们在字符串的左边和右边做了什么

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

对于大写你可以使用 UCase

myString = UCase(Trim(myString))

如果出现这样的情况,替换总是派上用场,因为我经常处理用户有时可能使用 # 字符而有时不使用的情况。

示例:“Patterson #288”或“PatTeRson 288” myString = UCase(Trim(Replace(myString,"#","") '消除 # 符号并去掉前导和尾随空格,并将字母大写以防用户也犯错

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

现在,如果文件名发生变化(这是工作簿名称)或工作表名称发生变化,您还可以让 "middleware" 始终将工作簿命名为相同的名称(包含您要导入的工作簿)与工作表相同,或者您可以计算工作表的数量并记录名称(再次有机会在您的 "middle ware" 中对它们进行标准化和重命名)

我想这不是 SQL 的答案,但因为我对 SQL 不太擅长,所以我会准备数据,在这种情况下,首先是 excel 工作簿并标准化它用于导入,因此代码不会在数据库端(服务器端)中断。

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

希望对您有所帮助。如果您在导入之前需要帮助格式化工作簿,方法是制作副本并应用所有更改(命名、字段名称约定//列 header),请告诉我。我或许可以提供帮助。

这类似于 V 在工作簿上对 运行ning 一个 pre-processing 脚本的评论。这就是我的处理方式。

干杯, 世界锦标赛