ms-access - 微软 Access : Is there a significant overhead when using CurrentDB as opposed to DBEngine(0)(0)?

标签 ms-access

来自 David W Fenton 的 answer SU 问题 Does MS Access 2003 contain a general purpose SQL console

The problem with using CurrentDB as your execution object is that it returns a new database object each time you call it



我的问题是:使用 CurrentDb 执行 SQL 或打开记录集时是否有开销,我应该避免它吗?

最佳答案

不清楚你所说的“开销”是什么意思,所以我不知道有人怎么能像措辞一样回答你的问题。

但多年来,在 Access 新闻组中,DBEngine(0)(0) 与 CurrentDB 的主题已被广泛讨论。我很久以前就对使用 CurrentDB 感到平静,所以我将总结我所看到的情况。

在这段代码中,DBEngine(0)(0) 比 CurrentDB 快得多:

  Dim db As DAO.Database
  Dim i As Integer

  Debug.Print "Start CurrentDB: " & Now()
  For i = 1 to 1000
    Set db = CurrentDB
    Set db = Nothing
  Next i
  Debug.Print "End CurrentDB: " & Now()

  Debug.Print "Start DBEngine(0)(0): " & Now()
  For i = 1 to 1000
    Set db = DBEngine(0)(0)
    Set db = Nothing
  Next i
  Debug.Print "End DBEngine(0)(0): " & Now()

如果我没记错的话,ADH97 说 DBEngine(0)(0) 大约快 17 倍。

但是看看那个代码——它没有测试任何有用的东西。请记住,CurrentDB 和 DBEngine(0)(0) 都返回指向当前在 Access UI 中打开的数据库的指针(以下针对 DBEngine(0)(0) 有一些注意事项)。Access 应用程序中没有任何位置这些循环中的任何一个都会以任何方式有用。在实际代码中,你这样做:
  Dim db As DAO.Database
  Dim rs As DAO.Recordset

  Set db = CurrentDB
  Set rs = db.OpenRecordset("a SQL SELECT")
  [do something with the recordset]
  rs.Close
  Set rs = db.OpenRecordset("another SQL SELECT")
  [do something with this other recordset]
  rs.Close
  Set rs = Nothing
  db.Execute("A SQL DML statement")
  Debug.Print db.RecordsAffected
  Set db = Nothing

虽然 DBEngine(0)(0) 在循环中可能快 1700%,但这无关紧要,因为您永远不会重复返回对当前在 Access UI 中打开的数据库的引用足够多的时间,从而使差异变为几乎可以忽略不计(我们在这里谈论的是毫秒,当然,对于具有更多对象的数据库,CurrentDB 将花费更长的时间)。

因此,首先,在我解释为什么存在差异之前,您必须首先认识到性能差异是完全无关紧要的,因为它可以超过最微不足道的差异的唯一情况是脑死的愚蠢代码的情况。

现在,为什么不同?

