【问题标题】:VBA Iteration vs SQL Speed in AccessAccess 中的 VBA 迭代与 SQL 速度
【发布时间】:2016-12-05 04:31:49
【问题描述】:

目标:我有一堆日期,我想按类别更新最小日期的记录:

JVID    APDATE      TAG     into >      JVID    APDATE      TAG
1       201501      Use                 1       201501      Don't Use
1       201502      Use                 1       201502      Use
1       201502      Use                 1       201502      Use
1       201503      Use                 1       201503      Use
2       201502      Use                 2       201502      Don't Use
2       201503      Use                 2       201503      Use

我使用的方法如下: 我创建了一个字典,其中 Key = ID,Value = MinDateByID 然后我循环遍历键(对于字典中的每个键)并为每个 ID 运行更新查询,检查 IIF 语句根据与最小日期匹配的日期更新使用/不使用。

这行得通,但是使用 +80k ID 覆盖 +1M 记录需要很长时间。

我正在考虑运行相同的东西,但转储 SQL 并仅遍历记录,但我无法想象这会更快?

我正在寻找 SQL 或 VBA 建议。

提前谢谢你!

编辑 - 从评论中添加 SQL UPDATE [FY16 Q12 BE] SET [FY16 Q12 BE].[Record Use] = IIF([FY16 Q12 BE].[Date] = "201601", "Use", "Don't Use") WHERE ([FY16 Q12 BE].[ID]="20165645699");

我浏览了每个字典键/值对 ex (20165645699, 201601) 以各种形式创建和运行此脚本 80k+ 次

【问题讨论】:

  • 这是 SQL 擅长的事情,但我不太明白您要做什么。您想在每个 ID 的最早日期有一个“使用”,还是什么?
  • @grahmaj42 是的,我想在最早的日期“使用”,有时最早的日期也会重复多次,所有这些项目都应该标记为使用。我认为这是一个非常清晰的 SQL 应用程序,但我的脚本非常慢——几个小时仍未完成。
  • 您使用的是什么“永远需要”的 SQL?
  • @dbmitch UPDATE [FY16 Q12 BE] SET [FY16 Q12 BE].[记录使用] = IIF([FY16 Q12 BE].[会计期间] = "201601", "Include", " Dup Perm JV") WHERE ([FY16 Q12 BE].[ID]="20165645699");我浏览了每个字典键/值对,基本上以各种形式创建和运行此脚本 80k 次
  • 如果您要添加新的 SQL 和数据 - 请编辑您的问题并将其添加到那里。在 cmets 区域不可读

标签: sql vba ms-access sql-update


【解决方案1】:

MS Access 在连接更新方面比主流数据库更严格,所以我不得不使用临时表 T2 来保存最小值。

SELECT T1.ID, MIN(T1.RDate) AS MinDate INTO T2 FROM Test1 GROUP BY T1.ID;

现在我可以执行联合更新​​了:

UPDATE T1 LEFT JOIN T2 ON T1.ID=T2.ID AND T1.RDate=T2.MinDate;

最后,我删除了临时表:

DROP T2; SET TAG = IIF(T2.ID IS NULL, "Don't Use", "Use");

[我已将您的表命名为 T1 和日期字段 RDate 以避免与保留字冲突。]

这可以通过在 (ID, MinDate) 上的 T2 上添加主键和 (ID, RDate) 上的 T1 上的索引来进一步加快速度。

【讨论】:

  • 有道理,让我实现,我会接受答案
【解决方案2】:

我认为您可以通过一个更新查询来做到这一点 - 或者至少在一个 SQL 语句中组合多个查询。

我将使用您的示例数据,因为我无法弄清楚您的 cmets 中的实际表或字段名称。

您需要将 Table4 替换为您的表名 - 以及 ID/日期/标签字段以匹配您的列名。

更新 SQL:

UPDATE Table4 SET Table4.Tag = "Don't Use"
WHERE ([Date] & "-" & [ID]) 
In (SELECT MergeID FROM 
    (SELECT Mins.[MinOfDate] & "-" & [ID] AS MergeID
        FROM 
        (SELECT Table4.ID, Min(Table4.Date) AS MinOfDate
            FROM Table4
            WHERE Table4.Tag="Use"
            GROUP BY Table4.ID) AS Mins) AS Merges);

如果您不需要条件仅检查未更改的 TAG,则可以取消条件WHERE Table4.Tag="Use"

其他“无标签检查”选项

更新 SQL:

UPDATE Table4 SET Table4.Tag = "Don't Use"
WHERE ([Date] & "-" & [ID]) 
In (SELECT MergeID FROM 
    (SELECT Mins.[MinOfDate] & "-" & [ID] AS MergeID
        FROM 
        (SELECT Table4.ID, Min(Table4.Date) AS MinOfDate
            FROM Table4
            GROUP BY Table4.ID) AS Mins) AS Merges);

【讨论】:

  • 我在我的表上使用了底部查询——800k条记录它已经运行了一个小时
  • 日期字段是否有索引?
  • 它已经运行了大约 3:15,进度条约为 20%,这表明这不是一个可行的解决方案。不,日期字段尚未编入索引 - 我现在就试试。
  • 呸。 SQL Server 的时间差不多了
【解决方案3】:

我想提出这个建议。但我看到你有重复的日期,我不确定你打算如何处理最早的日期。

update [FY16 Q12 BE]
set TAG = "Don't use"
where not exists (
    select 1 from [FY16 Q12 BE] as t2
    where t2.ID = [FY16 Q12 BE].ID and t2.[DATE] < [FY16 Q12 BE].[DATE]       
)

【讨论】:

    【解决方案4】:

    我选择了一种迭代方法 - 我不确定为什么它比上面概述的 SQL 选项快得多,但它确实有效。感谢您的反馈。

    Sub MinAPInclude(ByVal Tablename As String)
    Dim db As DAO.Database
    Dim qd As DAO.QueryDef
    Dim rs As DAO.Recordset
    Dim strList As String
    Dim JVMinAP As Dictionary
    Set JVMinAP = New Dictionary
    Set db = DBEngine(0)(0)
    Dim rst As DAO.Recordset
    
    If Not DoesFieldExist(Tablename, "APDate") Then Exit Sub
    SQLStatement = "SELECT [" & Tablename & "].[JVID], Min([" & Tablename & "].[APDate]) AS TargetAP"
    SQLStatement = SQLStatement & " FROM [" & Tablename & "]"
    SQLStatement = SQLStatement & " GROUP BY [" & Tablename & "].[JVID];"
    
    Set qd = db.CreateQueryDef("", SQLStatement)
    Set rs = qd.OpenRecordset
    
    rs.MoveFirst
    Do Until rs.EOF
        If Not IsNull(rs("JVID")) Then
            If Not JVMinAP.Exists(CStr(rs("JVID"))) Then
                MinAP = rs("TargetAP")
                JVMinAP.Add CStr(rs("JVID")), MinAP
            End If
        End If
        rs.MoveNext
    Loop
    rs.Close
    
    Set rst = db.OpenRecordset(Tablename)
    rst.MoveFirst
    Do Until rst.EOF
        If rst("Record Use") <> "Include" Then
            If rst("APDate") = JVMinAP(CStr(rst("JVID"))) Then
                rst.Edit
                    rst("Record Use") = "Include"
                rst.Update
            End If
        End If
    rst.MoveNext
    Loop
    
    rst.Close
    Set rst = nothing
    Set rs = Nothing
    Set qd = Nothing
    Set db = Nothing
    End Sub
    

    【讨论】:

      猜你喜欢
      • 2011-02-06
      • 2012-08-26
      • 1970-01-01
      • 2013-03-15
      • 2014-10-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-07-12
      相关资源
      最近更新 更多