最大限度地缩短VBA-Access功能的执行时间

时间:2017-02-06 10:04:03

标签: sql arrays database vba ms-access

我在Access中的AutoExec宏上运行以下函数。

问题是运行它需要大约40秒(!!!),这对于大约200条记录来说太长了。

有人可以建议任何方法来最小化这个运行时间吗?

此代码的目标基本上是检查RefreshedDatas table中的每条记录,如果多个值'PartNo字段中的一个值存在于来自其他table的2个可能字段中(请参阅图片:我必须检查SparPartNo字段是否包含SerialPartNo字段的值)。如果是,我会在WPRC Part字段中写YES,否则为NO。

举个例子,

我检查并发现代码的第5部分是39秒,而前4部分需要1秒。

Function PopulationOfWPRCField()

'1) Create an access to the PartNo field (=Recordset) and store all its values in an array called arrayPartNo
    Dim conn As New ADODB.Connection
    Dim connStr As String
    Dim rs As ADODB.Recordset
    Dim PartNoSQL As String
    Dim arrayPartNo() As Variant

    connStr = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & "PathtoMyAccessDatabase\" & "NewVersion.accdb" & ";"
    conn.ConnectionString = connStr
    conn.Open
    Set rs = New ADODB.Recordset
    PartNoSQL = "SELECT PartNo FROM RefreshedDatas" '"SELECT PartNo FROM 12Dec"
    rs.Open PartNoSQL, conn, adOpenStatic, adLockReadOnly, adCmdText

    If Not rs.EOF Then
        arrayPartNo = rs.GetRows
    End If
    'Set rs = Nothing


'2) Same with fields SerialPartNo and SparePartNo from the WPRC_List (+ShipmentID for the 4th part of this code)
    Dim arraySerialPartNo() As Variant
    Dim arraySparePartNo() As Variant
    Dim arrayShipmentID() As Variant
    Dim SerialPartNoSQL As String
    Dim SparePartNoSQL As String
    Dim ShipmentIDSQL As String

    'For SpartNo

        Set rs = New ADODB.Recordset
        SparePartNoSQL = "SELECT SparePartNo FROM WPRC_Parts_List"
        rs.Open SparePartNoSQL, conn, adOpenStatic, adLockReadOnly, adCmdText
        If Not rs.EOF Then
            arraySparePartNo = rs.GetRows
        End If
        'For i = 0 To UBound(arraySparePartNo, 2)
        '    Debug.Print arraySparePartNo(0, i)
        'Next i
        Set rs = Nothing

    'For SerialNo

        Set rs = New ADODB.Recordset
        SerialPartNoSQL = "SELECT SerialPartNo FROM WPRC_Parts_List"
        rs.Open SerialPartNoSQL, conn, adOpenStatic, adLockReadOnly, adCmdText
        If Not rs.EOF Then
            arraySerialPartNo = rs.GetRows
        End If
        'For i = 0 To UBound(arraySerialPartNo, 2)
         '   Debug.Print arraySerialPartNo(0, i)
        'Next i
        Set rs = Nothing

    'For ShipmentID

    Set rs = New ADODB.Recordset
        ShipmentIDSQL = "SELECT [Shipment ID] FROM RefreshedDatas"  
        rs.Open ShipmentIDSQL, conn, adOpenStatic, adLockReadOnly, adCmdText
        If Not rs.EOF Then
            arrayShipmentID = rs.GetRows
        End If
        'For i = 0 To UBound(arrayShipmentID, 2)
         '   Debug.Print arrayShipmentID(0, i)
        'Next i
        Set rs = Nothing

        Set conn = Nothing

'3) We calculate the size of the GoodArray() we'll declare later on so that we can declare it properly
    Dim h As Integer
    Dim longest As Integer
    longest = 0
    For h = 0 To UBound(arrayPartNo, 2) ' in this loop we search for the longest character in arrayPartNo and store its size in "longest"
        If Len(arrayPartNo(0, h)) > longest Then longest = Len(arrayPartNo(0, h))
    Next h
    'MsgBox longest '63 in this case
    h = (longest + 1) / 8 ' since h was only used in the loop above we reuse it to store this = 8 -> size of the 2nd dimension of GoodArray() , it represents the biggest number of PartNo a cell in arrayPartNo contains
    longest = UBound(arrayPartNo, 2) ' same here, we just reuse the variable to store this -> size of the 1st dimension of GoodArray () , it represents the number of cells(=records) in arrayPartNo ( in the MainForm)



