检查访问表是否存在

问题描述 投票:0回答:7

我想记录网站访问的 IP、日期时间、客户端和引用者数据以访问数据库,但我计划将每天的日志数据记录在单独的表中,例如 2010 年 6 月 6 日的日志将记录在 2010_06_06 命名表中。当日期更改时,我将创建一个名为 2010_06_07 的表。但问题是这个表是否已经创建了。

有什么建议如何检查 Access 中是否存在表吗?

sql ms-access ms-access-2007
7个回答
43
投票

您可以使用隐藏系统表 MSysObjects 来检查表是否存在:

If Not IsNull(DlookUp("Name","MSysObjects","Name='TableName'")) Then
    'Table Exists

但是,我同意每天创建一个新表是一个非常糟糕的主意。

编辑:我应该补充一点,表的类型为 1、4 或 6,不同类型的其他对象可能与表具有相同的名称,所以最好说:

If Not IsNull(DlookUp("Name","MSysObjects","Name='TableName' And Type In (1,4,6)")) Then
    'Table Exists

但是,不可能创建与查询同名的表,因此如果您需要查找来测试名称,最好将 5(即查询)添加到类型列表中。


10
投票

这是另一种解决方案,比循环所有表要快一些。

Public Function doesTableExist(strTableName As String) As Boolean
    Dim db As DAO.Database
    Dim td As DAO.TableDef
    Set db = CurrentDb
    On Error Resume Next
    Set td = db.TableDefs(strTableName)
    doesTableExist = (Err.Number = 0)
    Err.Clear
End Function

8
投票

几年前我测试了各种方法来查找表是否存在。这是我实现的所有代码,包括我的简单测试例程。

    Public Function TableExists(strTableName As String, Optional ysnRefresh As Boolean, Optional db As DAO.Database) As Boolean
    ' Originally Based on Tony Toews function in TempTables.MDB, http://www.granite.ab.ca/access/temptables.htm
    ' Based on testing, when passed an existing database variable, this is the fastest
    On Error GoTo errHandler
      Dim tdf As DAO.TableDef
    
      If db Is Nothing Then Set db = CurrentDb()
      If ysnRefresh Then db.TableDefs.Refresh
      Set tdf = db(strTableName)
      TableExists = True
    
    exitRoutine:
      Set tdf = Nothing
      Exit Function
    
    errHandler:
      Select Case Err.Number
        Case 3265
          TableExists = False
        Case Else
          MsgBox Err.Number & ": " & Err.Description, vbCritical, "Error in mdlBackup.TableExists()"
      End Select
      Resume exitRoutine
    End Function
    
    Public Function TableExists2(strTableName As String, Optional ysnRefresh As Boolean, Optional db As DAO.Database) As Boolean
    On Error GoTo errHandler
      Dim bolCleanupDB As Boolean
      Dim tdf As DAO.TableDef
    
      If db Is Nothing Then
         Set db = CurrentDb()
         bolCleanupDB = True
      End If
      If ysnRefresh Then db.TableDefs.Refresh
      For Each tdf In db.TableDefs
        If tdf.name = strTableName Then
           TableExists2 = True
           Exit For
        End If
      Next tdf
    
    exitRoutine:
      Set tdf = Nothing
      If bolCleanupDB Then
         Set db = Nothing
      End If
      Exit Function
    
    errHandler:
      MsgBox Err.Number & ": " & Err.Description, vbCritical, "Error in mdlBackup.TableExists1()"
      Resume exitRoutine
    End Function
    
    Public Function TableExists3(strTableName As String, _
         Optional db As DAO.Database) As Boolean
    ' Based on testing, when NOT passed an existing database variable, this is the fastest
    On Error GoTo errHandler
      Dim strSQL As String
      Dim rs As DAO.Recordset
    
      If db Is Nothing Then Set db = CurrentDb()
      strSQL = "SELECT MSysObjects.Name FROM MSysObjects "
      strSQL = strSQL & "WHERE MSysObjects.Name=" & Chr(34) & strTableName & Chr(34)
      strSQL = strSQL & " AND MSysObjects.Type=6;"
      Set rs = db.OpenRecordset(strSQL)
      TableExists3 = (rs.RecordCount <> 0)
      
    exitRoutine:
      If Not (rs Is Nothing) Then
         rs.Close
         Set rs = Nothing
      End If
      Exit Function
    
    errHandler:
      MsgBox Err.Number & ": " & Err.Description, vbCritical, _
         "Error in TableExists1()"
      Resume exitRoutine
    End Function
    
    Public Sub TestTableExists(strTableName As String, intLoopCount As Integer)
      Dim dteStart As Date
      Dim i As Integer
      Dim bolResults As Boolean
      
      dteStart = Now()
      For i = 0 To intLoopCount
        bolResults = TableExists(strTableName, , CurrentDB())
      Next i
      Debug.Print "TableExists (" & intLoopCount & "): " & Format(Now() - dteStart, "nn:ss")
    
      dteStart = Now()
      For i = 0 To intLoopCount
        bolResults = TableExists2(strTableName, , CurrentDB())
      Next i
      Debug.Print "TableExists2 (" & intLoopCount & "): " & Format(Now() - dteStart, "nn:ss")
    
      dteStart = Now()
      For i = 0 To intLoopCount
        bolResults = TableExists3(strTableName, CurrentDB())
      Next i
      Debug.Print "TableExists3 (" & intLoopCount & "): " & Format(Now() - dteStart, "nn:ss")
    End Sub

