如何压缩MS Access数据库

10

我有一个大小为70MB的.mdb文件。

在删除文件中包含的所有记录后,文件大小仍然是70MB。

如何使我的.mdb文件更小?


进行了一些编辑。采用了兆字节的猜测。看起来是最可能的计量单位。这并不会真正影响问题的结果。 - spender
2
我就是不懂为什么会有人批评这个问题。我去查看了原始问题的来源,非常清楚它想问什么,虽然编辑确实提高了其清晰度。从一开始它就是一个真正的问题。其次,关于数据库维护的问题,即使没有明确涉及编程,也可以在SO上提问,因为这种问题到处都存在,所以我不认为这是一个SuperUser的问题。对我来说,这看起来像一个灰色区域——这也将是一个可以在SU上提问的好问题,但我不认为它不适合在SO上提问。 - David-W-Fenton
4个回答

19
所有已经存在的数据库引擎都需要定期运行维护操作来优化数据存储并恢复空间。例如,在 xBase 时代,你需要运行 PACK 命令来删除已删除的行。在 SQL Server 上,你需要运行脚本来收缩实际数据文件,以达到相同的目的。
为什么每个数据库引擎都需要这样做?
因为每次向数据库写入时如果需要重写整个文件的优化顺序将会对性能产生巨大影响。考虑一个将每个数据表存储在单独文件中的数据库。如果一个表有10000条记录,并且你删除了第5000条记录以去除松散空间,那么你需要重写数据文件的后半部分。因此,每个数据库使用某些形式标记已使用的空间作为空闲可丢弃空间,下一次对数据表进行优化操作时便可以清理掉它们。
Jet / ACE 在这方面与任何其他数据库引擎没有区别,任何使用 Jet / ACE 数据库作为数据存储的应用程序都应该按计划进行定期维护操作,包括备份和压缩。
这在 Jet / ACE 中存在一些问题,而服务器数据库引擎中不存在这些问题。具体而言,如果所有用户都关闭了与数据文件的连接,则无法进行压缩。在服务器数据库中,用户连接到数据库引擎的服务器端进程,而该服务器端守护程序是存储数据的实际数据文件的唯一“用户”。因此,服务器守护程序可以决定何时执行优化和维护例程,因为它完全控制数据文件何时在使用或非使用状态下。
Access 应用程序的一个常见问题是用户会将应用程序保持打开状态并离开办公室。这意味着当你在凌晨2点运行压缩操作时,文件仍处于打开状态,无法运行(因为压缩会替换原始文件)。遇到这种问题的 Access 应用程序的大多数程序员要么容忍这种类型的夜间维护偶尔失败(卷影副本仍允许备份文件,尽管无法保证备份副本将处于100%内部一致状态),要么设计他们的 Access 应用程序以便在适当的时间终止,以允许夜间维护操作。我自己已经两种情况都做过。
在非 Access 应用程序中,同样存在这个问题,但必须采用不同的方法来解决。对于 Web 应用程序而言,这是一些问题,但总的来说,我认为任何需要进行压缩的 Web 应用程序都不适合使用 Jet / ACE 数据存储。
现在,关于“关闭时压缩(COMPACT ON CLOSE)”:
它不应该被任何人使用。
永远不要使用。
当它实际触发时,它是无用的且极其危险的。如果是Access应用程序,应该将其分为前端和后端,用户只能打开前端,如果是Web应用程序,用户不会直接与数据文件交互。在这两种情况下,没有人会触发“关闭时压缩”,所以打开它是浪费时间。
  • 即使有人偶尔触发它,也只有在该用户是唯一打开数据库的人时才能运行。如上所述,如果还有其他用户打开它,它就不能被压缩,因此这也行不通——必须具备独占访问权限时才能运行“关闭时压缩”。
  • 最糟糕的是,“关闭时压缩”是危险的,如果运行,可能会导致实际数据丢失。这是因为Jet/ACE数据库可能处于某些状态下,其中内部结构不正确,但所有数据仍然可以访问。当以那种状态运行紧凑/修复操作时,数据可能会潜在地丢失。这是一个极其罕见的情况,但确实是一个非常遥远的可能性。
  • 问题在于,“关闭时压缩”没有条件,并且没有提示询问您是否要运行它。在它运行之前,您没有机会备份,因此,如果您打开它并且在数据库处于非常罕见的状态时它触发,您可能会失去该数据,否则您将能够恢复它,如果不运行紧凑操作。
  • 因此,总之,任何了解Jet/ACE和压缩的人都不会打开“关闭时压缩”。
  • 对于单个用户,只需根据需要进行压缩即可。
  • 对于共享应用程序,最好的方法是某种类型的定期维护脚本,通常在文件服务器上隔夜运行。该脚本将备份文件,然后运行压缩。在VBScript中编写这样一个简单的脚本并轻松安排。
  • 最后,如果您的应用程序经常删除大量记录,通常表明存在设计错误。在正常生产使用中添加和删除的记录是临时数据,不属于主数据文件,无论逻辑上还是实际上都是如此。
  • 我所有的生产应用程序都有一个临时数据库作为架构的一部分,并且所有临时表都存储在那里。我从不费心地压缩临时数据库。如果由于临时数据库中的膨胀而导致性能下降,我只需复制干净的空临时数据库的副本覆盖旧的副本,因为其中的所有数据除了临时数据外都是不重要的。这减少了前端或后端中的翻新和膨胀,并大大降低了必须对后端数据文件进行压缩的频率。
  • 关于如何压缩,有许多选项:
  • 在Access UI中,您可以压缩当前打开的数据库(工具 | 数据库工具)。但是,这并不允许您在过程中备份,最好在压缩之前备份以防万一。
    在Access UI中,您可以压缩未打开的数据库。这将从现有文件进行压缩,生成一个新文件,完成后需要重命名原始文件和新压缩文件(以获得新名称)。询问要从哪个文件进行压缩的文件打开对话框会在此时允许您重命名该文件,因此可以将其作为手动过程的一部分完成。
    在代码中,您可以使用DAO DBEngine.CompactDatabase方法来执行此任务。这可在Access VBA中使用,也可在VBScript或任何可以使用COM的环境中使用。您在代码中负责备份和重命名文件等操作。
    在代码中的另一个选择是JRO(Jet & Replication Objects),但它在紧凑运算方面没有比DAO更多的提供内容。 JRO被创建为一个单独的库,用于处理在ADO本身中不支持的Jet特定功能,因此,如果您使用ADO作为接口,则MS推荐用于压缩的库将是JRO。在Access中,JRO不适合进行压缩,因为即使没有DAO引用(无论是否有DAO引用,DBEngine始终可用于Access),您已经可以使用CompactDatabase方法。换句话说,DBEngine.CompactDatabase可以在Access中使用,而无需DAO或ADO引用,而JRO CompactDatabase方法仅在具有JRO引用(或使用后期绑定)时可用。从Access之外,JRO可能是适当的库。
    让我强调备份的重要性。您在1000次中不需要它999次(甚至更少),但是当您需要它时,您会非常需要它!因此,在压缩之前永远不要进行备份。
    最后,在任何紧凑操作完成后,检查紧凑文件以查看是否存在名为MSysCompactErrors的系统表。如果有任何问题,请将其列在此表中。
    这就是我现在所能想到的关于压缩的所有内容。

    3
    好的帖子,尽管你的答案感觉像是用重兵器解决小问题。 :) - Joel Gauvreau
    是的,我猜你说得对。但是这个问题经常被问到了,而且很明显人们在使用数据库时,包括Jet/ACE,却没有基本的工作原理了解。这就把所有内容都汇总到了一个地方,或多或少,我希望需要时可以让人们指向它,而不是其他人需要逐个回答有关主题的问题。我喜欢写这样的答案,因为它帮助我理解自己所知道和不知道的东西。我也希望它有时能帮助别人。 - David-W-Fenton

    9

    3
    你是如何理解那个......的? - Alexander
    1
    我10年前也遇到了完全相同的问题...我想那时我的确是这么说的...;-) - Edward Leno
    你使用的是哪个版本的Microsoft Access? - Edward Leno
    5
    不建议启用“关闭时压缩数据库”选项,因为偶尔会出现问题并导致数据丢失。 - Tony Toews

    4
    微软Access数据库引擎提供了一种CompactDatabase方法,用于制作数据库文件的精简副本。在调用CompactDatabase之前,必须关闭数据库文件。
    文档: 以下是使用DAO复制和压缩MDB文件的Python脚本:
    import os.path
    import sys
    import win32com.client
    
    # Access 97: DAO.DBEngine.35
    # Access 2000/2003: DAO.DBEngine.36
    # Access 2007: DAO.DBEngine.120
    daoEngine = win32com.client.Dispatch('DAO.DBEngine.36')
    
    if len(sys.argv) != 3:
        print("Uses Microsoft DAO to copy the database file and compact it.")
        print("Usage: %s DB_FILE FILE_TO_WRITE" % os.path.basename(sys.argv[0]))
        sys.exit(2)
    
    (src_db_path, dest_db_path) = sys.argv[1:]
    print('Using database "%s", compacting to "%s"' % (src_db_path, dest_db_path))
    daoEngine.CompactDatabase(src_db_path, dest_db_path)
    print("Done")
    

    1
    使用Python,您可以使用pypyodbc库(无论是.mdb还是.accdb)进行压缩。
    import pypyodbc
    pypyodbc.win_compact_mdb('C:\\data\\database.accdb','C:\\data\\compacted.accdb')
    

    (来源)

    然后您可以使用 shutil 将压缩后的 compacted.accdb 复制回 database.accdb:

    import shutil
    shutil.copy2('C:\\data\\compacted.accdb','C:\\data\\database.accdb')
    

    (来源)

    注意:据我所知,对于使用ODBC的Access数据库,Python及其库必须是32位的(链接)。此外,这些步骤可能仅适用于Windows操作系统。


    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接