'4) Declaration of the 2-dimensional array GoodArray() and population of it
    Dim NumberOfPartNo As Integer ' Number of PartNo in a specific row
    Dim length As Integer ' length of a correct PartNo
    Dim i As Integer
    Dim GoodArray() As Variant ' this is a 2 dimensional array where 1st dimension contains UBound(arrayPartNo, 2) cells and 2nd dimension contains the number of PartNo (=NumberOfPartNo) for the row determined by the 1st dimension)
    ' reason for (longest+1)/8 : we have the following equation : x*7 + (x-1)= longest where x is the number of PartNo( but for the record which has the most PartNo) and (x-1) is the number of ";" separating all those PartNo, the total gives the number of character of the record (= longest for the record with the most PartNo)
   ReDim Preserve GoodArray(longest, h)
For i = 0 To UBound(arrayPartNo, 2)

    length = 7
    NumberOfPartNo = 0
    If Len(arrayPartNo(0, i)) > 0 Then
            Do
                GoodArray(i, NumberOfPartNo) = Mid(arrayPartNo(0, i), length + 1 - 7, 7)
                NumberOfPartNo = NumberOfPartNo + 1
                length = length + 8

            Loop While Len(arrayPartNo(0, i)) >= length ' since we use such a loop it allows any PartNo to go through it even if it has less than 6 characters
        End If
Next i

'5) Comparison of the arrays : For each element contained in GoodArray() check if it is included in arraySerialPartNo or arraySparePartNo
Dim j As Integer
Dim k As Integer
'Dim OnList As Boolean
For i = 0 To UBound(GoodArray, 1)
    k = 0
    'OnList = False
    Do Until GoodArray(i, k) = ""
        For j = 0 To UBound(arraySerialPartNo, 2)
            If arraySerialPartNo(0, j) = GoodArray(i, k) Then
                DoCmd.RunSQL "UPDATE RefreshedDatas Set [WPRC Part] = 'Yes' WHERE [Shipment ID] = " & arrayShipmentID(0, i)
                'OnList = True
                GoTo Prochain
            End If
        Next j

        For j = 0 To UBound(arraySparePartNo, 2)
            If arraySparePartNo(0, j) = GoodArray(i, k) Then
                DoCmd.RunSQL "UPDATE RefreshedDatas Set [WPRC Part] = 'Yes' WHERE [Shipment ID] = " & arrayShipmentID(0, i)                   
                'OnList = True
                GoTo Prochain
            End If
        Next j

        k = k + 1
    Loop

    DoCmd.RunSQL "UPDATE RefreshedDatas Set [WPRC Part] = 'No' WHERE [Shipment ID] = " & arrayShipmentID(0, i)        
Prochain:

Next i

End Function

2 个答案:

答案 0 :(得分:2)

没有真正的答复,但是......

IF 您的2张表格中有正确的主键,
IF 您在2个表之间定义了一个正确的关系(具有参照完整性)

那么你甚至不必检查是否匹配,你可以删除该代码。

无论如何,如果我理解的话,只需更新查询即可完成代码。我没有理由使用数组。

我甚至认为你不需要更新查询。只需使用SELECT查询LEFT JOIN,其中包含指示“匹配”或“不匹配”的公式

答案 1 :(得分:1)

  

此代码的目标基本上是检查中的每条记录   RefreshedDatas表,如果多个值中的一个值为'   PartNo字段存在于其他表的2个可能字段中(请参阅   图片中的结构:我要检查是否有SparPartNo字段   SerialPartNo字段包含值)。如果是的话,我会写   WPRC Part字段中为YES,否则为NO。

据我所知,你需要这个......

UPDATE  RefreshedDatas
SET WPRC = "NO"; // Everything is set to no.

现在测试这个sql,它将列出所有部件以及partno集合中找到的任何部件。 PartNo.Value是访问多值字段中项目的方法。

Select
R.SparePartNo
R.SerialPartNo
iif(isnull(R2.PartNo.value),"No", "Yes") as [Part found]
FROM RefreshedDatas as R left join RefreshedDatas as R2
    ON (R.SparePArtNo = R2.PartNo.Value OR R.SerialPartNo = R2.PArtNo.Value);

备份您的表并尝试此更新。与连接表类似,您也可以使用子查询从另一个表或同一个表中选择和查找值。

UPDATE RefreshedDatas
SET WPRC = "YES"
WHERE 
//if SparePartNo is available in the PartNo collection
    RefreshedDatas.SparePartNo in (SELECT P.PartNo.Value from RefreshedDatas as P WHERE P.ID = RefreshedDatas.ID)
// or if the SerialPartNo is available in the partNo Collection.
OR RefreshedDatas.SerialPartNo (SELECT S.PartNo.Value from RefreshedDatas as S WHERE S.ID = RefreshedDatas.ID)

没有办法测试代码,但你应该知道它在SQL中是如何工作的。 删除" P WHERE P.ID = RefreshedDatas.ID"如果您想在整个PartNo集合中搜索零件号而不是在同一行