【发布时间】:2010-11-30 09:57:46
【问题描述】:
很简单的问题,我知道。
【问题讨论】:
-
当然,只是希望 stackOverflow 有所有的答案!
很简单的问题,我知道。
【问题讨论】:
如果你想压缩/修复一个外部 mdb 文件(不是你刚才工作的那个):
Application.compactRepair sourecFile, destinationFile
如果您想压缩正在使用的数据库:
Application.SetOption "Auto compact", True
在最后一种情况下,您的应用将在关闭文件时被压缩。
我的意见:在一个额外的 MDB“压缩器”文件中编写几行代码,当你想压缩/修复 mdb 文件时可以调用它是非常有用的:在大多数情况下,需要压缩的文件不能正常打开了,需要从文件外调用方法。
否则,在 Access 应用程序的每个主模块中,自动压缩默认设置为 true。
如果发生灾难,请创建一个新的 mdb 文件并从错误文件中导入所有对象。您通常会发现无法导入的错误对象(表单、模块等)。
【讨论】:
如果您的数据库具有前端和后端。您可以在前端主导航窗体的主窗体上使用以下代码:
Dim sDataFile As String, sDataFileTemp As String, sDataFileBackup As String
Dim s1 As Long, s2 As Long
sDataFile = "C:\MyDataFile.mdb"
sDataFileTemp = "C:\MyDataFileTemp.mdb"
sDataFileBackup = "C:\MyDataFile Backup " & Format(Now, "YYYY-MM-DD HHMMSS") & ".mdb"
DoCmd.Hourglass True
'get file size before compact
Open sDataFile For Binary As #1
s1 = LOF(1)
Close #1
'backup data file
FileCopy sDataFile, sDataFileBackup
'only proceed if data file exists
If Dir(sDataFileBackup vbNormal) <> "" Then
'compact data file to temp file
On Error Resume Next
Kill sDataFileTemp
On Error GoTo 0
DBEngine.CompactDatabase sDataFile, sDataFileTemp
If Dir(sDataFileTemp, vbNormal) <> "" Then
'delete old data file data file
Kill sDataFile
'copy temp file to data file
FileCopy sDataFileTemp, sDataFile
'get file size after compact
Open sDataFile For Binary As #1
s2 = LOF(1)
Close #1
DoCmd.Hourglass False
MsgBox "Compact complete " & vbCrLf & vbCrLf _
& "Size before: " & Round(s1 / 1024 / 1024, 2) & "Mb" & vbCrLf _
& "Size after: " & Round(s2 / 1024 / 1024, 2) & "Mb", vbInformation
Else
DoCmd.Hourglass False
MsgBox "ERROR: Unable to compact data file"
End If
Else
DoCmd.Hourglass False
MsgBox "ERROR: Unable to backup data file"
End If
DoCmd.Hourglass False
【讨论】:
尝试添加这个模块,很简单,只需启动 Access,打开数据库,将“Compact on Close”选项设置为“True”,然后退出。
自动压缩的语法:
acCompactRepair "C:\Folder\Database.accdb", True
返回默认值*:
acCompactRepair "C:\Folder\Database.accdb", False
*没有必要,但是如果你的后端数据库大于 1GB,当你直接进入它并且需要 2 分钟退出时,这可能会很烦人!
编辑:添加了递归遍历所有文件夹的选项,我每晚运行一次以将数据库降至最低。
'accCompactRepair
'v2.02 2013-11-28 17:25
'===========================================================================
' HELP CONTACT
'===========================================================================
' Code is provided without warranty and can be stolen and amended as required.
' Tom Parish
' TJP@tomparish.me.uk
' http://baldywrittencod.blogspot.com/2013/10/vba-modules-access-compact-repair.html
' DGF Help Contact: see BPMHelpContact module
'=========================================================================
'includes code from
'http://www.ammara.com/access_image_faq/recursive_folder_search.html
'tweaked slightly for improved error handling
' v2.02 bugfix preventing Compact when bAutoCompact set to False
' bugfix with "OLE waiting for another application" msgbox
' added "MB" to start & end sizes of message box at end
' v2.01 added size reduction to message box
' v2.00 added recurse
' v1.00 original version
Option Explicit
Function accSweepForDatabases(ByVal strFolder As String, Optional ByVal bIncludeSubfolders As Boolean = True _
, Optional bAutoCompact As Boolean = False) As String
'v2.02 2013-11-28 17:25
'sweeps path for .accdb and .mdb files, compacts and repairs all that it finds
'NB: leaves AutoCompact on Close as False unless specified, then leaves as True
'syntax:
' accSweepForDatabases "path", [False], [True]
'code for ActiveX CommandButton on sheet module named "admin" with two named ranges "vPath" and "vRecurse":
' accSweepForDatabases admin.Range("vPath"), admin.Range("vRecurse") [, admin.Range("vLeaveAutoCompact")]
Application.DisplayAlerts = False
Dim colFiles As New Collection, vFile As Variant, i As Integer, j As Integer, sFails As String, t As Single
Dim SizeBefore As Long, SizeAfter As Long
t = Timer
RecursiveDir colFiles, strFolder, "*.accdb", True 'comment this out if you only have Access 2003 installed
RecursiveDir colFiles, strFolder, "*.mdb", True
For Each vFile In colFiles
'Debug.Print vFile
SizeBefore = SizeBefore + (FileLen(vFile) / 1048576)
On Error GoTo CompactFailed
If InStr(vFile, "Geographical Configuration.accdb") > 0 Then MsgBox "yes"
acCompactRepair vFile, bAutoCompact
i = i + 1 'counts successes
GoTo NextCompact
CompactFailed:
On Error GoTo 0
j = j + 1 'counts failures
sFails = sFails & vFile & vbLf 'records failure
NextCompact:
On Error GoTo 0
SizeAfter = SizeAfter + (FileLen(vFile) / 1048576)
Next vFile
Application.DisplayAlerts = True
'display message box, mark end of process
accSweepForDatabases = i & " databases compacted successfully, taking " & CInt(Timer - t) & " seconds, and reducing storage overheads by " & Int(SizeBefore - SizeAfter) & "MB" & vbLf & vbLf & "Size Before: " & Int(SizeBefore) & "MB" & vbLf & "Size After: " & Int(SizeAfter) & "MB"
If j > 0 Then accSweepForDatabases = accSweepForDatabases & vbLf & j & " failures:" & vbLf & vbLf & sFails
MsgBox accSweepForDatabases, vbInformation, "accSweepForDatabases"
End Function
Function acCompactRepair(ByVal pthfn As String, Optional doEnable As Boolean = True) As Boolean
'v2.02 2013-11-28 16:22
'if doEnable = True will compact and repair pthfn
'if doEnable = False will then disable auto compact on pthfn
On Error GoTo CompactFailed
Dim A As Object
Set A = CreateObject("Access.Application")
With A
.OpenCurrentDatabase pthfn
.SetOption "Auto compact", True
.CloseCurrentDatabase
If doEnable = False Then
.OpenCurrentDatabase pthfn
.SetOption "Auto compact", doEnable
End If
.Quit
End With
Set A = Nothing
acCompactRepair = True
Exit Function
CompactFailed:
End Function
'source: http://www.ammara.com/access_image_faq/recursive_folder_search.html
'tweaked slightly for error handling
Private Function RecursiveDir(colFiles As Collection, _
strFolder As String, _
strFileSpec As String, _
bIncludeSubfolders As Boolean)
Dim strTemp As String
Dim colFolders As New Collection
Dim vFolderName As Variant
'Add files in strFolder matching strFileSpec to colFiles
strFolder = TrailingSlash(strFolder)
On Error Resume Next
strTemp = ""
strTemp = Dir(strFolder & strFileSpec)
On Error GoTo 0
Do While strTemp <> vbNullString
colFiles.Add strFolder & strTemp
strTemp = Dir
Loop
If bIncludeSubfolders Then
'Fill colFolders with list of subdirectories of strFolder
On Error Resume Next
strTemp = ""
strTemp = Dir(strFolder, vbDirectory)
On Error GoTo 0
Do While strTemp <> vbNullString
If (strTemp <> ".") And (strTemp <> "..") Then
If (GetAttr(strFolder & strTemp) And vbDirectory) <> 0 Then
colFolders.Add strTemp
End If
End If
strTemp = Dir
Loop
'Call RecursiveDir for each subfolder in colFolders
For Each vFolderName In colFolders
Call RecursiveDir(colFiles, strFolder & vFolderName, strFileSpec, True)
Next vFolderName
End If
End Function
Private Function TrailingSlash(strFolder As String) As String
If Len(strFolder) > 0 Then
If Right(strFolder, 1) = "\" Then
TrailingSlash = strFolder
Else
TrailingSlash = strFolder & "\"
End If
End If
End Function
【讨论】:
对于 Access 2013,您可以这样做
Sendkeys "%fic"
这与在键盘上键入 ALT、F、I、C 相同。
可能不同版本的字母顺序不同,但“%”符号表示“ALT”,所以请保留在代码中。您可能只需要更改字母,具体取决于您按 ALT 时出现的字母
【讨论】:
Sendkeys "%yc"
回应 jdawgx 的精彩帖子:
请注意上述 CompactDB() 代码中的缺陷。
如果定义了数据库的“AppTitle”属性(就像在数据库属性中定义“应用程序标题”时发生的情况一样),这会使显示的“默认窗口标题”逻辑无效,这可能导致脚本失败,或者“行为不可预测”。因此,添加代码来检查 AppTitle 属性 - 或使用 API 调用从 Application.hWndAccessApp 窗口读取窗口标题文本都可能更可靠。
此外,在 Access 2019 中,我们观察到:
SendKeys "multi-key-string-here"
...也可能无法可靠地工作,需要替换为:
SendKey (single-character)
'put a DoEvents or Sleep 150 here
SendKey (single-character)
'put a DoEvents or Sleep 150 here
SendKey (single-character)
'put a DoEvents or Sleep 150 here
SendKey (single-character)
...从 Access UI 获得正确的响应。
也适用于 Access 2019:
Sendkeys "%yc"(
不再正确。
现在是:
Sendkeys "%y1c"
...如果这个小小的改变还不够 - 尝试确定(在代码中)如何区分 Access 2016 和 2019 - 祝你好运!因为 Application.Version 单独没有帮助,甚至结合 Application.Version 和 Application.Build 也不是保证(除非您处于受控发布的企业环境中,然后它可能会作为可能的版本/构建#s在流通应该更有限)。
【讨论】:
是的,这很简单。
Sub CompactRepair()
Dim control As Office.CommandBarControl
Set control = CommandBars.FindControl( Id:=2071 )
control.accDoDefaultAction
End Sub
基本上它只是以编程方式找到“压缩和修复”菜单项并单击它。
【讨论】:
我在 2003 年或可能是 97 年做了很多年,哎呀!
如果我记得您需要使用上面与计时器相关的子命令之一。 您无法在打开任何连接或表单的情况下对数据库进行操作。
因此,您需要关闭所有表单,并将计时器作为最后运行的方法启动。 (一旦一切关闭,它又会调用紧凑操作)
如果您还没有弄清楚这一点,我可以翻阅我的档案并将其拉出来。
【讨论】:
当用户退出 FE 尝试重命名后端 MDB 时,最好使用 yyyy-mm-dd 格式的名称中的今天日期。确保在执行此操作之前关闭所有绑定的表单,包括隐藏的表单和报告。如果您收到错误消息,哎呀,它很忙,所以不要打扰。如果成功,则将其压缩回去。
查看我的Backup, do you trust the users or sysadmins? 提示页面了解更多信息。
【讨论】:
DBEngine.CompactDatabase 源,目标
【讨论】:
Application.SetOption "Auto compact", False '(上面提到过) 将此与按钮标题一起使用:“DB Not Compact On Close”
编写代码以使用“DB Compact On Close”切换标题 连同 Application.SetOption "Auto compact", True
AutoCompact 可以通过按钮或代码设置,例如:导入大型临时表后。
启动表单可以包含关闭 Auto Compact 的代码,因此它不会每次都运行。
这样,您就不会试图与 Access 对抗。
【讨论】:
如果您不想在关闭时使用 compact(例如,因为前端 mdb 是一个持续运行的机器人程序),并且您不想创建单独的 mdb 来进行压缩,请考虑使用cmd文件。
我让我的 robots.mdb 检查自己的大小:
FileLen(CurrentDb.Name))
如果它的大小超过 1 GB,它会创建一个像这样的 cmd 文件...
Dim f As Integer
Dim Folder As String
Dim Access As String
'select Access in the correct PF directory (my robot.mdb runs in 32-bit MSAccess, on 32-bit and 64-bit machines)
If Dir("C:\Program Files (x86)\Microsoft Office\Office\MSACCESS.EXE") > "" Then
Access = """C:\Program Files (x86)\Microsoft Office\Office\MSACCESS.EXE"""
Else
Access = """C:\Program Files\Microsoft Office\Office\MSACCESS.EXE"""
End If
Folder = ExtractFileDir(CurrentDb.Name)
f = FreeFile
Open Folder & "comrep.cmd" For Output As f
'wait until robot.mdb closes (ldb file is gone), then compact robot.mdb
Print #f, ":checkldb1"
Print #f, "if exist " & Folder & "robot.ldb goto checkldb1"
Print #f, Access & " " & Folder & "robot.mdb /compact"
'wait until the robot mdb closes, then start it
Print #f, ":checkldb2"
Print #f, "if exist " & Folder & "robot.ldb goto checkldb2"
Print #f, Access & " " & Folder & "robot.mdb"
Close f
... 启动 cmd 文件 ...
Shell ExtractFileDir(CurrentDb.Name) & "comrep.cmd"
...然后关闭...
DoCmd.Quit
接下来,cmd文件压缩并重启robot.mdb。
【讨论】:
试试这个。它适用于代码所在的同一数据库。只需调用如下所示的 CompactDB() 函数。确保在添加函数后,在第一次运行之前单击 VBA 编辑器窗口中的保存按钮。我只在 Access 2010 中测试过。ba-da-bing,ba-da-boom。
Public Function CompactDB()
Dim strWindowTitle As String
On Error GoTo err_Handler
strWindowTitle = Application.Name & " - " & Left(Application.CurrentProject.Name, Len(Application.CurrentProject.Name) - 4)
strTempDir = Environ("Temp")
strScriptPath = strTempDir & "\compact.vbs"
strCmd = "wscript " & """" & strScriptPath & """"
Open strScriptPath For Output As #1
Print #1, "Set WshShell = WScript.CreateObject(""WScript.Shell"")"
Print #1, "WScript.Sleep 1000"
Print #1, "WshShell.AppActivate " & """" & strWindowTitle & """"
Print #1, "WScript.Sleep 500"
Print #1, "WshShell.SendKeys ""%yc"""
Close #1
Shell strCmd, vbHide
Exit Function
err_Handler:
MsgBox "Error " & Err.Number & ": " & Err.Description
Close #1
End Function
【讨论】:
请注意以下几点 - 所有喜欢为 MS-Access 执行“关闭时压缩”解决方案的人。
我以前也更喜欢这个选项,直到有一天,我在压缩和修复操作期间收到可能来自 DBEngine 的最糟糕的错误消息:
“表 MSysObjects 已损坏 - 表被截断。”
现在,您可能从未意识到甚至可能出现这种错误。
嗯,是的。如果您看到它,您的整个数据库以及其中的所有内容现在都已经消失了。 噗!
有趣的是,Access 会让你真正重新打开“固定”数据库,只是,Access 窗口和菜单项现在都完全没用了(除了关闭数据库并再次退出访问),因为所有表(包括其他 MSYS* 表、表单、查询、报告、代码模块和宏)完全消失了 - 并且先前分配给它们的磁盘空间已释放给 Windows 操作系统的招标 - 除非您有额外的保护而不是沼泽标准回收站,对你也无济于事。
所以,如果您真的想接受 Compact on Close 完全破坏您的数据库的风险 - 无法恢复它,那么请...继续。
如果 OTOH,像我一样,您发现这种风险是不可接受的,那么,请不要再启用 C&R-on-Close。
【讨论】:
查看此解决方案VBA Compact Current Database。
基本上它说这应该可以工作
Public Sub CompactDB()
CommandBars("Menu Bar").Controls("Tools").Controls ("Database utilities"). _
Controls("Compact and repair database...").accDoDefaultAction
End Sub
【讨论】:
还有 Michael Kaplan 的 SOON ("Shut One, Open New") add-in。你必须把它锁起来,但这是一种方法。
我不能说我有太多的理由想要以编程方式执行此操作,因为我正在为最终用户编程,而且他们从不使用 Access 用户界面中的前端以外的任何东西,而且没有有理由定期压缩设计合理的前端。
【讨论】: