|
|||||||
Microsoft Access: сжатие данных
Время создания: 12.10.2019 20:37
Текстовые метки: VBA_Access, Compress
Раздел: Разные закладки - VBA - Access - Compress
Запись: xintrea/mytetra_db_adgaver_new/master/base/1517138624vu3139ni8h/text.html на raw.githubusercontent.com
|
|||||||
|
|||||||
Microsoft Access: сжатие данных Q1. Из-за чего растет размер файла БД? 1.1. При удалении данных в таблицах БД записи из файла БД не удаляются, а просто помечается как удаленные (удаляются логически). Т.е. стертые данные так и лежат в базе, в результате файл по мере работы постепенно растет; Q2. За счет чего размер файла БД уменьшается при сжатии? При сжатии из файла БД физически удаляются записи и объекты БД, помеченные как "удаленные" (логически удаленные). Q3. Зачем необходимо сжимать БД? 3.1. Для повышения производительности Проекта (быстродействия БД). Во время сжатия Access не только физически удаляет логически удаленные записи, что само по себе увеличивает производительность (отпадает необходимость фильтровать действительные данные от логически удаленных данных), но и дефрагментирует таблицы, в результате чего увеличивается скорость доступа к данным; Q4. Как часто необходимо сжимать БД? Теоретически, сжимать БД необходимо при каждом закрытии БД. Практически это не всегда целесообразно, т.к. занимает определенное время и в некоторых случаях отрицательно сказывается на производительности БД в момент первого выполнения запросов (см. Пункт 3.5). Q5. Как сжимать БД? 5.1. Через меню: Сервис/Служебные программы/Сжать и восстановить базу данных; "C:\Program Files\Microsoft Office\Office\Msaccess.exe" "С:\Мои документы\MyDB.mdb" /Excl /Compact
5.6. Через VBA-код, при этом можно использовать несколько различных технологий программного сжатия БД (См. ответы на вопросы Q6 и Q7). jetcomp.exe /? Q6. Как сжать БД из VBA-кода моего Проекта? 6.1 - Вариант 1 Public Function AutoCompact()
With CommandBars.Add(, 1, , True)
.Controls.Add 1, 2071, , , True
.Visible = True
.Controls(1).SetFocus
DoEvents
SendKeys "~"
End With
End Function
Функция позволяет сжать текущую БД (базу данных, в которой выполняется VBA-код, вызывающий сжатие). Данный вариант является единственным вариантом, позволяющим сжимать текущую не закрытую БД. Не смотря на это, авторы FAQ настоятельно рекомендуют ознакомиться с пунктом 6.4, где рассмотрен более гибкий и надежный вариант реализации сжатия текущей БД; Пример сообщает о наличии подобного варианта "программного сжатия БД" и раскрывает его недостатки. Сжатие происходит только текущей БД (базы, в котором выполняется код), таким образом, если БД разделена на Интерфейсную часть и часть с Данными (см. Вопрос/Ответ Q8.3), то будет сжата только Интерфейсная часть в которой выполняется код, а часть с Данными останется несжатой; БД после сжатия перезапускается (открывается повторно); После последней строки в функции не должно быть никакого дополнительного кода, т.к. это может привести либо к невозможности выполнить сжатие, либо к невозможности выполнить данный дополнительный код; Метод не дает программного контроля над выполняемым сжатием, т.е. невозможно отследить (перехватить) и обработать соответствующим способом ошибки, которые могут возникнуть в процессе сжатия БД. 'ФУНКЦИЯ СЖАТИЯ БД DAO-Методом'
' gflngCompactDatabase(...)'
' ВХОДНЫЕ ПАРАМЕТРЫ ФУНКЦИИ:'
' CompactingDBPathAndName - строковый параметр, задающий ПОЛНЫЙ ПУТЬ (путь + имя файла)'
' к сжимаемой БД.'
' BackupBeforeCompactDB - необязательный логический параметр, указывающий на'
' необходимость сделать перед сжатием резервную копию сжимаемой БД (резервная'
' копия выкладывается в файл с именем "ИмяСжимаемогоФайла_Backup"). При'
' отсутствии параметра резервное копирование не производится.'
'ВОЗВРАЩАЕМОЕ ФУНКЦИЕЙ ЗНАЧЕНИЕ:'
' = 0, если сжатие произведено;'
' = Номеру возникшей ошибки, если выполнить сжатие не удалось.'
'ОСОБЕННОСТИ:'
' Для выполнения процедуры сжатия автоматически создается временный файл'
' с именем "ПолныйПуть\ИмяСжимаемогоФайла_Temp".'
' Резервное копирование, выполнение которого определяется параметром "BackupBeforeCompactDB",'
' производится в файл с именем "ПолныйПуть\ИмяСжимаемогоФайла_Backup"), при'
' этом старая копия резерва перезаписывается новой (фактически удаляется).'
' В случае, если сжимаемая БД открыта, то файл БД не будет скопирован (соответствующая'
' ошибка появится в момент копирования БД).'
Public Function gflngCompactDatabase( _
CompactingDBPathAndName As String, _
Optional BackupBeforeCompactDB As Boolean = False) As Long
Dim strTempFile As String
On Error GoTo ErrHandler
'Формируем имя для временного ("принимающего") файла'
strTempFile = Left(CompactingDBPathAndName, (Len(CompactingDBPathAndName) - 4)) & _
"_Temp" & Right(CompactingDBPathAndName, 4)
'Создаем (если надо) резервную копию файла БД перед сжатием'
If BackupBeforeCompactDB = True _
Then FileCopy CompactingDBPathAndName, _
Left(CompactingDBPathAndName, (Len(CompactingDBPathAndName) - 4)) & _
"_Backup" & Right(CompactingDBPathAndName, 4)
'Сжимаем файл БД (с перезаписью сжатого файла в новый файл)'
DBEngine.CompactDatabase CompactingDBPathAndName, strTempFile, dbLangCyrillic
'Перезаписываем сжатый (временный файл) на место несжатого (старого файла)'
FileCopy strTempFile, CompactingDBPathAndName
'Удаляем временный файл'
Kill strTempFile
Exit Function
ErrHandler:
'обрабатываем возможные ошибки'
gflngCompactDatabase = Err.Number
Err.Clear: Exit Function
End Function Для манипулирования файлами (получения имени для временного файла и копирование файла) функция использует стандартные команды Access и не требует наличия на компьютере библиотеки "Microsoft Scripting Runtime" (SCRRUN.DLL), которая (как показывает практика) может отсутствовать; Для получения имени временного файла функция добавляет к имени сжимаемого файла суффикс "_Temp" ("ПолныйПуть\ИмяСжимаемойБД_Temp"), поэтому в случае, если имеется рабочая БД с подобным именем, то по окончании процесса сжатия она будет удалена. Данный недостаток удается избежать либо приняв правило "Не присваивать рабочим файлам БД имени, оканчивающегося на '_Temp'", либо изменив функцию (заменить добавление суффикса "_Temp" на путь и имя файла, специально отведенного для сжатия БД), либо воспользовавшись технологией, приведенной в следующем Примере: 'ФУНКЦИЯ СЖАТИЯ БД DAO-Методом с использованием FileSystemObject для манипулирования файлами '
' gflngCompactDatabaseFSO(...)'
'ВХОДНЫЕ ПАРАМЕТРЫ ФУНКЦИИ:'
' CompactingDBPathAndName - строковый параметр, задающий ПОЛНЫЙ ПУТЬ (путь + имя файла)'
' к сжимаемой БД.'
' BackupBeforeCompactDB - необязательный логический параметр, указывающий на'
' необходимость сделать перед сжатием резервную копию сжимаемой БД (резервная'
' копия выкладывается в файл с именем "ИмяСжимаемогоФайла_Backup"). При'
' отсутствии параметра резервное копирование не производится.'
'ВОЗВРАЩАЕМОЕ ФУНКЦИЕЙ ЗНАЧЕНИЕ:'
' = 0, если сжатие произведено;'
' = Номеру возникшей ошибки, если выполнить сжатие не удалось.'
'ОСОБЕННОСТИ:'
' Резервное копирование, выполнение которого определяется параметром "BackupBeforeCompactDB",'
' производится в файл с именем "ПолныйПуть\ИмяСжимаемогоФайла_Backup"), при'
' этом старая копия резерва перезаписывается новой (фактически удаляется).'
' В случае, если сжимаемая БД открыта, то файл БД будет скопирован и соответствующая'
' ошибка появится только в момент сжатия БД.'
Public Function gflngCompactDatabaseFSO( _
CompactingDBPathAndName As String, _
Optional BackupBeforeCompactDB As Boolean = False) As Long
On Error GoTo ErrHandler
Dim strTempFile As String
Dim objFileSystem As Object
'Создаем объект Файловой системы '
Set objFileSystem = CreateObject("Scripting.FileSystemObject")
'Создаем (если надо) резервную копию файла БД перед сжатием'
If BackupBeforeCompactDB = True _
Then objFileSystem.CopyFile CompactingDBPathAndName, _
Left(CompactingDBPathAndName, (Len(CompactingDBPathAndName) - 4)) & _
"_Backup" & Right(CompactingDBPathAndName, 4)
'Получаем имя для временного ("принимающего") файла'
strTempFile = objFileSystem.GetTempName
'Сжимаем файл БД (с перезаписью сжатого файла в новый файл)'
DBEngine.CompactDatabase CompactingDBPathAndName, strTempFile, dbLangCyrillic
'Перезаписываем сжатый (временный файл) на место несжатого (старого файла)'
objFileSystem.CopyFile strTempFile, CompactingDBPathAndName, True
'Удаляем временный файл'
Kill strTempFile
'Уничтожаем объект Файловой системы'
Set objFileSystem = Nothing
Exit Function
ErrHandler:
'обрабатываем возможные ошибки'
gflngCompactDatabaseFSO = Err.Number
Err.Clear: Exit Function
End Function
Для получения имени временного файла функция использует специальный метод объекта FileSystemObject, генерирующий уникальное имя для файла, что исключает возможность перезаписать (фактически удалить) какой-либо существующий файл. Для манипулирования файлами (получения имени для временного файла и копирование файла) функция использует библиотеку "Microsoft Scripting Runtime" (SCRRUN.DLL), которая (как показывает практика) может отсутствовать на компьютере. Данный недостаток удается избежать воспользовавшись предыдущим примером DAO-реализации сжатия БД (пункт 6.2.1). If DIR(полный путь к ldb файлу)<>"" then ...
Dim cnn As ADODB.Connection, rst As ADODB.Recordset
Dim je As New JRO.JetEngine
Dim lngConnected As Long, strCurPCName As String, strBE As String, strBETemp As String
Const adhcUsers = "{947bb102-5d43-11d1-bdbf-00c04fb92675}"
strBE = "Полный путь к файлу БД"
strBETemp = "Полный путь к новому (сжатому) файлу БД"
Set cnn = New ADODB.Connection
cnn.Provider = "Microsoft.Jet.OLEDB.4.0"
cnn.Open "Data Source=" & strBE
'Используем элемент Connection Control (Jet версии не младше 4.0) для'
'запрета подключения новых пользователей.'
cnn.Properties("Jet OLEDB:Connection Control") = 1
Set rst = cnn.OpenSchema(Schema:=adSchemaProviderSpecific, SchemaID:=adhcUsers)
' strCurPCName - имя машины, с которой запускается процедура'
strCurPCName = Environ("COMPUTERNAME")
With rst
Do Until .EOF
' Считаем кол-во подключенных машин, исключая машину, с которой
' запущена процедура. Наверняка вычленить реальное имя машины из
' этого поля можно более красивым и правильным способом, мне просто
' лень было, у меня и так работает:-)'
If Not strCurPCName = Left(.Fields(0), 10) Then lngConnected = lngConnected + 1
.MoveNext
Loop
End With
If lngConnected > 0 Then
' База данных заблокирована другим пользователем->сжать не удастся
' -> выходим любым приемлемым способом, не забыв закрыть rst и cnn'
End If
' База не заблокирована -> переходим к сжатию. Вариант с принудительным
' отключением пользователей здесь не рассматривается, т.к. лично я
' считаю его приемлемым только в аварийных случаях.'
'Закрываем открытый ранее рекордсет'
If Not rst Is Nothing Then rst.Close
Set rst=nothing ' Проверяем наличие временного файла (возможно остался
' от предыдущей неудачной попытки сжатия) и удаляем его
If Dir(strBETemp) <> "" Then Kill strBETemp
' Закрываем объект cnn, открытый в п.2, иначе сжать данные не получится'
Set cnn=Nothing
' Сжимаем'
je.CompactDatabase "Data Source=" & strBE & ";", _
"Data Source=" & strBETemp & ";"
' Маловероятно, но гипотетически за время сжатия,
' кто-нибудь мог подключиться к файлу БД, поэтому можно еще раз
' проверить наличие подключенных пользователей (см. пример выше),
' если никто не подключен, то удаляем старый файл и
' переименовываем новый сжатый файл. Если кто-то все-таки
' успел открыть БД, то просто выходим, оставляя временный
' сжатый файл. Добавить обработку такой ситуации по вкусу.
Kill strBE
Name strBETemp As strBE
Set je = Nothing
Const TimeDelay As Long = 250 'задержка в миллисекундах между неудачными попытками сжатия'
Const MaxIterations As Long = 10 'количество повторов сжатия при неудачных попытках сжатия'
Dim mlngStep As Long 'номер текущего выполняемого шага'
Private Sub Form_Load()
Me.TimerInterval = 1 'запускаем таймер'
End Sub
Private Sub Form_Timer()
Me.TimerInterval = 0 'останавливаем таймер (исключаем повторное событие в момент сжатия)'
Select Case mlngStep
Case 0 'Первый шаг "Обслуживания БД"'
Call gsTryToCompactDB("C:\Тест\MyDB1.mdb")
Case 1 'Второй шаг "Обслуживания БД"'
Call gsTryToCompactDB("C:\Тест\MyDB2.mdb")
'Case N ''N-ный шаг "Обслуживания БД"'
'Действия по обслуживанию БД...'
'Приращиваем значение mlngStep для выполнения следующего шага обслуживания'
'Запускаем таймер для выполнения следующего шага'
Case 2 'заключительный шаг (N+1), заканчивающий выполнение Проекта "Обслуживания БД"'
MsgBox "Обслуживание завершено!", vbInformation
DoCmd.Quit
End Select
End Sub
Private Function gsTryToCompactDB(CompactingDB As String)
Dim lngUserAnswer As Long
Static lngIterationCnt As Long
If gflngCompactDatabase(CompactingDB) = 0 Then 'сжимаем БД и проверяем, получилось ли'
'если сжатие произошло...'
mlngStep = mlngStep + 1
lngIterationCnt = 0
Me.TimerInterval = 1
Else
'если сжатие не произошло (возникла ошибка)...'
If lngIterationCnt <= MaxIterations Then
lngIterationCnt = lngIterationCnt + 1
Me.TimerInterval = TimeDelay
Else
lngIterationCnt = 0
lngUserAnswer = MsgBox( _
"В данный момент не получается сжать файл '" & _
CompactingDB & "'." & vbNewLine & _
"Попытаться сжать данный файл еще раз?", _
vbExclamation + vbYesNoCancel, "Не удается сжать БД")
Select Case lngUserAnswer
Case vbYes
Me.TimerInterval = TimeDelay
Case vbNo
mlngStep = mlngStep + 1
Me.TimerInterval = 1
Case vbCancel
DoCmd.Quit 'Выход из Acces'
End Select
End If
End If
End Function
Обратите внимание, что в процедуре Private Sub Form_Timer() указан путь к двум сжимаемым БД, который необходимо настроить в соответствии с именами и размещением сжимаемых БД на машине. Shell "C:\Program Files\Microsoft Office\Office\Msaccess.exe " & _
"""C:\Тест\DBServiceProvaider.mdb""", vbMaximizedFocus
DoCmd.Quit
Обратите внимание, что сначала указывается полный путь к исполнимому файлу Access, а затем полный путь к созданной "DBServiceProvaider.mdb", при этом как тот, так и другой необходимо указать в соответствии с расположением файлов на машине. 'Для Англоязычной версии Access'
CommandBars("Menu Bar").Controls("Tools"). _
Controls("Database utilities").Controls("Compact and repair database..."). _
accDoDefaultAction
'Для Русскоязычной версии Access'
CommandBars("Menu Bar").Controls("С&ервис"). _
Controls("&Служебные программы").Controls("С&жать и восстановить базу данных"). _
accDoDefaultAction 'Обращение к панели инструментов через индексы'
CommandBars(40).Controls(12). _
Controls(7).Controls(2). _
accDoDefaultAction
Данный вариант не зависит от локализации Access и работает в версиях 2000 и XP, но гарантировать невозможно, что в следующих версиях индексы останутся прежними, а значит, что приложение использующее данный код в следующих версиях останется работоспособным. |
|||||||
Так же в этом разделе:
|
|||||||
|
|||||||
|