嗯,有两个主要原因:
  • DBEngine(0)(0) 返回在用户界面中当前打开的数据库首次打开时初始化时的集合,除非您手动刷新集合。所以,如果你添加一个新保存的 QueryDef,为了它在使用 DBEngine(0)(0) 的代码中可用,在添加新的 QueryDef 之后你必须调用
    DBEngine(0)(0).QueryDefs.Refresh
    Before that, your new query won't be in the QueryDefs collection, but after it, it will. CurrentDB, on the other hand, refreshes all collections each time it is called, so you never have to worry about refreshing any of your collections.

  • DBEngine(0)(0) returns the internal pointer the Access Jet workspace uses to point to the database currently open in the Access UI. CurrentDB returns a copy of the database structure, and each call to CurrentDB creates a new copy. Thus, CurrentDB will use more memory, because it creates a copy of the structure that points to the database currently open in the Access UI, while DBEngine(0)(0) uses no additional memory, because it returns not a copy, but simply a pointer to an existing memory structure.

  • Likely the refreshing of the collections is the reason why CurrentDB is "1700%" slower (or whatever the number was), but probably some of the extra time is taken up by the process of setting up the copy of the database object, as well.

    Again, none of this makes any difference in actual coding practice, as you just don't need to constantly be opening and closing pointers to the database currently open in the Access UI, as IT'S NOT BEING OPENED AND CLOSED CONSTANTLY.

    So, is this a potaeto/potahto thing?

    No, because there's one "bug" in DBEngine(0)(0) that could cause it to return an unexpected database pointer (though it would actually be technically correct), and that is in certain contexts immediately after an Access wizard has run, DBEngine(0)(0) will return a pointer to the wizard database, and not to the database currently open in the Access UI.

    That is because there is a distinction between:

    1. the database currently open in the Access UI, AND

    2. the first database in the first workspace of the DBEngine object.

    CurrentDB, on the other hand, always returns a reference to #1 and never to #2. DBEngine(0)(0), however, can return something else, as for a very brief moment, the wizard really is the first database in the first workspace of the DBEngine object right after the wizard is dismissed.

    Now, is it likely that production code could ever encounter this error? Probably not, since it's unlikely that you'd use a wizard in a production app. But this could also apply to library databases, and that's not so uncommon a technique, particularly for advanced Access programmers.

    If there were a practical performance difference, DBEngine(0)(0) might be worth it, but since there isn't, CurrentDB is preferable since it is 100% reliable in returning the expected database reference.

    All that said, I don't use either in my apps.

    Instead, I use a function that caches a database variable initialized with CurrentDB. This means I never have to initialize any database variables, just use my dbLocal() function in place of any database variable. Here's the code:

      Public Function dbLocal(Optional bolCleanup As Boolean = False) As DAO.Database
      ' This function started life based on a suggestion from 
      '   Michael Kaplan in comp.databases.ms-access back in the early 2000s
      ' 2003/02/08 DWF added comments to explain it to myself!
      ' 2005/03/18 DWF changed to use Static variable instead
      ' uses GoTos instead of If/Then because:
      '  error of dbCurrent not being Nothing but dbCurrent being closed (3420)
      '  would then be jumping back into the middle of an If/Then statement
      On Error GoTo errHandler
        Static dbCurrent As DAO.Database
        Dim strTest As String
    
      If bolCleanup Then GoTo closeDB
    
      retryDB:
        If dbCurrent Is Nothing Then
           Set dbCurrent = CurrentDb()
        End If
        ' now that we know the db variable is not Nothing, test if it's Open
        strTest = dbCurrent.Name
    
      exitRoutine:
        Set dbLocal = dbCurrent
        Exit Function
    
      closeDB:
        If Not (dbCurrent Is Nothing) Then
           'dbCurrent.close ' this never has any effect
           Set dbCurrent = Nothing
        End If
        GoTo exitRoutine
    
      errHandler:
        Select Case Err.Number
          Case 3420 ' Object invalid or no longer set.
            Set dbCurrent = Nothing
            If Not bolCleanup Then
               Resume retryDB
            Else
               Resume closeDB
            End If
          Case Else
            MsgBox Err.Number & ": " & Err.Description, vbExclamation, "Error in dbLocal()"
            Resume exitRoutine
        End Select
      End Function
    

    在代码中,您可以这样使用它:
      Dim rs As DAO.Recordset
    
      Set rs = dbLocal.OpenRecordset("SQL SELECT statement")
      [do whatver]
      rs.Close
      Set rs = Nothing
      dbLocal.Execute("SQL INSERT statement")
      Debug.Print dbLocal.OpenRecordset("SELECT @@IDENTITY")(0)
      dbLocal.Execute("SQL UPDATE statement")
      Debug.Print dbLocal.RecordsAffected
    

    第一次调用它时,它会用 CurrentDB 初始化自己并返回缓存的数据库对象。

    当您关闭应用程序时,您将 bolCleanup 标志设置为 TRUE 来调用它,以便它清理缓存的变量。

    如果你添加到集合中,它们不会刷新(因为你不是每次都调用 CurrentDB,只是使用一个用 CurrentDB 初始化的缓存数据库变量),所以你必须这样做:
      [add a new QueryDef]
      dbLocal.QueryDefs.Refresh
    

    就是这样。没有全局变量,不需要经常用 CurrentDB(或 DBEngine(0)(0))初始化数据库变量。您只需使用它,而不必担心它。唯一的技术细节是确保您的应用程序的关闭例程调用 dbLocal(False)。

    所以,这就是我对 DBEngine(0)(0) 与 CurrentDB 的看法。

    关于清理由这两种方法初始化的数据库变量的一个附带问题:

    如果使用 CurrentDB 初始化 db 变量,则不会关闭它,只需将其设置为 Nothing:
      Dim db As DAO.Database
    
      Set db = CurrentDB
      ...
      'db.Close <= don't do this
      Set db = Nothing
    

    如果您确实发出 db.Close,则什么都不会发生,既不好也不好。

    另一方面,在这种情况下:
      Dim db As DAO.Database
    
      Set db = DBEngine(0)(0)
      ...
      'db.Close <= don't do this
      Set db = Nothing
    

    ...发出 db.Close 可能会导致您的应用程序在某些版本的 Access 中崩溃。

    它们都不能实际工作,因为您无法通过数据库对象的 Close 方法关闭当前在 Access UI 中打开的数据库。

    另一方面,如果你这样做:
      Dim db As DAO.Database
    
      Set db = DBEngine.OpenDatabase("path to external MDB file")
      ...
      db.Close ' <=you *must* do this
      Set db = Nothing
    

    ...你真的想关闭它,因为它是一个外部数据库。该代码不能用 CurrentDB 完成,因为这是打开对另一个数据库的引用的唯一方法。

    关于ms-access - 微软 Access : Is there a significant overhead when using CurrentDB as opposed to DBEngine(0)(0)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1833746/

    相关文章:

    sql - 为什么在左连接中使用子查询会给出与等效表完全不同的答案?

    以 MS Access 数据库作为数据源的 WPF 应用程序

    SQL 2表,先计数,按秒分组

    ms-access - 如何在连续表单上有条件地设置文本框边框格式?

    java - UCanAccess:打开和关闭必填字段以防止违反完整性约束:NOT NULL 错误

    c# - 创建新的数据库并填充另一个数据库

    database - Delphi - 网络 ODBC 数据库 (MS Access)

    ms-access - 在MS Access中删除表格后如何从1重新开始计数?

    excel - 用于与 Excel 交互的 MS Access VBA 脚本

    ms-access - 为什么书签属性在我的 MS Access 表单中不起作用