【问题标题】:Handle "big" Recordset from Access in VBA, DoEvents在 VBA、DoEvents 中处理来自 Access 的“大”记录集
【发布时间】:2016-12-22 06:01:50
【问题描述】:

我的目标是计算来自我们的呼叫经理的每个电话的电话费用。该脚本适用于小型表 (

我的调用表有 160k 行。 q_get_call 结果有 6 列。该脚本每次都运行良好,直到第 7431 行。我在较小的桌子上尝试过,它也完成了他的工作。如果我不使用 DoEvents 函数,Access 将不再响应,但脚本也只会在 30 分钟后运行到第 8000 行左右。

一般问题:vba 中是否有一些重要的东西,比如在每次循环后将变量设置回 0,来处理这样的数据大小?

我已经将查询减少到所需的 4 列。可能是 Select Case 语句更好?或者有没有比我的 insertSQL 方法更好的方法来将成本更新到 call_table 中?

如果我在几分钟后暂停脚本,它就会卡在 DoEvents() 函数上。

Set Datenbank = CurrentDb
Set rs_calls = Datenbank.OpenRecordset("q_get_called", dbOpenDynaset)
    'Loop through all call-data to calculate cost
    Do While Not rs_calls.EOF
    callID = rs_calls!globalCallID_CallId
    internal = rs_calls!IfInternal
    inside = rs_calls!FromInside
    If inside = 0 Or internal = -1 Then 'cost is Zero if call coming from outside or is internal
        cost = 0
    Else 'else proceed calculation
        callingNr = rs_calls!callingPartyNumber
        calledNr = rs_calls!finalCalledPartyNumber
        duration = rs_calls!duration

        If Left(calledNr, 3) Like "0??" Then 'domestic
            multiplier = 1
        ElseIf Left(calledNr, 3) Like "00?" Then multiplier = 5 'national
        ElseIf Left(calledNr, 3) Like "000" Then multiplier = 10 'international
        Else: multiplier = 0
        End If
        'Select Case Left(calledNr, 3) 'Maybe Select case is smarter?
        '    Case "0??"
        '        multiplier = 1
        '    Case "00?"
        '        multiplier = 5
        '    Case "000"
        '        multiplier = 10
        '    Case Else
        '        multiplier = 0
        'End Select
        cost = (duration / 60) * multiplier  
    End If
    insertSQL = "UPDATE tbl_cdr SET cost = " & cost & " WHERE globalCallID_callID = " & callID
    Datenbank.Execute (insertSQL) 'set cost column
    rs_calls.MoveNext
    subForm = DoEvents() 'pass control to OS
Loop

【问题讨论】:

  • 您可以使用代码尝试一些小事情 - 使用 WITH rs_calls 块并创建参数更新查询,但我认为您最好的选择是查看交易 - 一次执行 2000-3000 个。话虽如此,如果您可以复制/粘贴 SQL 用于记录集查询q_get_called,我会发现它很有用。我认为您的更新足够基本,可以通过一个查询完成 - 但我不知道您的查询与您的 tbl_cdr 表有什么关系。
  • 另外 - If Left(calledNr, 3) Like "0??" 你想要完成什么 - 该条款将导致您的国内和国际号码为真 - 所以所有号码都将被视为国内号码。您的案例陈述说它们必须等于“0??”,依此类推。这就是你想要的,而不是 Likeany character wildcard,“?”
  • 继续!实现飞跃!尝试将其迁移到 SQL Server express(免费)之类的东西中。您仍然可以在前端使用 Access,但可以使用 T-SQL 替换此 VBA。如果您希望您的记录数会随着时间的推移而增加,那么您确实需要将其转移到一个更具可扩展性的平台上。您甚至可以试用免费的 Azure 云订阅,而无需安装任何东西
  • @dbmitch 我将看看 Transactions 和 WITH 块,我的 q_get_called 是一个简单的 Select ... From tbl_cdr ,我在 if 子句上犯了一个重大错误,我现在也意识到了:o @Nick .McDermaid 我已经预料到了这个建议。未来的记录计数不会大幅增加。现在我需要建立一个快速的解决方案。如果我能再次获得这个项目的资源,我肯定会设置一个 SQL Server。
  • @dbmitch calledNr 是一个最多 14 个字符的字符串,它可以以 "b","+","0","00","000" 或任何其他数字开头。对于计算,只有“0;00;000”的情况是相关的。如果是其中一种情况,则仅将 multiplier 设置为相关值“1;5;10”。

标签: loops ms-access vba recordset


【解决方案1】:

因此,作为使用更新查询直接在访问中执行所有操作的解决方案目前可以正常工作。

UPDATE tbl_cdr SET tbl_cdr.cost = IIf(Left([tbl_cdr].[calledNumber],3)="000",
([tbl_cdr].[duration]/60)*10, IIf(Left([tbl_cdr].[calledNumber],2)="00",
([tbl_cdr].[duration]/60)*5, IIf (Left([tbl_cdr].[calledNumber],1)="0",
[tbl_cdr].[duration]/60,0)));

不到 1 分钟就解决了。 不过,我的计算会变得更加复杂。就像用callingNumber 分隔调用一样,并根据它们使用不同的乘数常量。

编辑: 我现在实现了更多的逻辑,并努力在一个 SQL 更新语句中完成所有事情

是否可以像这样在更新语句中输入子查询:

UPDATE tbl_cdr SET [tbl_cdr].[cost]=
 IIf([tbl_cdr].[fromInside]=-1,
  IIf(Left([tbl_cdr].[fncpn],3)="000",
   ([tbl_cdr].[duration]/60)*
   (SELECT international FROM [tbl_cc] WHERE ccc=Left(cpn,4);),
   IIf(Left([tbl_cdr].[fncpn],2)="00",
    ([tbl_cdr].[duration]/60)*
    (SELECT national FROM [tbl_cost] WHERE ccc=Left(cpn,4);),
     IIf(Left([tbl_cdr].[fncpn],1)="0",
     ([tbl_cdr].[duration]/60)*
     (SELECT domestic FROM [tbl_cost] WHERE ccc=Left(cpn,4);),
     0)
   )
  )
 ,0)
;`

ErrorLog:“操作必须使用可更新的查询”

-我确实拥有文件夹的所有权限

-链接表包含主键

或者将 IIf-clauses 与 SQL 混合使用不是正确的方法?

因为解决方案可能会为每个不同的callingCountry 进行查询

像这样:

UPDATE tbl_cdr AS cdr SET cdr.cost = cdr.duration* (SELECT cc.international FROM tbl_costConstant AS cc WHERE cc.callingCountryCode = 1760) WHERE cdr.finalCalledPartyNumber Like "000%" AND cdr.callingPartyNumber Like "1760%";

不过,这似乎是一个很大的解决方法。

【讨论】:

  • 请将新问题作为新问题提出。 :) 但首先,将 SELECT 语句替换为 DLookup 调用。
  • 竖起大拇指 Dlookup 工作完美。谢谢@Andre
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-03-02
  • 1970-01-01
  • 2011-02-05
  • 1970-01-01
  • 2017-03-06
  • 2011-06-18
  • 2015-12-13
相关资源
最近更新 更多