【问题标题】:Fastest way to transfer Excel table data to SQL 2008R2将 Excel 表数据传输到 SQL 2008R2 的最快方法
【发布时间】:2012-05-29 07:13:21
【问题描述】:

有没有人知道使用外部实用程序(即 bcp)从 Excel 表(VBA 数组)获取数据到 SQL 2008 上表的最快方法? 请记住,我的数据集通常是 6500-15000 行,大约 150-250 列;我最终在自动 VBA 批处理脚本中传输了大约 20-150 个。

我尝试了几种方法将大量数据从 Excel 表 (VBA) 获取到 SQL 2008。我在下面列出了这些方法:

方法 1. 将表传入 VBA 数组并发送到存储过程 (ADO) -- 发送到 SQL 很慢

方法2. 创建断开连接的RecordSet 加载它,然后同步。 -- 发送到 SQL 非常慢

方法 3. 将表放入 VBA 数组,循环遍历数组并连接(使用分隔符)然后发送到存储过程。 -- 发送到 SQL SLOW,但比方法 1 或 2 快。

方法 4. 将表放入 VBA 数组,循环遍历数组并连接(使用分隔符),然后使用 ADO 记录集 .addnew 命令放置每一行。 -- 发送到 SQL 的速度非常快(比方法 1-3 快大约 20 倍),但现在我需要使用单独的过程拆分该数据,这将增加大量等待时间。

方法5. 将表放入VBA数组,序列化成XML,以VARCHAR形式发送到存储过程,并在存储过程中指定XML。 -- 发送到 SQL 非常慢(比方法 1 或 2 慢大约 100 倍)

我缺少什么吗?

