1

我继承了一组 excel VBA 宏,它们从电子表格中获取数据并将数据上传到数据库(SQL DB)。问题是当数据“大”(特定工作表上的 46 列 * 10,500 行)时所花费的时间非常长。在我看来,将数据分块以将其放入数据库会更好,但这是正确的吗?如果是这样,最好的方法是什么?我目前正在尝试将以下代码封装在一个 for 循环中,将其分成 500 行,但这并不优雅,因为 vba 不是我的强项。

    Sub Upload_Claims()

    Dim SubmissionNumber As Integer
    Dim LoopVar As Integer, row As Integer

    Set cnnConn = New ADODB.Connection
    cnnConn.ConnectionString = "driver={SQL Server};server=" & Server & ";database=happyfunserver"
    cnnConn.Open

    SubmissionNumber = Sheets("Quality Check").Range("SubID").Value

    'Upload HPL - PPL
    Set cmdCommand = New ADODB.Command
    Set cmdCommand.ActiveConnection = cnnConn
    With cmdCommand
        .CommandText = "Select * from losses where submission_id = " & SubmissionNumber
        .CommandType = adCmdText
        .Execute
    End With

    ' Open the recordset.
    Set rstRecordset = New ADODB.Recordset
    Set rstRecordset.ActiveConnection = cnnConn
    rstRecordset.Open cmdCommand, , adOpenStatic, adLockBatchOptimistic

    'upload '

    Sheets("PL").Select
    row = 8

    Do While Range("C" & row).Value <> vbNullString

      With rstRecordset
        .AddNew
        .Fields("submission_id") = SubmissionNumber

        If Range("A" & row).Value <> vbNullString Then
            .Fields("tag_id") = Range("A" & row).Value
        End If
        If Range("B" & row).Value <> vbNullString Then
            .Fields("batch_tag_id") = Range("B" & row).Value
        End If
        If Range("C" & row).Value <> vbNullString Then
            .Fields("source") = Left(Range("C" & row).Value, 250)
        End If
        If IsDate(Range("D" & row).Value) Then
            .Fields("evaluation_date") = Range("D" & row).Value
        End If



        If Range("E" & row).Value <> vbNullString Then
          If Range("E" & row).Value = "HPL" Then
          .Fields("coverage_type_id") = 22
          ElseIf Range("E" & row).Value = "PL" Then
          .Fields("coverage_type_id").Value = 2
          End If
        End If
        '--------------'

        If Range("F" & row).Value <> vbNullString Then
            .Fields("claim_no") = Left(Range("F" & row).Value, 250)
        End If
        If Range("G" & row).Value <> vbNullString Then
            .Fields("claimant") = Left(Range("G" & row).Value, 200)
        End If


        'upload layer'

        If Range("H" & row).Value <> vbNullString Then
          If UCase(Range("H" & row).Value) = "UNKNOWN" Then
          .Fields("layer_id") = 0
          ElseIf UCase(Range("H" & row).Value) = "AAA" Then
          .Fields("layer_id") = 1
          ElseIf UCase(Range("H" & row).Value) = "BBBBBB" Then
          .Fields("layer_id") = 2
          ElseIf UCase(Range("H" & row).Value) = "CCCCC" Then
          .Fields("layer_id") = 3
          ElseIf UCase(Range("H" & row).Value) = "DDDDDDDD" Then
          .Fields("layer_id") = 4
          ElseIf UCase(Range("H" & row).Value) = "EEE" Then
          .Fields("layer_id") = 5
          End If
        End If
        '-------------------'

        If Range("I" & row).Value <> vbNullString Then
            .Fields("aaaaaaaa_name") = Left(Range("I" & row).Value, 100)
        End If
        If IsNumeric(Range("J" & row).Value) And Range("J" & row).Value <> 0 Then
            .Fields("bbb_id") = Left(Range("J" & row).Value, 7)
        End If
        If Not IsError(Range("K" & row).Value) Then
            .Fields("ccc_id_verified") = Range("K" & row).Value
        End If
        If Not IsError(Range("L" & row).Value) Then
            If Range("L" & row).Value <> vbNullString And Range("L" & row).Value <> 0 Then
                .Fields("dddddddd_city") = Left(Range("L" & row).Value, 80)
            End If
        End If
        If Range("M" & row).Value <> vbNullString And Range("M" & row).Value <> 0 Then
            .Fields("eeeeeeee_fips") = Left(Range("M" & row).Value, 5)
        End If
        If Not IsError(Range("N" & row).Value) Then
            If Range("N" & row).Value <> vbNullString And Range("N" & row).Value <> 0 Then
            .Fields("ffffffff_stateabbr") = Left(Range("N" & row).Value, 2)
            End If
        End If
        If IsDate(Range("O" & row).Value) Then
            .Fields("gggggggg_date") = Range("O" & row).Value
        End If
        If IsDate(Range("P" & row).Value) Then
            .Fields("hhhhhh_date") = Range("P" & row).Value
        End If
        If IsNumeric(Range("Q" & row).Value) Or Range("Q" & row).Value = 0 Then
            .Fields("iiiiiiiii_paid") = Range("Q" & row).Value
        End If
        If IsNumeric(Range("R" & row).Value) Or Range("R" & row).Value = 0 Then
            .Fields("jjjjjjjjj_reserve") = Range("R" & row).Value
        End If
        If IsNumeric(Range("S" & row).Value) Or Range("S" & row).Value = 0 Then
            .Fields("kkkk_paid") = Range("S" & row).Value
        End If
        If IsNumeric(Range("T" & row).Value) Or Range("T" & row).Value = 0 Then
            .Fields("llll_reserve") = Range("T" & row).Value
        End If


        'upload claim status'

        If Range("U" & row).Value <> vbNullString Then
           If UCase(Range("U" & row).Value) = "CLOSED" Then
           .Fields("status_id") = 1
           ElseIf UCase(Range("U" & row).Value) = "OPEN" Then
           .Fields("status_id") = 0
           ElseIf UCase(Range("U" & row).Value) = "REOPEN" Then
           .Fields("status_id") = 2
           End If
        End If
        '---------------------------'

        If IsDate(Range("V" & row).Value) Then
            .Fields("closed_date") = Range("V" & row).Value
        End If
        If Range("W" & row).Value <> vbNullString Then
            .Fields("description") = Range("W" & row).Value
        End If


            If IsNumeric(Range("AN" & row).Value) Then
                .Fields("manual") = Range("AN" & row).Value
            End If
            If IsNumeric(Range("AB" & row).Value) Then
                .Fields("11111") = Range("AB" & row).Value
            End If
            If IsNumeric(Range("AC" & row).Value) Then
                .Fields("2222222") = Range("AC" & row).Value
            End If
            If IsNumeric(Range("AD" & row).Value) Then
                .Fields("33333333333") = Range("AD" & row).Value
            End If
            If IsNumeric(Range("AE" & row).Value) Then
                .Fields("444444444") = Range("AE" & row).Value
            End If
            If IsNumeric(Range("AF" & row).Value) Then
                .Fields("55555555") = Range("AF" & row).Value
            End If
            If IsNumeric(Range("AG" & row).Value) Then
                .Fields("666666666") = Range("AG" & row).Value
            End If
            If IsNumeric(Range("AH" & row).Value) Then
                .Fields("7777777777777") = Range("AH" & row).Value
            End If
            If IsNumeric(Range("AI" & row).Value) Then
                .Fields("other") = Range("AI" & row).Value
            End If
            If IsNumeric(Range("AJ" & row).Value) Then
                .Fields("88") = Range("AJ" & row).Value
            End If
            If IsNumeric(Range("AK" & row).Value) Then
                .Fields("cause") = Range("AK" & row).Value
            End If
            If IsNumeric(Range("AL" & row).Value) Then
                .Fields("dept") = Range("AL" & row).Value
            End If
            If IsNumeric(Range("AM" & row).Value) Then
                .Fields("outcome") = Range("AM" & row).Value
            End If
            If IsNumeric(Range("AS" & row).Value) Then
                .Fields("report_lag") = Range("AS" & row).Value
            End If
            If IsNumeric(Range("AT" & row).Value) Then
                .Fields("closed_lag") = Range("AT" & row).Value
            End If


        .Update
    End With
    row = row + 1
    If row Mod 25 = 0 Then
        Application.StatusBar = "PL" & " - " & row
        DoEvents
    End If
Loop
Application.StatusBar = "Performing " & "PL" & " Batch Update..."

rstRecordset.UpdateBatch

'(Similar loop repeats for 5 different pieces)


End Sub

任何建议表示赞赏。我尽量保持简短,但是当你不知道自己真正在做什么或往哪个方向前进时,这很难。

4

1 回答 1

1

根据我们的对话以及我认为您的代码是如何工作的,这是一个您可以尝试的未经测试的解决方案,它可以通过将大部分处理转移到 SQL 来加快它的速度。不幸的是,您必须跳过已有的 in-recordset-processing 方法。根据谷歌的说法,你不能使用 ADO.Recordset 作为 SQL 查询的源(它们位于内存的不同部分并且看不到彼此)。所以,你可以试试这个:

  1. 在 SQL Server 上创建临时表。我们称它为 TblStaging 因为为什么不呢。此临时表的数据类型将只是大字符串字段,因此它可以保存您放入其中的任何内容,包括错误。

  2. 声明连接字符串后,立即尝试 JosieP 建议的 Insert 语句将数据加载到 TblStaging。之后注释掉所有的 VBA。

  3. 创建一个 .sql 文件,该文件将根据 VBA 中的规则验证您的数据,然后将其移动到您的永久 SQL 表中。(我假设你知道足够多的 SQL 能够做到这一点。)因为这一切现在都在 SQL 而不是 VBA 中,所以它应该快得多。

  4. 这个 sql 文件必须以某种方式运行。如果您不想每次都手动执行,有 2 个选项(假设您对 SQL Server 足够好):
    4a。弄清楚如何从命令行运行 .sql 并运行从 VBA 调用该命令行的批处理文件。
    4b。将其设置为定期重复代理。

于 2013-07-08T23:28:05.900 回答