如何确定 Access 数据库中哪个表使用的空间最多?

Posted

技术标签:

【中文标题】如何确定 Access 数据库中哪个表使用的空间最多?【英文标题】:How to determine which table uses the most space in an Access database? 【发布时间】:2009-10-07 16:25:22 【问题描述】:

是否有任何简单的方法可以确定 Access 2007 数据库中每个表使用了多少空间?

我有一个异常大的 Access 数据库,需要找出哪个表占用的空间最多。行数没有提供有关已用空间的足够信息。

【问题讨论】:

【参考方案1】:

我知道这是一篇旧帖子,但我有一个基于我自己对同一问题的经验的解决方案。我的解决方案是将所有表格导出到文本文件。每个文本文件的大小与它在 mdb/accdb 文件中使用的空间大小大致成正比。

下面的代码将在当前数据库文件夹下创建一个子文件夹“temp_table_size”并将所有表导出到它。您可以将参数传递给它以仅处理本地表。完成后,它会告诉您导出了多少表并询问您是否要打开该文件夹。按大小对该文件夹进行排序,您将快速识别出罪魁祸首。我使用此例程查找在部署之前可能忘记清除的表,或者在我继承别人的数据库时帮助我了解大表在哪里。

为了使这个例程对我更方便,我将此代码添加到 Access 加载项中,以便可以针对任何数据库运行它。该加载项还具有导出所有其他 Access 对象的功能,因此我可以查看哪些表单/报告正在占用数据库中的空间。如果有兴趣的话,也许我会找个地方分享一下。

Public Sub DocDatabase_Table(Optional bolLocalTablesOnly As Boolean = False)
 '====================================================================
 ' Name:    DocDatabase_Table
 ' Purpose: Exports the tables in this database to a series of
 '          text files.  The size of each text file will give you
 '          an idea of what tables use the most disk space.
 '
 ' Author:  Ben Sacherich
 ' Date:    5/2/2011
 '====================================================================
    On Error GoTo ErrorHandler

    Dim dbs As Database ' or Variant if this fails.
    Dim td As TableDef
    Dim strSaveDir As String
    Dim lngObjectCount As Long
    Dim lngCount As Long
    Dim strMsg As String
    Dim varReturn As Variant

    Set dbs = CurrentDb() ' use CurrentDb() to refresh Collections

    ' Export to a subfolder of the current database folder.
    strSaveDir = CurrentProject.path & "\temp_table_size\"

    If Len(strSaveDir) > 0 Then

        strMsg = "This feature exports all of the tables in this database to a series of " _
            & "comma delimited text files.  The size of each text file will give you " _
            & "an idea of what tables use the most disk space." & vbCrLf & vbCrLf

        ' Get a count of the tables, minus the system tables.
        If bolLocalTablesOnly = True Then
            lngObjectCount = DCount("Name", "MSysObjects", "Type=1 AND Name not like 'MSys*' AND Name not like '~*'")
            strMsg = strMsg & "There are " & lngObjectCount & " local tables in this database. " _
                & vbCrLf & vbCrLf
        Else
            ' Include Local, Linked, and ODBC tables
            lngObjectCount = DCount("Name", "MSysObjects", "Type in (1,4,6) AND Name not like 'MSys*' AND Name not like '~*'")
            strMsg = strMsg & "There are " & lngObjectCount & " tables in this database " _
                & "(including local, linked, and ODBC)." & vbCrLf & vbCrLf
        End If
        strMsg = strMsg & "The tables will be exported to a subfolder of the current database:  " _
            & strSaveDir & vbCrLf & vbCrLf
        strMsg = strMsg & "Do you want to continue?"

        If MsgBox(strMsg, vbYesNo + vbInformation, "Export Tables") = vbYes Then

            If Dir(strSaveDir, vbDirectory) = "" Then
                MkDir strSaveDir
            End If

            ' Initialize and display message in status bar.
            varReturn = SysCmd(acSysCmdInitMeter, "(" & Format((lngCount) / lngObjectCount, "0%") & ")  Preparing tables", lngObjectCount)

            dbs.TableDefs.Refresh
            For Each td In dbs.TableDefs ' Tables
                If (bolLocalTablesOnly = True And Len(td.Connect) = 0) _
                  Or (bolLocalTablesOnly = False) Then

                    If Left(td.Name, 4) <> "MSys" And Left(td.Name, 1) <> "~" Then
                        Debug.Print td.Name, td.Attributes

                        ' Update message in status bar.
                        varReturn = SysCmd(acSysCmdSetStatus, "(" & Format((lngCount + 1) / lngObjectCount, "0%") _
                            & ")  Exporting table: " + td.Name)

                        DoCmd.TransferText acExportDelim, , td.Name, strSaveDir & "Table_" & td.Name & ".txt", True
                        lngCount = lngCount + 1

                    End If
                End If
            Next td

            'Remove the Progress Meter
            varReturn = SysCmd(acSysCmdRemoveMeter)

            If MsgBox("Exported " & lngCount & " object(s)." _
                & vbCrLf & vbCrLf & "Do you want to open the destination folder: " & strSaveDir & " ? " _
                , vbSystemModal + vbYesNo + vbInformation, "Table Size") = vbYes Then

                ' Open the output folder in Windows Explorer
                Call Shell("explorer.exe " & strSaveDir, vbNormalFocus)
            End If
        End If
    End If