【问题讨论】:

    标签: excel vba sql-server-2008 import large-data


    【解决方案1】:

    没有一种最快的方法,因为它取决于许多因素。确保 SQL 中的索引已配置和优化。许多索引会破坏插入/更新性能,因为每个插入都需要更新索引。确保只与数据库建立一个连接,并且在操作过程中不要打开/关闭它。在服务器负载最小时运行更新。您还没有尝试过的唯一其他方法是使用 ADO Command 对象,并发出直接的 INSERT 语句。使用记录集对象的“AddNew”方法时,请确保在插入结束时只发出一个“UpdateBatch”命令。除此之外,VBA 的运行速度只能与接受输入的 SQL 服务器一样快。

    编辑: 好像你已经尝试了一切。 SQL Server 中还有所谓的“批量记录”恢复模式,它减少了写入事务日志的开销。可能是值得研究的东西。这可能很麻烦,因为它需要稍微调整一下数据库恢复模型,但它可能对您有用。

    【讨论】:

    • -索引不是问题,因为我正在加载到临时表中-但是谢谢,我忘记了这一点。肯定只使用一个连接。至于 ADO 命令对象,我已经尝试过了,但发现它的速度与传递给 sp 没有什么不同。是的,在循环添加新内容后,我只发布了一个 ADO 更新批次。 .add 新方法过去是并且仍然是迄今为止最快的方法,但仅在与 concat 结合使用时才需要 - 稍后必须对其进行解析。
    • 我会研究一下批量日志恢复模式。
    【解决方案2】:

    到目前为止,最快的方法是通过 T-SQL 的BULK INSERT

    有一些注意事项。

    • 您可能需要先将数据导出到 csv(您可以直接从 Excel 导入;我的经验是从 Access .mdbs 转到 SQL Server,这需要中间步骤到 csv em>)。
    • SQL Server 机器需要有权访问该 csv(当您运行 BULK INSERT 命令并指定文件名时,请记住文件名将在运行 SQL Server 的机器上解析 )。
    • 您可能需要调整默认的 FIELDTERMINATORROWTERMINATOR 值以匹配您的 CSV。

    最初设置此设置对我来说需要反复试验,但与我尝试过的所有其他技术相比,性能提升是惊人的。

    【讨论】:

    • 谢谢,但 BCP 不是一个选项。我即时处理数千种格式,而 bcp 给我带来的只是麻烦,足以让我每次都造成灾难。我需要一些可以在批处理循环期间通过错误反馈进行控制的东西;这消除了大多数批量程序...尤其是 bcp。
    • 我没有意识到 BCP == BULK INSERT。也就是说,我最初与 BCP 合作时遇到了类似的问题。我不知道您的具体情况,但我解决了使用自定义行和字段终止符并在将数据导出到 csv 时“按摩”数据时遇到的问题。我尝试了您列出的大部分内容的变体,但性能从未接近 Bulk Insert。我同意 Bulk Insert 是“挑剔的”(至少可以这么说),并且批处理循环期间的错误反馈几乎是不可能的(不诉诸某种杂耍),但我认为值得再看看。祝你好运!
    【解决方案3】:

    以下代码将在几秒钟(2-3 秒)内传输数千条数据。

    Dim sheet As Worksheet
        Set sheet = ThisWorkbook.Sheets("DataSheet")        
    
        Dim Con As Object
        Dim cmd As Object
        Dim ServerName As String
        Dim level As Long
        Dim arr As Variant
        Dim row As Long
        Dim rowCount As Long
    
        Set Con = CreateObject("ADODB.Connection")
        Set cmd = CreateObject("ADODB.Command")
    
        ServerName = "192.164.1.11" 
    
        'Creating a connection
        Con.ConnectionString = "Provider=SQLOLEDB;" & _
                                        "Data Source=" & ServerName & ";" & _
                                        "Initial Catalog=Adventure;" & _
                                        "UID=sa; PWD=123;"
    
        'Setting provider Name
         Con.Provider = "Microsoft.JET.OLEDB.12.0"
    
        'Opening connection
         Con.Open                
    
        cmd.CommandType = 1             ' adCmdText
    
        Dim Rst As Object
        Set Rst = CreateObject("ADODB.Recordset")
        Table = "EmployeeDetails" 'This should be same as the database table name.
        With Rst
            Set .ActiveConnection = Con
            .Source = "SELECT * FROM " & Table
            .CursorLocation = 3         ' adUseClient
            .LockType = 4               ' adLockBatchOptimistic
            .CursorType = 0             ' adOpenForwardOnly
            .Open
    
            Dim tableFields(200) As Integer
            Dim rangeFields(200) As Integer
    
            Dim exportFieldsCount As Integer
            exportFieldsCount = 0
    
            Dim col As Integer
            Dim index As Integer
            index = 1
    
            For col = 1 To .Fields.Count
                exportFieldsCount = exportFieldsCount + 1
                tableFields(exportFieldsCount) = col
                rangeFields(exportFieldsCount) = index
                index = index + 1
            Next
    
            If exportFieldsCount = 0 Then
                ExportRangeToSQL = 1
                GoTo ConnectionEnd
            End If            
    
            endRow = ThisWorkbook.Sheets("DataSheet").Range("A65536").End(xlUp).row 'LastRow with the data.
            arr = ThisWorkbook.Sheets("DataSheet").Range("A1:CE" & endRow).Value 'This range selection column count should be same as database table column count.
    
            rowCount = UBound(arr, 1)            
    
            Dim val As Variant
    
            For row = 1 To rowCount
                .AddNew
                For col = 1 To exportFieldsCount
                    val = arr(row, rangeFields(col))
                        .Fields(tableFields(col - 1)) = val
                Next
            Next
    
            .UpdateBatch
        End With
    
        flag = True
    
        'Closing RecordSet.
         If Rst.State = 1 Then
           Rst.Close
        End If
    
       'Closing Connection Object.
        If Con.State = 1 Then
          Con.Close
        End If
    
    'Setting empty for the RecordSet & Connection Objects
    Set Rst = Nothing
    Set Con = Nothing
    End Sub
    

    【讨论】:

    • 这似乎是从 SQL Server 将数据写入 Excel,而不是像问题所问的那样。
    • @thursdaysgeek 不,这正是问题所在。
    【解决方案4】:

    工作得很好,另一方面为了提高速度,我们仍然可以修改查询:

    改为:Source = "SELECT * FROM " & Table

    我们可以使用:Source = "SELECT TOP 1 * FROM " & Table

    这里我们只需要列名。因此无需对整个表进行查询,只要导入新数据就可以扩展流程。

    【讨论】:

      【解决方案5】:

      据我记忆,你可以创建一个链接到Excel文件的服务器(只要服务器能找到路径;最好把文件放在服务器的本地磁盘上),然后使用SQL从中检索数据它。

      【讨论】:

        【解决方案6】:

        刚刚尝试了几种方法,我又回到了一种相对简单但速度很快的方法。它速度很快,因为它让 SQL 服务器完成所有工作,包括高效的执行计划。

        我只是构建了一个包含 INSERT 语句脚本的长字符串。

            Public Sub Upload()
                Const Tbl As String = "YourTbl"
                Dim InsertQuery As String, xlRow As Long, xlCol As Integer
                Dim DBconnection As New ADODB.Connection
        
                DBconnection.Open "Provider=SQLOLEDB.1;Password=MyPassword" & _
                    ";Persist Security Info=false;User ID=MyUserID" & _
                    ";Initial Catalog=MyDB;Data Source=MyServer"
        
                InsertQuery = ""
                xlRow = 2
                While Cells(xlRow, 1) <> ""
                    InsertQuery = InsertQuery & "INSERT INTO " & Tbl & " VALUES('"
        
                    For xlCol = 1 To 6 'Must match the table structure
                        InsertQuery = InsertQuery & Replace(Cells(xlRow, xlCol), "'", "''") & "', '"  'Includes mitigation for apostrophes in the data
                    Next xlCol
                    InsertQuery = InsertQuery & Format(Now(), "M/D/YYYY") & "')" & vbCrLf 'The last column is a date stamp, either way, don't forget to close that parenthesis
                    xlRow = xlRow + 1
                Wend
        
                DBconnection.Execute InsertQuery 'I'll leave any error trapping to you
                DBconnection.Close  'But do be tidy :-)
                Set DBconnection = Nothing
            End Sub
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-07-06
          • 1970-01-01
          • 1970-01-01
          • 2012-11-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多