3
投票

我发现查询系统表或 tabledef 是不可靠的,并且会在定期创建和删除表的脚本中引入不可预测的行为。

根据我的结果,我的假设是这些表不一定在执行

CREATE
DROP
时立即更新,或者并发问题阻止我获得准确的结果。

我发现以下方法更可靠:

Public Function TableExists(theDatabase As Access.Application, _
    tableName As String) As Boolean

    ' Presume that table does not exist.
    TableExists = False

    ' Define iterator to query the object model.
    Dim iTable As Integer

    ' Loop through object catalogue and compare with search term.
    For iTable = 0 To theDatabase.CurrentData.AllTables.Count - 1
        If theDatabase.CurrentData.AllTables(iTable).Name = tableName Then
            TableExists = True
            Exit Function
        End If
    Next iTable

End Function

除非有大量的表,否则不应该出现运行时问题。


2
投票

这个问题很老了,但我发现没有一个答案令人满意,因为:

  • 它们不处理“坏”链接表的情况,其中链接表指向不存在的数据库或表。
  • 由于链接表可能很大,我们必须能够通过快速查询来检查它们。

所以这是我的简单但更完整的解决方案:

    Function isTableOk(tblName As String) As Boolean
    'works with local or linked tables
        Dim db As DAO.Database, rs As DAO.Recordset
        Dim sSql As String
        sSql = "SELECT TOP 1 ""xxx"" AS Expr1 FROM [" & tblName & "]"
        
        On Error Resume Next
        Err.Clear
        Set db = CurrentDb
        Set rs = db.OpenRecordset(sSql)
        isTableOk = (Err.Number = 0)
        rs.Close
    End Function

您甚至可以使用此版本检查外部 Access 数据库中的表:

    Function isTableOk(tblName As String, Optional dbName As String) As Boolean
    'works with local or linked tables, or even tables in external db (if dbName is provided)
    
        Dim db As DAO.Database, rs As DAO.Recordset
        Dim sSql As String
        
        Set db = CurrentDb
        sSql = "SELECT TOP 1 'xxx' AS Expr1 FROM [" & tblName & "]"
        If Len(dbName) > 0 Then 'external db 
            sSql = sSql & " IN '" & dbName & "'"
        End If
        Err.Clear
        On Error Resume Next
        Set rs = db.OpenRecordset(sSql)
        isTableOk = (Err.Number = 0)
        rs.Close
    End Function

0
投票
'OfficePaper.cc/functions/awa-cømezwe
'chernipeski*duck.com

Public Function IsTable( _
Optional ByVal LookingFor = 0, _
Optional ByVal IncludeLinked = False) _
As Boolean
    a = IIf(IncludeLinked, "1,6", "1")
    c = "Type In (" & a & ") AND Name='" & LookingFor & "'"
    r = DLookup("Name", "MSysObjects", c)
    r = Nz(r, "")
    IsTable = (LookingFor = r)
End Function


0
投票

我意识到这是一个非常老的问题,但上面的答案都不符合我的要求。

有些人利用了错误干预,即使在最好的时候我也不喜欢这样做。有些使用循环搜索,迭代直到找到或未找到所需的表(可能对 TableDefs 中的每个表重复)。其他人则在 MSys 表上进行搜索(这是一种公平的方法,但使用

DLookup
DCount
)。

这个版本使用了二进制斩波,将每次需要检查的数据量减半...

Public Function TableExists(tblName As String) As Boolean
    'Initialise...
    Dim cdb As DAO.Database: Set cdb = CurrentDb
    Dim s As Integer, e As Integer, o As Integer, p As Integer, x As Integer
    Dim running As Boolean, found As Boolean
    found = False: running = True
    x = 0
    'Set the start, end and pointer...
    s = 0: e = cdb.TableDefs.count - 1: o = -1: p = (e - s) / 2
    'Test whether we're still running...
    While running
        'Test the current pointer in the collection against the required...
        found = cdb.TableDefs(p).name = tblName
        'If it's not found then check whether required is before or after the pointer...
        s = (tblName >= cdb.TableDefs(p).name And p) Or _
            (tblName <  cdb.TableDefs(p).name And s)
        e = (tblName <= cdb.TableDefs(p).name And p) Or _
            (tblName >  cdb.TableDefs(p).name And e)
        'Relocate the pointer, taking into account odds/evens...
        p = (s + (e - s) / 2)
        p = p + -(s = e - 1 And o = p)
        'Determine whether we're still running...
        running = (x < cdb.TableDefs.count) And Not found And (o <> p)
        x = x + 1 'Debugging trap, just in case of infinite loops
        o = p
    Wend
    TableExists = found
End Function

我讨厌Access。

© www.soinside.com 2019 - 2024. All rights reserved.