Exit_Sub:
    Set td = Nothing
    Set dbs = Nothing

    Exit Sub

ErrorHandler:

    Debug.Print Err.Number, Err.Description
    Select Case Err
        Case "3011"
            MsgBox "Table '" & td.Name & "' could not be found or has a broken link." _
                & vbCrLf & vbCrLf & "Link: " & td.Connect _
                & vbCrLf & vbCrLf & "Click OK to continue.", vbExclamation, "Error 3011"
            Resume Next
        Case "75"
            ' This happens when you try to create a folder name that already exists.
            ' For this Q&D function, ignore the error.
            Resume Next
        Case Else
            MsgBox Err.Description
            Resume Next
    End Select

    GoTo Exit_Sub

End Sub

【讨论】:

干得好,谢谢!我不得不将变量中的As Database 更改为As Variant,因为访问不希望我出于某种原因运行代码(?),之后就像一个魅力。 :) 我想指出,在阅读以下内容后,通过使用带有SELECT * INTO 而不是DoCmd.TransferText 的ADO,我能够将上述代码的导出速度提高70%:***.com/a/14612302/1898524【参考方案2】:

这实际上是一个有趣的问题,因为 Access 使用可变长度记录来存储它的数据。

准确执行此操作的最佳方法是遍历表中的每条记录和每个字段,然后将字段的长度相加。如果桌子很大,可能需要一段时间。由于索引和关系,它不会增加大小。

在我们的 Total Access Analyzer 程序中,我们有一些报告使用简单的记录大小估计乘以记录数来提供表大小的估计值。此处显示了一个示例:http://fmsinc.com/MicrosoftAccess/Documentation/Reports/Table_SizeBySize.html

这可能足以进行大致估计或相对大小比较。

另一种可能非常准确的测量方法是创建一个新数据库并将表导出到其中。压缩数据库并从中减去空白数据库大小以获得表的大小。

【讨论】:

+1 用于“创建新数据库并导出表”的建议,而不是无耻的插件 ;)【参考方案3】:

对于正常运行的 Access 数据库,您可以获得简单的工具 Access Memory Reporter 1.0,它显示了表和索引所需的内存量。请注意,我自己没有尝试过这个工具。

一旦发现最大的桌子,您的目标是什么?你的 MDB 有多大?你最近压缩了吗?

当你压缩它时它会收缩多少?那就是你在里面创建和删除很多表/记录吗?如果是这样,请参阅我网站上的 TempTables.MDB page,它说明了如何在您的应用中使用临时 MDB。

您是否在表格中使用了很多图形?

【讨论】:

感谢您指出该工具。不幸的是,该工具只告诉我大部分空间被“Memo/OLE”数据占用,但它没有告诉我哪个表包含多少“Memo/OLE”数据。相反,所有“备忘录/OLE”数据都分组在标题“## 与一个表## 不直接相关”下。压缩数据库没有帮助; MDB 文件根本不收缩。 - 弗兰克 作为一种解决方法,我现在已将 MDB 数据库导入 SQL Server,并且在 SQL Server Management Studio 中,我已将“使用的数据空间”列添加到列表视图中。这告诉我哪个表使用了多少空间。使用该过程,我发现数据库中的单个表几乎使用了所有磁盘空间。 备忘录/OLE 数据实际上并未存储在记录中。而是有一个指向实际存储每个备忘录/OLE 的页面的指针。所以它实际上与记录无关。但是,您可以运行一些 VBA 代码来确定哪些表具有备注/OLE 字段以及它们的数量。嗯,可能每个的大小,但我不得不戳一下。虽然您现在似乎已经弄清楚哪个表占用了空间。【参考方案4】:

这就是我的方法: 1. 收集数据库中所有非系统表。 2. 将每张表导出到临时数据库,比较前后大小。 3. 显示包含收集信息的表并删除临时数据库。

无论如何,这只是一个估计,因为由于关系、unicode 压缩等原因,很难准确计算大小。

将此 Sub 复制到全局模块并按 F5 运行它:

Sub CheckTableSize()
    ' Table Size Analysis
    Dim DB As DAO.Database, NewDB As String, T As DAO.TableDef, SizeAft As Long, _
        SizeBef As Long, RST As DAO.Recordset, F As Boolean, RecCnt As Long

    Const StTable As String = "_Tables"

    Set DB = CurrentDb

    NewDB = Left(DB.Name, InStrRev(DB.Name, "\")) & Replace(Str(Now), ":", "-") & " " & _
        Mid(DB.Name, InStrRev(DB.Name, "\") + 1, Len(DB.Name))
    Application.DBEngine.CreateDatabase NewDB, DB_LANG_GENERAL

    F = False
    For Each T In DB.TableDefs
        If T.Name = StTable Then
            F = True: Exit For
        End If
    Next T
    If F Then
        DB.Execute "DELETE FROM " & StTable, dbFailOnError
    Else
        DB.Execute "CREATE TABLE " & StTable & _
            " (tblName TEXT(255), tblRecords LONG, tblSize LONG);", dbFailOnError
    End If

    For Each T In DB.TableDefs
        ' Exclude system tables:
        If Not T.Name Like "MSys*" And T.Name <> StTable Then
            RecCnt = T.RecordCount
            ' If it's linked table:
            If RecCnt = -1 Then RecCnt = DCount("*", T.Name)
            If RecCnt > 0 Then DB.Execute "INSERT INTO " & StTable & _
                " (tblName, tblRecords) " & _
                "VALUES ('" & T.Name & "', " & RecCnt & ")", dbFailOnError
        End If
    Next T

    Set RST = DB.OpenRecordset("SELECT * FROM " & StTable, dbOpenDynaset)
    If RST.RecordCount > 0 Then
        Do Until RST.EOF
            Debug.Print "Processing table " & RST("tblName") & "..."
            SizeBef = FileLen(NewDB)
            DB.Execute ("SELECT * " & _
            "INTO " & RST("tblName") & " IN '" & NewDB & "' " & _
            "FROM " & RST("tblName")), dbFailOnError
            SizeAft = FileLen(NewDB) - SizeBef
            RST.Edit
                RST("tblSize") = SizeAft
            RST.Update
            Debug.Print "    size = " & SizeAft
            RST.MoveNext
        Loop
    Else
        Debug.Print "No tables found!"
    End If
    RST.Close: Set RST = Nothing

    Debug.Print ">>> Done! <<<"
    MsgBox "Done!", vbInformation + vbSystemModal, "CheckTableSize"
    Kill NewDB
    Set DB = Nothing

    DoCmd.OpenTable StTable, acViewNormal, acReadOnly
End Sub

from my github repository

【讨论】:

【参考方案5】:

我用的是Access 2003,很容易得到表记录数。表记录数表示表大小的大小。记录越多,尺寸越大。 如何获取表记录数?

    用 Access 2003 打开数据库, 点击标签“数据库工具” 单击“数据库文档管理工具”(可能是其他名称。) 点击“全部”和“确定” 您将看到一个新的白色底页显示所有表格的表格信息。点击“TXT”(在Word图标下)导出为txt 您会将 TXT 保存到文件中。我们将其命名为 tableinfor.txt 通过 txt 编辑器打开“tableinfo.txt”。搜索关键字“记录计数”。 我想你知道哪张桌子占用的空间最多。

【讨论】:

我认为这不会有帮助:假设您有两个表 T1 和 T2,每个表都有一个备忘录类型的字段。 T1有100万条记录,每个备注字段包含1个字符; T2 有 1,000 条记录,每个备注字段包含 10,000 个字符。那么,T2 显然比 T1 消耗更多的空间(即大约 10 倍)。但是T2的记录数会比T1少很多。【参考方案6】:

您可以将每个表单独复制到单独的 Access 数据库中,然后比较每个表的大小。虽然它不会为您提供表格本身的确切大小,但每个文件的大小大致显示了每个表格的大小。

【讨论】:

以上是关于如何确定 Access 数据库中哪个表使用的空间最多?的主要内容,如果未能解决你的问题,请参考以下文章

如何找出 TempDB 中哪个表占用的空间最多?

如何创建一个通用的 MS Access 表单?

oracle占用的空间特别大,该如何解决?

如何确定哪个数据集用于特定变量?

我可以使用 Access VBA 来确定表是不是有数据宏吗?

如何对access中两个数据库中表进行联合查询