Программирование на VBA. Оптимизация приложений

Программирование на VBA. Оптимизация приложений.

Оптимизация приложения представляет собой тему для бесконечного обсуждения и споров между разработчиками. Всем нужны оптимальные решения, но что же точно означает термин "оптимальный"? Одни полагают, что "оптимальный" значит самый быстрый, поэтому самая высокоскоростная технология всегда будет оптимальной. Другие утверждают, что оптимальным является такое решение, которое обеспечивает большую надежность, даже если при этом приложение работает медленно и генерирует чрезмерное количество предупреждений. Третьи считают, что целью оптимизации является эксплуатационная надежность и расширяемость. Кто же прав? Ответ заключается в том, что, как часто это бывает в жизни, правы все и все они ошибаются. На самом деле оптимизация представляет собой попытку уравновесить все три представленные мнения.

Оптимальным является такое решение, которое обеспечивает пользователя приемлемым быстродействием приложений. Обычно пользователи не в восторге от медленно выполняющихся приложений. Однако, если оптимальное решение представляет собой лишь способ быстрого разрушения и потери данных, разработчик пошел на неразумный компромисс. Если приложение работает достаточно быстро и доказало свою надежность, существуют неплохие шансы, что пользователи захотят пользоваться таким приложением и дальше. Однако необычные решения по дизайну и неортодоксальное выполнение данного дизайна могут затруднить дальнейшее улучшение и расширение возможностей хорошо работающего приложения.

Приложения с некоторой точки зрения напоминают автомобили. Мало кто захочет оказаться за рулем очень быстрого автомобиля, у которого слабые тормоза и плохая управляемость. Всем нужен автомобиль, который заводится каждый день и доставляет куда нужно без особой суматохи. Наконец, счета за ремонт и обслуживание не должны превышать разумных величин. Хотя в данной статье рассматривается оптимизация скорости работы приложения, необходимо помнить, что приложение, кроме того, должно быть надежным, стабильно работающим и расширяемым.

В данной статье рассматривается много методик оптимизации приложения. Почти все они имеют некоторые ограничения, а некоторые иногда могут только ухудшить работу с приложениями. Даже лучшие из описанных методик могут оказаться бесполезными в данном конкретном случае. Слепо копируя некоторые подходы, можно потерять плоды других усилий. Тем не менее, разумно воплощая на практике уроки данной статьи, можно добиться значительного повышения производительности приложений.

Единственный способ узнать, подойдут ли данные методики, — поэкспериментировать с ними. Часто результаты могут оказаться совершенно неожиданными. Подход, который прекрасно работает в одном случае, может не оказать никакого ощутимого влияния в другом. Оптимизация — сложный процесс, поскольку рабочее окружение является сложной и постоянно меняющейся средой, и, кроме того, цель оптимизации может оказаться трудно достижимой.

Существуют тысячи вопросов об оптимизации, которые можно обсудить. В данной статье рассмотрение начинается с наиболее общих вопросов и заканчивается непосредственно касающимися приложения, т.е. сначала речь пойдет об аппаратном обеспечении и операционной системе, а затем о различных методиках кодирования.

Оптимизация аппаратных средств и ОС Windows

Каждый модуль Access 2000 обладает множеством возможностей для настройки приложения, но нельзя рассчитывать на какой-либо успех в работе, если компьютер, на котором выполняется приложение, является устаревшим или если недостаточно оперативной памяти. Обычно дешевле приобрести современное аппаратное обеспечение, чем платить разработчикам за улучшение программы, достигая при этом одинаковой производительности приложений. Аппаратное обеспечение дешевле, чем программное, и обновление оборудования способствует повышению производительности всего совместимого программного обеспечения, которое установлено на данном компьютере.

Естественно предположить, что приложение Access (или любое подобное приложение) будет быстрее работать на более быстродействующем компьютере. Если имеется возможность обновить оборудование, ею следует воспользоваться. Компания Microsoft опубликовала минимальные требования для работы Access или Microsoft Office 2000. Однако разработчики быстро обнаруживают, что попытка запустить приложение Access 2000 на компьютере, удовлетворяющем этим минимальным требованиям, приводит к разочарованию. На самом деле минимальные требования для работы с приложениями должны быть такими:

• Процессор Pentium 133 МГц,

• 32 Мб ОЗУ (особенно под управлением Windows NT),

• Если выбирать между модернизацией процессора и установкой ОЗУ большего объема, следует выбрать ОЗУ. Учитывая мировое падение цен на микросхемы памяти, увеличение объема ОЗУ — самый дешевый способ повышения производительности PC. Необходимо помнить о том, что требования для компьютера разработчика значительно выше. У разработчика должен быть установлен, по крайней мере, процессор Pentium II 233 МГц и 32-64 Мб ОЗУ.

• Даже если объем оперативной памяти пользовательского компьютера достаточно велик, ее все равно может оказаться недостаточно. Access, как и любая другая серьезная база данных, для своей нормальной работы требует значительного объема памяти. Необходимо убедиться в том, что для работы данного приложения достаточно памяти.

• Для экономии оперативной памяти необходимо отказаться от использования любых хранителей экрана, обоев, фоновых картинок и всех других подобных "излишеств". Только необходимые приложения должны использовать ресурсы процессора при выполнении приложения. Чем больше оперативной памяти будет отведено под выполнение приложения, тем быстрее оно будет работать.

• Не следует использовать виртуальные диски, организованные в оперативной памяти. В современной 32-разрядной операционной системе для них нет места.

• Необходимо регулярно очищать корзину и удалять временные файлы (особенно файлы Internet и электронной почты!). Базы данных требует значительного объема дискового пространства, а эти файлы могут поглотить массу места еще до того, как пользователь успеет это осознать.

• Рекомендуется воспользоваться преимуществами утилит дефрагментации диска. В зависимости от используемой операционной системы дефрагментация может производиться автоматически. Компьютерные файлы не хранятся в одном месте. При создании, редактировании, сохранении и удалении файлов используются любые свободные кластеры на жестком диске. Файл .MDB не является исключением. Если жесткий диск сильно фрагментирован, даже простой поиск происходит значительно дольше, потому что компьютер вынужден просматривать весь диск в поисках необходимого результата.

• Следует избегать программного сжатия дисков (включая сжатие NTFS). Выполнение базы данных на сжатом диске приводит к значительному снижению производительности приложения.

• Можно приобрести дополнительный жесткий диск. В зависимости от того, какую задачу решает приложение, может понадобиться в 5-10 раз больше свободного пространства на диске, чем занимает файл .MDB. Недостаток свободного места снижает скорость выполнения больших запросов, длительных операций, компиляции, обслуживания, импорта и запросов действия. В последние годы цены на жесткие диски сильно упали.

• Рекомендуется отключить Journal (Журнал) в Outlook. Журнал Outlook генерирует запись при каждом запуске и выходе из приложения. Этот журнал может стать очень большим и поглощать дисковое пространство и процессорное время, необходимые приложению.

• Необходимо использовать освободившуюся оперативную память. Приложения очень хорошо поглощают оперативную память, но неохотно ее освобождают. Со временем многие приложения, включая Access, уменьшают действительный объем доступной оперативной памяти. Периодический выход из среды Access позволит Windows перераспределить освободившуюся память.

• Windows и Office следует устанавливать с локального местоположения. Рекомендуется не запускать их в локальной сети. В настоящее время такой подход кажется немного необычным, но до сих пор существует масса случаев, где он оправдывает себя.

• Настройка файла подкачки (файла свопинга или виртуальной памяти) также может помочь повысить производительность приложения. Когда объем памяти для Access недостаточен, он занимает дисковое пространство файла подкачки и обращается с ним как с дополнительным ОЗУ. Если виртуальной памяти недостаточно, Access должен постоянно записывать и считывать информацию с диска, чтобы выполнять необходимые операции. Увеличение объема виртуальной памяти может привести к значительному повышению скорости выполнения приложения. Однако следует помнить, что чтение и запись на диск протекает в сотни раз медленнее, чем работа с ОЗУ. Увеличение объема файла подкачки поможет только в том случае, если диск дефрагментирован.

СОВЕТ

Следует убедиться, что файл подкачки не размещен на сжатом диске или в разделе. Работа со сжатым диском замедляет чтение и запись временных файлов.

Может оказаться, что многие из приведенных советов осуществить невозможно, но при разработке приложения Access их обязательно следует учитывать и соответственно предлагать для реализации. Другими возможностями оптимизации разработчик Access может воспользоваться во всей полноте.

СОВЕТ

У многих разработчиков приложений компьютеры намного более быстродействующие, чем компьютеры, на которых будет выполняться приложение. Необходимо протестировать приложение на компьютере, сравнимом с компьютерами пользователей. Если аппаратное обеспечение отличается от компьютера к компьютеру, приложение следует разрабатывать, рассчитывая на самый низкоскоростной компьютер.

Установка приложения с учетом достижения оптимальной производительности

Способ настройки файлов приложения может оказать ощутимый эффект на скорость его выполнения. Следующие указания помогут вам оптимизировать работу приложения и в полной мере воспользоваться советами, которые приведены в других разделах данной статьи.

• Необходимо отделять данные от приложения. В качестве стандартной практики рекомендуется создавать один файл .MDB для хранения таблиц и размещать его в сети, а другой файл .MDB — для хранения запросов, форм, отчетов и т.д. Это поможет повысить производительность, улучшить работу в многопользовательской среде и обслуживание. Из всех советов, приведенных в данной статье, от этого следует отказываться только по очень важной причине.

• Рекомендуется использовать текущую версию файла рабочей группы (system, mdw). Хотя в приложении можно пользоваться предыдущими версиями файла рабочей группы, текущая версия обеспечивает более высокую производительность.

• Необходимо регулярно производить сжатие базы данных. Кроме того, базу данных следует сжимать после выполнения обширных операций импорта, удаления и обновления. Сжатие базы данных обеспечивает использование свободного места между страницами данных (резерва) и позволяет выполнить перерасчет статистики базы данных, использующейся при оптимизации запросов. (Более подробно данная тема рассмотрена дальше в этой статье.) Сжатие базы данных, кроме того, позволяет освободить дисковое пространство для других целей. Access 2000 предлагает новую возможность — сжатие базы данных при выходе из базы данных. Для этого необходимо установить флажок Compact on Close (Сжимать при закрытии) во вкладке General (Общие) диалогового окна Options (Параметры) в меню Tools | Options (Сервис | Параметры). Некоторые разработчики обнаружили, что предыдущие версии Access следует сжимать дважды, чтобы добиться хороших результатов. Можно попробовать запустить базу данных из отдельного файла .MDB и позволить этому стартовому приложению выполнить сжатие при запуске базы данных. В сочетании с опцией Compact on Close при каждом выполнении будет производиться двойное сжатие.

• При возможности следует установить версию .MDE для приложения. При этом существует несколько преимуществ. Версия .MDE требует наличия скомпилированных модулей, а скомпилированные модули выполняются быстрее, чем нескомпилированные. Кроме того, для .MDE требуется меньший объем ОЗУ и меньше дискового пространства, чем для .MDB, что повышает производительность.

СОВЕТ

Перед инсталляцией .МDЕ необходимо убедиться, что пользователи осведомлены о невозможности внесения ими изменений в приложение. Учитывая все возрастающую популярность Access, в последнее время многие пользователи настаивают на возможности дальнейшей модификации приложения. Кроме того, необходимо тщательно протестировать приложения, поскольку МDЕ выдают сложные для понимания сообщения об ошибках.

Оптимизация настройки механизма Jet Database Engine

Хорошо настроенный компьютер и оптимизированная инсталляция базы данных не помогут повысить производительность, если не оптимизировано само приложение. Настоящее приложение базы данных состоит из нескольких частей. Самой важной из них является механизм баз данных Jet. Механизм Jet — это центр почти всего, что происходит в приложении баз данных Access, и его можно оптимизировать точно так же, как и другие функции Access. Попытки оптимизировать Jet должны быть предприняты после разработки большей части, если не всего, приложения; при этом необходимо затратить достаточное количество времени на оценку различных методик оптимизации.

Jet 4.0, который поставляется вместе с Access 2000, обладает несколькими преимуществами над своими предшественниками. Такие возможности, как новые типы данных, сжатие текста, индексация полей примечаний, внедрение ANSI SQL-92, управление SQL Security, большая гибкость в работе с внешними индексами, улучшенная схема репликации и улучшенные блокировки, обещают способствовать повышению производительности. Все эти прекрасные возможности подробно описываются в других статьях. В этой статье рассматривается только их влияние на производительность.

Механизм Jet выполняет основную оптимизацию без вмешательства разработчика или пользователя. Из всех сложных действий, которые выполняет Jet, можно увидеть только около 15, если заглянуть в системный реестр. Эти установки оказывают серьезное влияние на многие аспекты функционирования системы, поэтому обращаться с ними следует с осторожностью. Изменение установки может привести к результату, совершенно противоположному тому, который ожидался. Даже если приложение на первый взгляд работает быстрее, может оказаться, что нанесен серьезный ущерб стабильности и совместимости. Список установок Jet Registry приведен в табл. 1.

Таблица 1. Установки в системном реестре для Jet 4.0.

Ключ

Описание

Значение по умолчанию

Место нахождения ключа

Константа для метода SetOption

ExclusiveAsyncDelay

В эксклюзивной среде это – количество миллисекунд, в течение которых Jet должен ожидать перед выполнением неявной транзакции.

2000

\HKEY LOCAL MASH1NES

\SOFTWARE\MICROSOFT

\JET\4.0\ENGINES\JET 4.0

dbExclusiveAsyncDelay

FlushTransactionsTimeout

Когда здесь введено значение, задержки SharedAsyncDelay и ExclusiveAsyncDelay отключены. Это значение указывает число миллисекунд перед запуском асинхронных записей, если в кэш не были добавлены страницы.

500

\HKEY LOCAL MASH1NES\SOFTWARE

\MICROSOFT\JET

\SOFTWARE\MICROSOFT\JET

\4.0\ENGINES\JET 4.0

dbPlushTransactions

Timeout

ImplicitCornrnitSync

Определяет, должна ли ожидать система выполнения неявных транзакций перед продолжением процесса. Если ключ установлен в значение No, Jet выполняет неявные транзакции асинхронно.

No

\HKEY LOCAL MASH1NES

\SOFTWARE\MICROSOFT\JET

\4.0\ENGINES\JET 4.0

dblmplicitCommitSync

LockDetay

Число миллисекунд, в течение которых Jet ожидает, прежде чем повторить попытку блокировки.

100

\HKEYLOCALMASHINES

\SOFTWARE\MICROSOFT\JET

\4.0\ENGINES\JET 4.0

dbLockDelay

LockRetry

Количество повторных попыток для блокировки страницы.

20

\HKEY LOCAL MASH1NES

\SOFTWARE\MICROSOFT\JET

\4.0\ENGINES\JET 4.0

dbLockRetry

MaxBufferSize

Размер кэша Jet в килобайтах.

0

\HKEYLOCALMASHINES

\SOFTWARE\MICROSOFT\JEr

\4.0\ENGINES\JET 4.0

dbMaxBufferSize

MaxLocksPerRte

Максимальное количество блокировок, разрешенных для одной операции. Если для транзакции требуется больше блокировок, она разбивается на несколько транзакций, которые могут быть проведены отдельно и частично. Данную установку можно использовать для предотвращения проблем, которые могут возникнуть с проблем, которые могут возникнуть с Novell NetWare 3.1.

9500

\HKEYLOCALMASHINES

\SOFTWARE\MICROSOFT\JET

\4.0\ENGINES\JET 4.0

dbMaxLocksPerRle

Ключ

Описание

Значение по умолчанию

Место нахождения ключа

Константа для метода SetOption

Pages LockerfIoTableLock

(Новая возможность Access 2000)

Обеспечивает эксклюзивный доступ к таблице

для проведения обновлений, позволяя

программно управлять количеством блокировок

страницы, перед достижением которого Jet

попытается блокировать всю таблицу.

Значение 50 заставит Jet попытаться

блокировать таблицу на 51-й блокировке

страницы. Если блокировка таблицы не

удастся, Jet совершит новую попытку на 101-

и блокировке странице. При нулевом

значении данная функция отключается.

0

\HKEY.LOCALMASHINES

\SOFTWARE\MICROSOF7\

JET\4.0\ENGINES\JET 4.0

Отсутствует

PageTimeoirt

Количество времени (в миллисекундах), в

течение которого блокированная на

чтение страница удерживается в кэше

перед обновлением.

5000

\HKEYLOCAL MASHINES

\SOFTWARE\MICROSOFT\

JET\4.0\ENGINES\JET 4.0

dbPageTimeout

RecycteLVs

Определяет, будет ли Jet повторно

использовать OLE, примечания и

бинарные страницы.

0 (отключено)

\HKEY LOCAL MASHINES

\SOFTWARE\MICROSOFT

\JET\4.0\ENGINES\JET 4.0

dbRecycteLVs

SharedAsyncDelay

В общей среде это - количество миллисекунд,

в течение которых Jet должен ожидать перед

проведением неявной транзакции.

50

\HKEYLOCAL MASHINES

\SOFTWARE\MICROSOFT

\JET\4.0\ENGINES\JET 4.0

dbSharedAsyncDelay

Threads

Количество фоновых подлроцессов,

использующих Jet.

3

\HKEYLOCAL MASHINES

\SOFTWARE\MICROSOFT

\JET\4.0\ENGINES\JET 4.0

Отсутствует

UserCommitSync

Определяет, ожидает ли система завершения

операций записи перед продолжением процессов.

Если ключ установлен в значение No, механизм

Jet выполняет явные транзакции асинхронно.

Yes

\HKEY LOCAL MASHINES

\SOFTWARE\MICROSOFT

\JET\4.0\ENGINES\JET 4.0

dbllserCommitSync

CompactByPKey

Определяет порядок кодировки начальных

значений. При значении 1 Jet повторно

отсортирует записи по начальному значению.

При значении 0 записи остаются в том

порядке, в котором они были введены

(естественный порядок).

1

\HKEY LOCAL MASHINES

\SOFTWARE\MICROSOFT

\JE7\4.0\ENGINES

Отсутствует

System DB

Полный путь и имя файла рабочей группы.

nyn,Access

\system.md

\HKEY LOCAL MASHINES

\SOFTWARE\MICROSOFT

\JET\4.0\ENGINES

Отсутствует

PrevFormatCompact

WithUNICODECompression

(Новая возможность Access 2000) Разрешает

или предотвращает сжатие хранящихся

текстовых полей и полей примечаний. По

умолчанию сжатие разрешено.

1

\HKEY LOCAL MASHINES

\SOFTWARE\MICROSOFT

\JET\4.0\ENGINES

Отсутствует

JetShowPlan

По умолчанию не инсталлирован. При

создании разработчиком и установке в

значение On (зависит от регистра) Jet

создает в текущей папке текстовый файл с

именем showplan.out. При выполнении

запросов в этом файле регистрируются планы

выполнения. По окончании разработки ключ

необходимо отключить.

По умолчанию

не инсталлирован.

\HKEY LOCAL MASHINES

\SOnWARE\MICROSOFT

\JET\4.0\ENGINES\JET 4.0

Отсутствует

Безопасное изменение установок Jet

Существует три способа, позволяющие изменить установки Jet в системном реестре:

• Непосредственное изменение установок, принятых по умолчанию, с помощью RegEdit.exe,

• Создание пользовательского профиля для отмены установок, принятых по умолчанию, и вызов данного профиля с помощью опции командной строки /profile,

• Временная отмена в приложении установок, принятых по умолчанию, с помощью метода SetOption объекта базы данных.

Наиболее предпочтительна третья опция. Ее использование не только не вызывает сложностей, но и позволяет настроить каждое приложение отдельно. В приложении можно даже изменять установки в разное время, чтобы действительно оптимизировать быстродействие. При таком подходе установки, принятые по умолчанию, никогда не изменяются, поэтому новые проекты начинаются на общей основе.

Для временной отмены установок в системном реестре нет необходимости использовать вызовы API. Механизм баз данных предлагает метод, который использует два аргумента. Синтаксис приведен ниже:

DBEngine.SetOption ConstantNameofSetting, value

Например, производительность пакета обновлений можно повысить, если заставить Jet ожидать несколько дольше перед выполнением неявных транзакций. Соответствующий код для временного увеличения периода ожидания SharedAsync Delay до 1 секунды (1000 миллисекунд) должен быть таким:

DBEngine.SetOptiuon dbSharedAsyncDelay, 1000

Данная установка остается в силе, пока приложение не изменит ее с помощью другого метода — SetOption — либо пока DBEngine не выйдет из области действия, либо пока приложение не прекратит работу. Метод SetOption не записывает значения в системный реестр. С помощью метода SetOption нельзя отменить установки, принятые по умолчанию для SystemDB, CompactByPkey или подпроцессов.

ПРИМЕЧАНИЕ

Если приложение использует источник данных ODBC, установки в системном реестре следует искать в ключе д., t- \HKEY_LOCAL_MACHINES\SOFTWARE\MICROSOFT\JET\4.0\ENGINES\ODBC.

Средства оценки производительности

Разработчику важно знать, какое же влияние оказали на приложение все предпринятые усилия. Для отслеживания и оценки относительной производительности, достигнутой с помощью той или иной методики, можно использовать несколько разных инструментальных средств. Первый способ — это таймер для измерения количества времени, которое занимает выполнение процесса. Второй способ — недокументированная функция для подсчета операций с диском, кэшем и операций блокировки. Третий способ — способность Access отобразить план выполнения запросов для просмотра разработчиком. Первые два способа можно применять для оценки производительности практически каждой методики, описанной в данной статье. Планы выполнения запросов относятся только к оптимизации запросов.

Хотя VBA содержит функцию Timer(), она может оказаться не совсем точной для оценки предпринятых усилий по оптимизации. Функция VBA Timer() записывает в секундах промежуток времени, прошедший с полуночи. Поскольку измеренное время кодируется однозначным числом, в данном случае эта функция может оказаться не совсем точной, особенно для оценки промежутков времени меньше 1 секунды. Многие разработчики, кроме того, используют функцию GetTickCount. Поскольку эта функция привязана к часовому таймеру PC, она возвращает время не в миллисекундах, а в приращениях 1/18 секунды. API Windows предлагает таймер, который отслеживает время в миллисекундах. Функция timeGetTimeO измеряет промежуток времени с момента запуска Windows. Поскольку она использует другой аппаратный счетчик, то возвращает время с точностью до миллисекунды.

Используя timeGetTime(), можно вставить строку кода до и после выполнения любой критической операции и получить очень точное измерение времени, которое понадобилось для завершения действия.

Для использования вызова API необходимы две вещи: объявление функции и глобальная переменная для хранения времени запуска таймера. В разделе объявлений модуля необходимо ввести следующие три строки:

Private Declare Function a2ku apigettime Lib "winmm.dll" _

Alias "timeGetTime" () As Long

Dim Ingstartingtime As Long

Затем можно создать подпрограмму для запуска часов и функцию остановки часов.

Sub a2kuStartClock()

Ingstartingtime = a2ku_apigettime()

End Sub

Function a2kuEndClock()

a2kuEndClock = a2ku_apigettime() - Ingstartingtime

End Function

Использование данных функций для оценки производительности запроса иллюстрирует код, приведенный в листинге 1.

Листинг 1. Определение времени выполнения запроса.

Sub QueryTimer(strQueryName As String)

Dim db As Database

Dim qry As QueryDef

Dim rs as Recordset

Set db = CurrentDb()

Set qry = db.QueryDefs(strQueryName)

'Запуск часов. a2kuStartClock

Set rs = qry.OpenRecordset()

'Остановка часов и вывод результата в окне отладки.

Debug.Print strQueryName & " executed in: " & a2kuEndClock & _

" milliseconds"

rs.Close

End Sub

ПРИМЕЧАНИЕ

Для достижения наибольшей точности измерения необходимо разместить вызов процедур a2kuStartClock и a2kuEndClock как можно ближе к местонахождению рассматриваемой процедуры.

О происходящем "за кулисами”

Хотя определение времени играет не последнюю роль, но только на основе этой информации невозможно судить о производительности приложения во время разработки. Скорее всего, разработка происходит. на высокоскоростном компьютере, и тестирование производится на небольшом наборе тестовых данных, который наверняка меньше набора, используемого настоящим приложением. Более того, таймер не может сообщить, как будет выполняться приложение на другом компьютере с меньшим объемом памяти или более низкоскоростным диском. Для более точного наблюдения за приложением можно воспользоваться другой недокументированной функцией — ISAMStats. Функция ISAMStats не документирована и не поддерживается, поэтому информацию, которую она сообщает, можно использовать лишь в качестве общих указаний. При запуске функция производит измерение шести важных оказывающих влияние на производительность операций. Она подсчитывает все обращения чтения и записи на диск, обращения чтения в кэше, опережающего чтения, размещения блокировок и освобождения блокировок. Данную функцию можно использовать в Access 2000 и предыдущих версиях Access. Синтаксис функции очень простой:

DBEngine.ISAMStats(опция,[reset])

Существует шесть возможных значений для аргумента опция:

Значение аргумента опция

Описание

0

1

2

3

4

5

Запись на диск

Чтение с диска

Чтение из КЭШа

Чтение из кэша (опережающее чтение)

Размещение блокировки

Освобождение блокировки

Необязательный параметр переустановки позволяет переустановить отдельные счетчики на нулевое значение. Чтобы воспользоваться данной функцией для оценки производительности, необходимо либо вычесть одни показания из предыдущих, либо переустановить счетчик на нулевое значение и затем вы-поднять оценку. В листинге 2 приведен один из способов использования функции ISAMStats для оценки производительности.

Листинг 2. Один из способов использования ISAMStats для оценки производительности.

Sub PrintStats()

Dim i As Integer

Debug.Print

Debug.Print "Disk Reads: " & ISAMStats (0, Falsa)

Debug.Print "Disk Writes: " & ISAMStats(1, False)

Debug.Print "Cache Reads: " & ISAMStats(2, False)

Debug.Print "Read-Ahead Cache Reads: " & ISAMStats(3, False)

Debug.Print "Locks Placed: " & ISAMStats(4, False)

Debug.Print "Locks Released: " & ISAMStats(5, False)

For i = 0 To 6

ISAMStats i, True

Next

End Sub

PrintStats может дать некоторое представление о том, почему одна методика работает быстрее, чем другая. Кроме того, эта информация может помочь сделать выбор между двумя подходами, выполнение которых занимает одинаковый промежуток времени. Взглянув на полученные данные, можно определить, как разная аппаратная конфигурация или больший набор данных может повлиять на производительность того или иного подхода.

Функции A2KU_TimeClock и PrintStats полезны только при сравнении одной методики с другой. Более того, надежные результаты можно получить лишь в том случае, если усреднить данные многих тестов, проведенных при разных условиях. Данные две функции можно с легкостью объединить в одну для проведения подобных повторяющихся тестов.

Далее в статье приведен пример использования этих функций для сравнения двух различных методик.

Оптимизация базы данных

Реляционные базы данных, включая Access, основываются на таблицах информации. Конструкция этих таблиц и отношения между ними оказывают сильное влияние на производительность приложения. Если таблицы не оптимизированы, проиндексированы и обращаются к свойствам друг друга, каждый запрос и процедура кода выполняются значительно дольше. Лучшее, что можно сделать для оптимизации базы данных, — правильно сконструировать таблицы.

Составление таблиц данных

Кроме обычных правил оптимизации, при построении таблиц необходимо помнить о следующем:

• При работе со связанными таблицами следует создавать постоянные связи.

• Использование простых подтаблиц в таблицах, где подтаблица является другой таблицей, а связи главный/подчиненный используют индексированные поля, оказывает ничтожно малое отрицательное влияние на производительность; однако более сложная подтаблица может значительно уменьшить скорость выполнения приложения. Вместо таблиц в запросах следует использовать подтаблицы. В данном случае они используются только при необходимости, а не каждый раз при открытии таблицы.

• Шаблоны ввода, поиска и правила верификации также должны использоваться только там, где они действительно необходимы, — в формах. Лучше всего создавать как можно более простые и легкие таблицы.

• Не рекомендуется создавать поля данных, размер которых будет больше, чем необходимо. Access предоставляет место для данных по размеру поля этих данных. Можно впустую потратить массу свободного места (и времени), используя типы данных или установки свойств, слишком большие для данных, содержащихся в этих полях.

Нормализация данных в целях повышения производительности

Нормализация уже рассматривалась в данной книге, поэтому здесь приведено лишь несколько замечаний.

• Нормализованная база данных позволяет экономить дисковое пространство, а также препятствует появлению конфликтующих или некорректных данных, поскольку она не содержит повторяющихся данных.

• В нормализованной базе данных используется меньше страниц данных и индексов, что ускоряет поиск.

• Когда имеет смысл нарушать правила нормализации? Иногда имеет смысл создавать некоторые модули приложения на основе ненормализованных таблиц, если данные таблицы являются временными таблицами, создание которых требует много времени. Однако при постоянном хранении данные должны находиться в нормализованной структуре. Следует помнить, что для обновления нормализованной структуры после действий с ненормализованными структурами необходимо создавать специальные процедуры, которые могут повлиять на производительность. Правила нормализации можно нарушать лишь в последнюю очередь.

• Еще один аспект нормальной формы, который обычно можно игнорировать без какого-либо ущерба, — это правило многократности данных, поскольку оно влияет лишь на второстепенные данные. Часто имеются дополнительные биты информации о покупателе или товаре, которые, если строго придерживаться правил, следует хранить в отдельных таблицах. Примером могут служить дневные и вечерние номера телефонов, номера факсов, сотовых телефонов, пейджеров, адреса электронной почты и т.д.

• Правила нормализации можно нарушать в тех случаях, когда это имеет смысл. Нарушение правил нормализации иногда позволяет повысить производительность для некоторых частей приложения. Но всегда следует помнить о том, что такое нарушение может повлиять на надежность, целостность данных и эксплуатационные качества.

Идеальная схема нормализации заставила бы разработчика создать таблицу контактов с полем ID покупателя, полем типа контакта и другими полями для хранения номеров телефонов и адресов электронной почты. Вероятно, такой подход можно считать излишеством, а дополнительная целостность и гибкость не стоят затраченных усилий. Вполне разумно хранить эту информацию в таблице покупателей и воспользоваться преимуществами повышенной производительности. Однако правило заключается в том, что нужно нормализовать все, что возможно. Это то, чего ожидает Jet, так как лучше всего он работает с нормализованными данными.

Создание индексов, ускоряющих выполнение запросов

• Индексы могут ускорить процесс получения данных примерно в 10 раз.

• Индексы, кроме того, могут замедлить обновления и ввод данных, поэтому их не следует создавать без особой необходимости.

• Рекомендуется создать такое начальное значение, которое что-нибудь значит для разработчика и пользователей. С точки зрения производительности нельзя разрешать Access создавать начальное значение путем вставки поля AutoNumber. Пока у пользователей нет других средств для идентификации своих записей данных, необходимо пользоваться чем-то более удобочитаемым. Можно попробовать применить номер телефона, номер карточки социального обеспечения, номер счета, код поставщика и т.д. Для того чтобы индекс на самом деле приводил к повышению производительности, необходимо его использовать вместе с запросами.

• Можно проиндексировать все поля, для которых приложение применяет условие отбора. Создание индексов на множественных полях в таблицах упростит оптимизацию запросов, созданных позже в процессе разработки.

• Можно проиндексировать поля с обеих сторон ожидаемой связки. Поскольку поля Order Number относятся как к таблице Order Detail, так и к таблице Orders, обе таблицы должны иметь соответствующий индекс. Но если необходимо выполнять много отчетов по дате заказа, возможно, данные поля также необходимо проиндексировать.

Раннее создание отношений для повышения производительности

Отношения между таблицами рекомендуется устанавливать в окне Relationships (Отношения). При создании отношения в этом окне разработчик имеет возможность определить свойства данного отношения. Кроме того, при этом Jet узнает о существовании отношения. Jet может использовать всю эту информацию для создания более эффективного плана оптимизации при запросах к данным. Это значительно повышает производительность.

Итак, следует нормализовать данные, создать индексы там, где они необходимы, экономно обращаться с типами данных и размерами и помнить о том, что таблицы должны быть простыми. Таблицы существуют для хранения данных, а не для их отображения.

Повышение производительности запросов

Хотя пользователи могут никогда и не увидеть запрос в приложении, большую часть работы выполняют именно запросы. Реляционная база данных была бы бесполезной без возможности выполнять запросы к данным. Однако запросы создаются по-разному. Даже если разработчик принял все меры для нормализации данных и создал все необходимые индексы, можно иметь запросы, которые выполняются не так быстро, как могли бы. Может даже существовать два запроса, дающие идентичный результат, но выполняющиеся по-разному.

Чтобы понять, как оптимизировать запросы, необходимо понимать, как их обрабатывает Jet. Каждый запрос проходит четыре этапа:

1. Определение — создается SQL-оператор с помощью одного из нескольких инструментальных средств.

2. Компиляция — SQL-строка разбивается на составные части.

3. Оптимизация — используя алгоритм оценки стоимости. Jet формулирует и тестирует несколько различных способов получения результата, который удовлетворяет данному SQL-оператору.

4. Выполнение — используя план оптимизации, Jet передает результирующий набор пользователю.

Можно определить запрос с помощью QBE Grid, SQL-строки, выполняющейся в коде, SQL-строки в свойстве источника формы, отчета или элемента управления либо с помощью любого другого средства, которое способно создавать SQL-операторы.

Jet размещает составные части строки в иерархической внутренней структуре. Эти части весьма напоминают ключевые слова SQL-оператора. В основе лежат базовые таблицы, используемые запросом (Из). Потом устанавливаются столбцы результата (Выбор). Далее следуют условия отбора или ограничения, заданные запросом (Где). Затем оцениваются отношения базовых таблиц (Объединение). Наконец, происходит сортировка результирующего набора (Сортировка). Такая структура переходит в фазу оптимизации.

Оптимизация представляет собой самый сложный этап. Jet оценивает и рассчитывает стоимость каждого возможного подхода. Это делается путем рассмотрения запроса под двумя различными углами: с точки зрения доступа к базовым таблицам и с точки зрения исследований связей между ними. Понимание действий Jet может способствовать разработке более быстрых запросов в дальнейшем.

Для Jet существует три способа получения строк данных из таблиц:

• Сканирование. Это самый дорогостоящий подход. При сканировании Jet должен прочесть каждую строку информации без использования индекса. Запрос вынуждает Jet сканировать таблицу, если запрос ограничен неиндексированными полями или условие отбора запроса удовлетворяется большей частью строк в таблице.

• Индекс. Jet использует индекс таблицы для чтения строк таблицы. Хотя Jet может считывать страницу данных больше чем один раз, такой подход работает намного быстрее, чем сканирование.

• Срочная оптимизация (Rushmorc Optimizations). Этот способ доступен только в тех случаях, когда установлены ограничения более чем для одного индекса в запросе. Срочная оптимизация позволяет Jet считать намного меньше страниц данных, иногда вообще ни одной. При использовании способа срочной оптимизации Jet читает только индексные страницы, что весьма эффективно.

Очевидно, что сканирования следует избегать при любой возможности и попытаться воспользоваться индексами. Но как проверить, будет ли для данного запроса действовать лучший способ — срочная оптимизация? Срочную оптимизацию нельзя просто включить или выключить, и не существует какого-то явного индикатора для определения этого. Она всегда включена, но лишь определенные виды запросов могут воспользоваться преимуществами данного способа. Для того чтобы была реализована срочная оптимизация, должны удовлетворяться следующие три условия:

• Запросы должны содержать множественные индексы.

• Для данных индексированных полей должны быть заданы условные ограничения.

При установке критерия отбора данные индексы должны использоваться одним из трех способов:

• Перекрытие индексов. Условие отбора с оператором AND. Jet может применить способ срочной оптимизации на данном наборе ограничений, поскольку индексированы оба поля.

WHERE CompanyName='Ernst Handle' And City='Graz'

• Объединение индексов. Условие отбора с оператором "OR". Jet может применить способ срочной оптимизации на данном наборе ограничений, поскольку индексированы оба поля.

WHERE CompanyName='Ernst Handle' Or City='Graz'

• Счетчики индексов. Составные запросы, возвращающие только счетчик записей. При срочной оптимизации обрабатывается каждый запрос, даже если не существует набора ограничений в условии Where.

SELECT Count(*) FROM Customers

Необходимо убедиться в том, что существуют и множественные индексы для всех таблиц, которые также выигрывают от этого. Кроме того, следует попытаться выразить запросы таким образом, который предполагает перекрытие или объединение данных индексов. Такой подход способствует оптимальному выполнению операций над базовыми таблицами в плане выполнения запроса.

После того как Jet определит способ доступа к данным в отдельных таблицах, он должен определить отношения таблиц между собой. Начинается этап объединения процесса оптимизации. Учитывая сведения о типах соединения, приведенные в табл. 2, можно определить вероятные стратегии соединения плана выполнения.

Таблица 2. Типы объединения: принцип работы и отличительные признаки.

Тип объединения

Принцип работы

Отличительные признаки

Возможное использование

Индексное слияние

Основную работу выполняют индексы.

С обеих сторон связи используются индексы. По крайней мере, один из индексов не допускает нулевых значений (начальное значение). Все таблицы должны соответствовать собственному формату Jet.

Везде, где возможно.

Индекс

Первая таблица сканируется, а затем строки второй таблицы выбираются по индексу.

Существует индекс для связанных полей второй таблицы. В данных индексах разрешены нулевые значения. Ограничения не используют индексы.

Если во второй таблице мало записей, если ее записи не отображаются в результирующем наборе или если условие отбора для первой таблицы слишком ограничительное.

Слияние

Обе таблицы сканируются

одновременно

Две таблицы сортируются по связанным полям. В результирующем наборе отображены данные из обеих таблиц.

Обе таблицы достаточно большие и сортируются по связанным полям.

Выборка

Вторая таблица сканируется и сортируется перед объединением.

Нет индексов для связанных полей таблицы.

Когда вторая таблица имеет небольшие размеры и для связанных полей второй таблицы не существует индексов.

Вложенная итерация

Построчная итерация по каждой таблице в отношении.

Ни с одной стороны объединения не существует индексов.

Только для очень малых таблиц и если нет другого выбора.

Jet выбирает один из этих планов в зависимости от таких факторов, как:

• Число записей в каждой базовой таблице,

• Число страниц данных, используемых базовыми таблицами,

• Местонахождение и тип таблицы — локальная ISAM или ODBC,

• Избирательность индексов таблиц — разрешены ли нулевые значения или повторения,

• Число страниц индекса.

Оценка типа результирующего набора для достижения оптимальной производительности

Jet, кроме того, рассматривает запрашиваемый результирующий набор. Например, для представления динамического множества может быть выполнен план, который эффективно представляет первую страницу данных, даже если отображение оставшихся записей происходит медленнее. Для образования динамического множества Jet создает набор уникальных ключевых значений, которые указывают на строки соответствующей базовой таблицы. Таким образом, для Jet достаточно получить только ключевые значения, а оставшиеся записи будут отображены тогда, когда они понадобятся пользователю. Однако в снимках Jet перед представлением результата собирает все записи и столбцы для результирующего набора. Если весь снимок не помещается в памяти, его часть переходит в файл подкачки, что отрицательно сказывается на производительности. Гораздо большую производительность можно получить при использовании большого динамического множества.

Во-первых, Jet оценивает имеющиеся опции доступа к базовым таблицам. Каждый план доступа к базовой таблице получает соответствующую оценку. Затем Jet оценивает имеющиеся опции объединения. Механизм Jet рассматривает только объединение результата отношения с базовой таблицей, а не объединение результатов одной связи с результатами другой. Это помогает ограничить количество времени, которое уходит на оптимизацию запросов. После анализа каждой доступной стратегии объединения с каждой стратегией доступа к базовым таблицам и рассмотрения типа результирующего набора записей Jet выбирает план.

Для выполнения запроса к таблицам, не являющимся таблицами ODBC, Jet очищает план, уменьшает его размер и передает на выполнение.

Компиляция и оптимизация происходят в первый раз при создании или редактировании запроса и выполнении. Запрос может остаться нескомпилированным. Если модифицировать запрос и сохранить его, но не выполнять, запрос остается нескомпилированным. Кроме того, если модифицировать индексы таблиц чли схему данных, оптимизация запросов может нарушиться. Рекомендуется всегда открывать запросы в режиме конструктора, сохранять их и затем выполнять до распространения приложения. Такой подход обеспечивает компиляцию запросов.

Для оптимизации запросов Jet производит сложную оценку стоимости. В число этих сложных оценок входит просмотр статистики базы данных. На основе статистических данных Jet сообщается о количестве страниц данных, страниц индекса, строк в таблицах и т.д. При нестандартном завершении работы базы данных статистика может быть искажена, транзакции отменены либо базе данных может понадобиться сжатие. При оптимизации всегда вначале следует сжимать базу данных.

Как известно. Jet создает план выполнения для каждого запроса. Создав в системном реестре запись

\HKEY_LOCALMACHINS\SOFTWARE\MICROSOFT\JET\4 . 0\ENGINES\DEBUG

и установив значение строки в ON, Jet создает или добавляет текстовый файл в текущей папке плана выполнения запроса. В этот план входит многое из того, что было описано выше. План изменить невозможно, если не изменять схему данных, структуру запроса или ограничения запроса. Чем подробнее план, тем лучше. В листинге 3 приведен план выполнения для запроса Quarterly Orders для фирмы Northwind.

Листинг 3. План выполнения с использованием преимуществ срочной оптимизации.

--- Quarterly Orders ---

  • Inputs to Query –
  • Table 'Customers'

Using index 'PrimaryKey'

Having Indexes:

PrimaryKey 91 entries, I page, 91 values

which has 1 column, fixed, unique, primary-key, no-null

PostalCode 91 entries, 1 page, 87 values

which has 1 column, fixed

CompanyName 91 entries, 3 pages, 91 values

which has 1 column, fixed

City 91 entries, 1 page, 69 values

which has 1 column, fixed

Table 'Orders'

- End inputs to Query -

01) Restrict rows of table Orders

using rushmore

for expression "Orders.OrderDate Between #1/1/98# And #12/31/98#"

02) Outer Join result of '01)' to table 'Customers'

using index 'Customers!PrimaryKey'

join expression "Orders.CustomerID=Customers.CustomerID"

03) Sort Distinct result of '02)'

Можно увидеть раздел базовой таблицы с анализом индексов и количеством записей, баз данных, страниц индекса и оцениваемых значений. Обратите внимание, что данный запрос может быть обработан методом срочной оптимизации, поскольку существует индекс для поля Order Date таблицы Orders. План определяет связь между таблицей Orders и таблицами Customers как внешнюю связь. Индексы и срочная оптимизация прекрасно оптимизируют данный запрос. Если разработчик не уверен в том, насколько хорошо был оптимизирован запрос, можно проверить план выполнения. SHOWPLAN не документирован и не поддерживается. Некоторые запросы не создают планов, и некоторые планы неверны, но с долей осторожности все же можно пользоваться JetShowPlan.

В листинге 4 приводится пример плохо оптимизированного запроса.

Листинг 4. Недостаточно хорошо оптимизированный план выполнения запроса без использования преимуществ оптимизации Jet.

SELECT Customers.CustomerID, Customers.CompanyName

FROM Customers INNER JOIN Orders ON

Customers.CustomerID = Orders.CustomerID

WHERE ((Not (Orders.ShipCountry)="USA")) ;

-— Customers with Shipping Address Outside USA ---

- Inputs to Query –

Table 'Orders'

Table 'Customers'

Using index 'PriearyKey'

Having Indexes:

PrimaryKey 91 entries, 1 page, 91 values

which has 1 column, fixed, unique, primary-key, no-nulls

PostalCode 91 entries, 1 page, 87 values

which has 1 column, fixed

CompanyName 91 entries, 3 pages, 91 values

which has 1 column, fixed

City 91 entries, 1 page, 69 values

which has 1 column, fixed

- End inputs to Query -

01) Restrict rows of table Orders

by scanning

testing expression "Not Orders.ShipCountry="USA""

02) Inner Join result of '01)' to table 'Customers'

using index 'Customers!PrimaryKey'

join expression "Orders.CustomerID=Customers.CustomerID"

Недостаток данного запроса заключается в способе задания критерия отбора. Поскольку ограничения не используют индекс, запрос вынужден сканировать таблицу и проверять каждую запись на удовлетворение условию. Сканирование таблицы — слишком дорогостоящий процесс. Использование индексов сделало бы данный запрос намного более эффективным.

Повышение скорости выполнения запросов

Оптимизация запросов в Jet — процесс довольно сложный, но это не значит, что в нем невозможно разобраться. Ниже приведены советы, которые помогут ускорить выполнение запросов:

• Рекомендуется создавать индексы для всех полей, которые будут использованы для определения критерия отбора.

Необходимо создавать индексы с обеих сторон связей в запросах.

• Вместо уникальных индексов лучше пользоваться начальными значениями. Поскольку начальные значения запрещают использование пулевых значений, запрос может использовать преимущества большего количества типов объединения.

• В результирующем наборе не следует отображать какие-либо лишние столбцы. Обработка и отображение каждого столбца занимает дополнительное время.

• Рекомендуется воздерживаться от употребления сложных выражений в запросах.

• Следует избегать функции IIF() (немедленное IF). IIF() оценивает и истинное, и ложное значения перед тем, как выдать результат. Если выполнять данную операцию для каждой записи, это может сильно повлиять на производительность.

• При использовании вложенных запросов рекомендуется записывать все вычисления в последнем запросе серии.

• Вместо Count([Customer]) лучше применять Count(*), поскольку при срочной оптимизации Count(*) обрабатывается быстрее — перед подсчетом не нужно проверять нулевые значения.

• По возможности следует пользоваться оператором Between для уменьшения количества строк в результирующем наборе вместо операторов "больше чем" и "меньше чем".

• Обычно размещение условия со стороны "один" отношения "один-ко-многим" — самый эффективный способ, но не всегда. Можно попробовать передвинуть ограничение к стороне "многие", чтобы проверить, не изменится ли производительность. После каждого изменения условий отбора необходимо тщательно проверять результирующий набор.

• Нормализованные таблицы могут хранить данные с использованием меньшего количества страниц данных и страниц индекса. Нормализация должна стать правилом, и нарушать ее можно лишь при отсутствии другой альтернативы.

• Рекомендуется по возможности поэкспериментировать с подчиненными запросами вместо использования объединений или сложных условий OR. Оптимальный выбор зависит от многих дискретных факторов, и только эксперимент поможет решить, какой подход использовать.

• Внешние связи следует использовать только при крайней необходимости, поскольку они автоматически требуют проведения сканирования доминантной (сохраняемой) таблицы в объединении.

• Вместо SQL-операторов в коде рекомендуется использовать сохраненные запросы с параметрами. Jet уже скомпилировал запросы с параметрами и создал для них план выполнения (хотя эти планы недоступны в SHOWPLAN.OUT). Использование скомпилированных и сохраненных запросов устраняет необходимость оценки и оптимизации SQL-строки. Access компилирует SQL-строки, использующиеся в качестве источника записей или источника строк для форм, отчетов или элементов управления, поэтому они остаются нетронутыми.

• Рекомендуется всегда использовать скомпилированные запросы.

• Для манипуляций с данными вместо DAO по возможности следует пользоваться запросами. Для решения этих задач запросы (SQL) всегда выполняются быстрее, чем DAO.

• Типы наборов записей следует выбирать с осторожностью. Если необходимо добавить или отредактировать данные, требуется динамическое множество. Снимок или однонаправленный снимок данных может понадобиться в том случае, когда необходимо только считать данные. Снимки дольше открываются, но прокручиваются быстрее.

• При открытии набора записей только для добавления данных следует использовать опцию dbAppendOnly. При этом строки не считываются.

• При использовании данных клиент/сервер необходимо тестировать запросы к серверу. Кроме того, рекомендуется ознакомиться с новыми возможностями Access 2000, имеющими отношение к архитектуре клиент/сервер. Запросы к серверу не всегда выполняются быстрее, чем запросы к вложенным серверным таблицам, но сетевой трафик при их использовании меньше.

• Большие запросы действия могут выполняться лучше, если присвоить свойству UseTransaction значение False. При проведении транзакций Access создает временные файлы. Иногда эти таблицы становятся слишком большими и уменьшают скорость выполнения запросов.

• При запрашивании данных с сервера необходимо применять методы CacheStart, FillCache и EndCache для обработки данных, поступающих с сервера.

• При работе с серверными данными следует избегать локальной обработки. Локальная обработка напоминает использование сложного выражения Group By с ключевым словом Distinct, использование оператора LIKE в текстовых полях или полях заметок, множественных агрегирующих функций в перекрестном запросе или перекрестных запросов без условий ORDER. Кроме того, следует избегать сложных внешних и внутренних комбинаций связей. Такие конструкции вынуждают сервер посылать громадные объемы данных на локальный PC для обработки, что значительно снижает производительность.

• Необходимо регулярно сжимать базу данных и проверять статистические показатели, используемые механизмом Jet для оптимизации запросов.

• Если это возможно, следует заполнить приложение таким же количеством тестовых данных, которое будет использоваться при работе с пользователями. Механизм Jet сможет оптимизировать запросы, используя те статистические показатели, которые точно отражают реальные условия выполнения запросов.

• Рекомендуется индексировать поля для сортировки.

• Если данные являются в основном статичными, следует рассмотреть возможность создания табличного запроса к необходимым данным вместо неоднократного запрашивания базы данных.

• Необходимо избегать использования доменных агрегирующих функций (DIookupO) для таблиц, которых нет в запросе.

Запросы представляют собой наиболее сложный аспект Access. К счастью, большую часть работы по оптимизации запросов выполняет механизм Jet. Информация, изложенная в данном разделе, поможет разработчику оказать механизму Jet содействие при оптимизации. Следует проверить результаты экспериментов в SHOWPLAN и с помощью подпрограмм PrintStats и QueryTimer, чтобы определить, какая комбинация решений выполняется с максимальной производительностью.

Оптимизация форм

Поскольку большую часть своего времени пользователи работают с формами, созданными разработчиком, следует позаботиться, чтобы все усилия, затраченные на оптимизацию, не были сведены на нет плохо спроектированными формами. Формы должны быть простыми и экономичными.

В начале...

При запуске приложения первое, что видят пользователи, — начальная форма. Она может представлять собой всплывающий экран с названием приложения и другой информацией или кнопочную панель для навигации по приложению. В любом случае из данной формы следует удалить код. Код запуска формы следует записать в стандартный модуль и выполнять только те процедуры, которые необходимы для запуска приложения. Поскольку такая форма будет загружаться быстрее, она создаст у пользователя хорошее первое впечатление. Возможно, выполнение некоторых операций можно отложить на какое-то время. После удаления модуля из формы свойству HasModule необходимо присвоить значение No. Это поможет создать простую форму, которая будет выполняться быстрее. Однако, изменив значение свойства HasModule, разработчик уничтожает весь код формы и код элементов управления. Перед изменением данного свойства следует убедиться, что все подпрограммы и функции размещены в стандартном модуле.

Начальная форма не должна содержать элементы управления ActiveX. Элементы ActiveX замедляют процесс загрузки больше, чем все другие элементы управления. Если в начальной форме необходимо использовать элемент ActiveX, возможно, следует ограничить количество других элементов управления.

Вместо макросов AutoExec лучше воспользоваться опцией Startup Form.

Быстрая загрузка изображений

Использование изображений в интерфейсе приложения демонстрирует графическую мощь Access. Однако то, что Northwind быстро отображает фотографии сотрудников, еще не значит, что разработчик может, не задумываясь, применять графику в приложениях. В данном разделе рассматривается использование графики в базе данных.

Следует попытаться использовать для изображений элемент управления Image. Для отображения некоторых видов графики он намного более эффективен, чем связанные или несвязанные фреймы.

При загрузке формы Access должен прорисовать или визуализировать каждый элемент управления. Не удивительно, что визуализация занимает значительно больше времени, чем совмещение. Чтобы убедиться, что тщательно подогнанные элементы управления случайно не перекрываются, необходимо воспользоваться командами Format | Vertical Spacing (Формат | Расстояние по вертикали) и Format | Horizontal Spacing (Формат | Расстояние по горизонтали).

Необходимо использовать минимально возможное количество цветов графических изображений; лучше всего — черно-белый вариант. Чем больше количество цветов данного изображения, тем больше памяти и времени процессора требуется на его обработку.

Графические и другие большие типы данных (заметки и OLE-объекты) следует исключить из первичных форм, а также из начальных таблиц и запросов. Для их отображения необходимо создать отдельную форму или запрос, вызываемый только по требованию пользователя. Это предотвращает затраты времени и ресурсов, необходимых для отображения огромных объектов, в то время как, возможно, отображение данного объекта и не является обязательным. Память, используемая формами, содержащими OLE-объек-ты, не освобождается для приложения до тех пор, пока форма не будет закрыта, поэтому по окончании работы с формами их необходимо закрывать.

Основы создания быстрых форм

Единственной серьезной проблемой, значительно влияющей на производительность при использовании форм, является количество и тип элементов управления. Каждый элемент управления поглощает память и ресурсы, одни — больше, другие — меньше. Образно говоря, связанный фрейм объекта "весит" примерно в 40 раз больше, чем линия. Элемент управления ActiveX может быть еще "увесистее" в зависимости от того, из чего он состоит и какие действия выполняет.

При выборе элементов управления для данной формы следует учитывать сведения, представленные в табл. 3. Необходимо помнить о том, что чем большей функциональностью обладает элемент управления, тем больше он поглощает ресурсов. В следующей таблице приведен список элементов управления, содержащихся в панели элементов конструктора форм. Подсчет имеющихся свойств каждого элемента управления и сравнение этого числа со сложностью задачи, решаемой каждым элементом управления, позволяет составить таблицу относительного "веса" элемента. Конечно же значения, приведенные в таблице, можно использовать лишь в качестве опорных. На самом деле использование ресурсов элементами форм зависит от их назначения.

Таблица 3. Относительный "вес" элементов форм.

Тип элемента управления

Относительный "вес"

Прямоугольник

1

Линия

1

Разрыв страницы

1

Вкладка (не включая собственные элементы управления)

4

Фрейм изображения (не включая само изображение)

6

Элемент управления вкладки

6

Подчиненная форма (как минимум)

6

Метка

8

Кнопка опции

8

Кнопка команды

8

Флажок

8

Группа опций (не включая собственные элементы управления)

8

Кнопка переключения

9

Текстовое поле

10

Список (как минимум)

10

Поле со списком (как минимум)

20

Элементы управления ActiveX

>= 20

Объектный фрейм (не включая само изображение)

30

Связанный объектный фрейм (не включая само изображение)

40

Некоторые элементы управления на несколько порядков "тяжелее", чем другие, поэтому для оптимизации полезно пользоваться подстановкой. Бывает много случаев, когда список может заменяться полем со списком или подчиненной формой. Изображения могут часто использовать элемент изображения вместо фрейма, а элемент вкладки может помочь разделить форму на быстро загружающиеся части, поскольку визуализируются только те элементы управления, которые находятся на текущей отображаемой странице. Учитывая, что в последнее время пользователи привыкли к гипертекстовым ссылкам в Internet, можно попробовать заменить командные кнопки гиперссылками. Если имеется возможность использовать элемент управления с меньшим "весом" без ущерба для функциональности, следует воспользоваться данной возможностью.

Ниже приводятся несколько советов, которые помогут повысить быстродействие элементов управления:

• Рекомендуется ограничивать количество полей, отображаемых в списках и свести к минимуму число полей со списками. Кроме того, необходимо индексировать поля поиска и отображаемые поля. Следует отключить опцию AutoExpand для полей со списками, установив ее в значение No. Если Access реагирует на каждый введенный символ, производительность значительно падает. Данную опцию следует использовать только тогда, когда избежать этого невозможно. Если необходимо использовать опцию AutoExpand, нужно убедиться, что поле, в котором пользователь вводит символы, является полем текстового типа. Access может использовать функцию AutoExpand только с текстовыми данными. Если данные не являются текстовыми. Access вынужден затратить некоторое время на преобразование данных и записей.

• При необходимости скрыть поле, связанное с полем со списком, следует избегать использования выражений в этом поле или в отображаемых полях. Поле со списком не отображает результаты так быстро, как будто они были подсчитаны заранее и считаны из таблицы.

• Следует избегать использования ограниченных запросов в качестве основы для полей со списками или списков. Можно попытаться взять за основу единственную таблицу. Если разработчику удастся создать таблицу с помощью запроса и воссоздавать ее по мере необходимости, производительность должна быть намного выше.

• При обновлении форм или любых элементов управления, которые отображают изменившиеся данные, рекомендуется применять метод Requery, работающий гораздо быстрее, чем макрос Rcquery Action. Данный метод можно использовать после выполнения удалений, особенно в многопользовательском приложении.

• Вместо использования одного большого и длинного поля со списком следует проверить, не сможет ли форма вместить несколько полей со списками, которые являются взаимно ограничивающими. Например, одно поле со списком служит для выбора регионов. Данный выбор ограничивает содержание второго поля со списком только городами в выбранном регионе. Такой подход гораздо быстрее и эффективнее, чем прокрутка одного поля со списком со всеми регионами и городами.

• Следует экономно обращаться не только с количеством и типами элементов управления, размещаемых в форме, но и с количеством полей, представленных в наборах записей форм. Хотя из таблицы формы всегда загружают данные быстрее, чем из запроса, здесь есть и обратная сторона. При открытии формы, основанной на таблице, загружаются все поля таблицы, даже если форма их не отображает. Бывают случаи, когда лучше открыть форму с помощью запроса, чтобы исключить ненужные поля из набора записей.

• Сортировку базового набора записей форм следует производить лишь в крайнем случае. Процесс пересортировки записей, по времени совпадающий с конфигурированием формы для отображения, может значительно замедлить открытие формы.

• При создании кода для формы (CBF) используйте ключевое слово Me. Эта константа всегда ссылается на активную форму и работает быстрее, чем другие ссылки.

• Рекомендуется индексировать поля Link Child и Link Master конструкции главная форма/подчиненная форма. Это позволит значительно ускорить выборку, которая часто производится данными формами.

• Если пользователь не собирается редактировать записи подчиненной формы, необходимо соответствующим образом установить свойства подчиненной формы. Свойства AllowEdits, AllowAppend и AllowDeletes оказывают значительное влияние на производительность, поскольку они реализуют функциональные свойства формы. Можно получить выигрыш в скорости, если избавиться от ненужных свойств. Точно так же можно получить выигрыш в скорости, если выбрать только нужные свойства. Если форма открывается для записи данных, необходимо установить ее свойства DataEntry в значение Yes, чтобы избежать ненужного считывания записи.

• Следует строго относиться к установке свойств форм и элементов управления во время выполнения. Данные свойства следует устанавливать только при необходимости и только в том случае, если это не вредит производительности.

• В каждом конкретном случае рекомендуется проверять, что работает быстрее — динамическое множество или простой снимок.

• Так же, как при сокрытии элементов управления с помощью разрывов страниц и вкладок, можно получить выигрыш в скорости за счет сокрытия форм. Неплохой практикой является невидимая загрузка нескольких из наиболее общих форм приложения. Такая возможность может появиться при запуске или при первом обращении к форме. Для невидимой загрузки формы ее необходимо открыть путем установки параметра WindowMode в значение acHidden:

DoCmd.OpenForm "имя_формы",,,,,acHidden

• Когда понадобится отобразить форму для пользователя, следует воспользоваться такой командой:

Forms("имя_формы").setfocus

• На тот случай, если пользователю снова понадобится форма, вместо того чтобы закрывать, ее сле-'дует скрыть. Метод Hide уберет форму с экрана, но при этом сохранит ее в памяти.

Formobject.Hide

• Избегая загрузки запросов, загрузки форм и этапов визуализации элементов управления, можно значительно повысить производительность интерфейса. Недостатки такого подхода заключаются в том, что скрытые формы требовательны к памяти и это может отрицательным образом сказаться на других модулях приложения. Хотя данную методику нельзя использовать для всех форм в приложении, ее рекомендуется применять для часто используемых форм.

Работая с очень большими наборами записей, не следует пытаться представить пользователю все записи за один раз. В любом случае большинство пользователей не знает, что делать с десятками тысяч записей, отображенными одновременно. Но если пользователи работают с многопользовательским приложением и форма открывает набор записей из больших таблиц или запросов, производительность падает по причине заторов в сети, ограничений кэша, блокировок записей и страниц и других перегрузок. Лучше всего отыскать логический способ, позволяющий разбить данные на логические подмножества с определенными ограничениями. Еще лучше отображать для пользователя только одну запись в определенный момент времени, запрашивая начальное значение записи или индекс поля. В небольших приложениях такой подход может привести к снижению производительности, но в больших многопользовательских системах это единственный выход. Для реализации такого решения необходимо переписать SQL-опсратор в коде, программно меняя свойство формы Record Source и многократно запрашивая форму.

Повышение скорости печати отчетов

Какой смысл в быстродействующей базе данных, если она целый день распечатывает отчет? Единственная большая и оказывающая влияние на производительность разница между формами и отчетами заключается в способе обработки разделов. В форме существует заголовок формы, область данных и примечание формы. В отчете имеются заголовок и примечание отчета, заголовок и примечание страницы, заголовки и примечания разделов, а также область данных. При открытии формы запрос, на котором она основана, выполняется только один раз. При открытии отчета он должен создать запрос (основываясь на запросе в источнике записей) для каждого раздела. Если запрос очень сложен, отчет должен выполнять его или ^некоторые его части несколько раз.

Ниже приводятся советы, следуя которым можно повысить скорость создания отчетов.

• Запрос, на котором основывается отчет, должен быть как можно более простым.

• Рекомендуется перенести вычисления в отчет. Если поместить вычисления в запрос, они будут выполняться для каждой строки. Однако, если поместить вычисления в отчет, они будут выполняться только при необходимости и пользователь сразу после расчета одной страницы данных Access увидит результат.

• Следует основывать запрос на возможно меньшем количестве таблиц. Поскольку отчет выполняет запрос больше чем один раз, может оказаться полезным создать таблицу необходимого результирующего набора. Отчет может обработать эту таблицу гораздо быстрее, чем снова выполнить запрос. Данный подход особенно полезен в том случае, если отчет основан на запросах с подчиненными запросами.

• Необходимо избегать использования подчиненных запросов в источнике отчета. Отчетам необходим большой объем памяти, а запрос с подчиненными запросами поглощает больше памяти, чем требуется в действительности.

• Следует проверить, необходим ли на самом деле подчиненный отчет. Подчиненные отчеты не только усложняют форматирование вывода, но и поглощают память, а также снижают производительность отчета. Тем не менее, подчиненные отчеты имеют широкое применение. Если существует несколько доменных функций, может оказаться, что подчиненный отчет выполняется быстрее, чем несколько вызовов данных функций.

• Необходимо избегать сортировки или группировки выражений. Чтобы правильно отображать сортировку или группировку. Access будет вынужден просчитывать каждое выражение больше чем один раз. Значения следует рассчитать до того, как они перейдут в отчет.

• Рекомендуется индексировать все поля, использующиеся для сортировки или группировки. Поскольку индексы по умолчанию сортируют записи, отчет без особых сложностей может отсортировать и сгруппировать данные непосредственно из индексированных полей.

• Источник записей не должен содержать агрегирующие доменные функции (DIookup). Снова это вынуждает отчет выполнять дополнительную обработку данных, что задерживает отображение отчета.

• Нет смысла в отображении для пользователей пустого отчета с записями #Еггог. Если отчет не содержит данных, следует отправить пользователю соответствующее сообщение и закрыть отчет. Определить, содержит ли отчет данные для отображения, можно с помощью свойств HasData или NoData.

Многие из методик, которые используются для повышения производительности форм, можно использовать и для отчетов. В данном разделе были описаны только те подходы, которые ограничиваются отчетами.

Создание высокопроизводительного кода

Что касается кода, существует несколько способов, обеспечивающих более быстрое выполнение функций и подпрограмм. Хотя разница в скорости между одной методикой и другой заключается лишь в долях секунды, использование самой быстрой методики может предоставить больше возможностей для последующего повышения производительности. Когда к разработчику обращаются пользователи с просьбой о модификации, всегда лучше иметь некоторый резервный запас функциональных свойств, которым можно воспользоваться для вставки аудиторских следов и сложных контрольных процедур, чтобы не испытывать терпение пользователей.

Разработчик обнаружит, что внедрение некоторых методик, описанных выше в данной статье, позволяет упростить оптимизацию кода. Самая большая помеха для кода — это плохо разработанная база данных. Если база данных разработана плохо, почти каждая функция и подпрограмма будет содержать ошибки и использовать обходные пути. Применение искусственных приемов всегда замедляет выполнение программы по сравнению со стандартной методикой.

Однако существует несколько простых правил, которых необходимо придерживаться, и несколько альтернативных методик, используя которые можно добиться максимальной скорости выполнения функций и подпрограмм приложения. Очень не многие из описанных подходов сами по себе оказывают более или менее значительный эффект, но их совместное и многократное применение приводит к поразительным результатам.

Использование памяти кодом

Access вызывает в память модули, все подпрограммы и функции, в них содержащиеся, используя метод загрузки дерева вызовов. Это означает, что, если функция А первого модуля вызывает функцию В первого модуля, а последняя, в свою очередь, — функцию С второго модуля, Access загружает в память целиком первый модуль и весь второй модуль. В памяти остаются оба модуля. Таким образом, имеет смысл

группировать модули в логические блоки. Если функции и модули часто вызывают друг друга, их следует записать в один модуль для уменьшения непроизводительных издержек и количества загрузок модулей. Необходимо также удалить все функции и процедуры, не используемые приложением.

VBA, кроме того, загружает модуль, если существует ссылка на глобальные переменные данного модуля. Разработчику следует убедиться, что глобальные переменные разумно сгруппированы в модулях.

Работа с модулями

Перед выполнением модулей VBA должен скомпилировать их. Это не значит, что модули не будут работать, если их специально не скомпилировать, просто перед выполнением модуля VBA будет вынужден временно скомпилировать его. Кроме того, модуль будет компилироваться каждый раз, когда его необходимо выполнить. Это может сильно сказаться на производительности.

При компиляции модуля VBA преобразовывает его в гораздо более меньший по размеру, быстрее выполняющийся блок. Хотя исходный код всегда хранится в файле .MDB, Access загружает и выполняет только скомпилированный код VBA. Код VBA, кроме того, не содержит пробелов, комментариев, заголовков и занимает гораздо меньший объем памяти, чем созданный разработчиком исходный код. При попытке выполнить нескомпилированную процедуру VBA должен загрузить весь исходный код (с пробелами, комментариями и невыполняемым кодом) в память и скомпилировать перед выполнением. То же самое происходит при выполнении кода форм и отчетов.

Компиляция кода

Можно скомпилировать код, выбрав в меню пункты Debug j Compile projectname (Отладка | Скомпилировать имя_проекта). Перед распространением приложения необходимо обязательно скомпилировать его.

Декомпиляция

При редактировании модуль декомпилируется. Отчет или форма декомпилируются при внесении любых изменений, даже если они не задевают код. Создание новой формы или отчета может также декомпилировать код. Во время разработки для компиляции по необходимости можно полагаться на команду Compile on Demand (Компиляция по требованию) во вкладке General меню Tools [ Options. При этом VBA компилирует модули во время выполнения. Еще одна новая опция Access 2000 — фоновая компиляция (Background Compile). При компиляции приложения в фоновом режиме VBA освобождает разработчика от дополнительных затрат времени.

Если выполнить проверку, то оказывается, что скомпилированное приложение занимает больше дискового пространства, чем нескомпилированное. Причина заключается в том, что в скомпилированном приложении хранится как скомпилированный код, так и исходный. Никакого ущерба для производительности здесь нет. Поскольку в память загружается только скомпилированный код, который выполняется быстрее исходного, производительность значительно повышается.

Поскольку Access постоянно загружает модули, необходимо внимательно рассмотреть все модули в данной разработке. Следует убедиться, что в приложении не осталось неиспользуемых функций или процедур. Необходимо удалить весь неиспользуемый код, созданный во время разработки. Если этого не сделать, компилятор будет вынужден обрабатывать невыполняемый код, а это создаст дополнительные задержки. Кроме того, во время разработки рекомендуется время от времени закрывать окно проекта, чтобы очистить память. Даже если приложение полностью скомпилировано, должен загружаться как можно меньший объем кода.

Составление файла .MDE

Самый надежный способ удостовериться в том, что приложение остается в скомпилированном состоянии для пользователя — это создать файл .MDE для распространения. MDE-файл не содержит исходного кода и не декомпилируется.

Кроме методик группировки, управления памятью и компиляции на уровне модуля, существует несколько специфических подходов к написанию кода, которые могут помочь создавать более быстродействующие приложения.

Использование Option Explicit

Рекомендуется всегда использовать Option Explicit. Данная опция требует объявления всех переменных. Если не объявить переменную, VBA вынужден использовать самый большой и самый гибкий тип данных

для использования данной переменной. Обычно этот тип является также и самым медленным. Из соображений читабельности и целостности данных рекомендуется всегда объявлять переменную.

Выбор размеров переменных

При объявлении переменной следует использовать наименьший размер переменных из возможных. Не нужно применять двойное слово, когда достаточно применить целый тип. По возможности необходимо использовать строки фиксированной длины вместо строк переменной длины.

Сохранение стекового пространства с помощью строковых переменных

Строковая переменная — один из наиболее часто употребляемых в коде типов данных. Их можно разделить на три вида:

• Локальные фиксированной длины (не более 64 символов) — эти переменные используют два байта на символ и не используют область динамической памяти.

• Локальные фиксированной длины (более 65 символов) — эти строки также используют два байта на символ, но в динамической памяти. Кроме того, им нужны четыре байта в стеке для указания на переменную в динамической структуре.

• Локальные переменной длины (длина не имеет значения) — объем динамической памяти зависит от длины строки. Для указания на переменную в динамической структуре используется четыре байта стека.

При работе со строками необходимо стремиться к уменьшению объема используемого стека. Можно попытаться изменить строки на локальные строки переменной длины или на статические строки фиксированной длины. Ниже приведен пример строки переменной длины, объявленной как статическая строка фиксированной длины для сохранения стековой памяти.

Dim strString as string

Static strString as string * 30

СОВЕТ

Для определения размера текстовых полей фиксированной ширины можно пользоваться размерами полей таблиц.

Объявление типа объекта

Типы объектов необходимо объявлять очень точно. Если код работает с элементами управления текстовых полей формы, объектную переменную следует объявить как текстовое поле, а не просто как элемент управления. В таком случае VBA не понадобится решать, к какому типу элементов управления происходит обращение. Для уменьшения времени выполнения вместо

Sub CycleControls(cnti as control)

необходимо использовать

Sub CycleContrals(cnti as TextBox)

Использование поточного кода вместо вызова других функций

Классическая дилемма между эксплуатационной надежностью и скоростью возникает, когда функция или подпрограмма вызывает другую функцию или подпрограмму. Такой метод очень надежен, но гораздо менее эффективен, чем решение задачи в одной процедуре. Иногда нужно выбирать между скоростью и надежностью, но не следует этого делать ради нескольких миллисекунд.

Переключение True и False

Установку флага в положение "истинно" (true) или "ложно" (false) не обязательно проверять с помощью условия If...Then...Else. Можно сохранить время и уменьшить объем кода, изменив значение на обратное с помощью оператора NOT. Установив булеву переменную в значение, которое не присвоено данной переменной, получим обратное значение. Ниже приводится соответствующий пример кода:

Вместо этого фрагмента:

If bFlag = False then

bFlag=True

Else

BFlag=False

EndIF

можно использовать строку:

bFlag=Not bFlag

Выполнение одной строки кода занимает намного меньше времени, чем выполнение нескольких строк с оценкой.

Использование Len() вместо пустой строки

Чтобы проверить строковую переменную на наличие символов, следует использовать функцию Len() вместо того, чтобы выполнять сравнение с пустой строкой (""). Функция Len() оценивается быстрее, чем сравнение строки символов со строкой нулевой длины.

Sub CheckString(strString as string)

If Len(strString) then

MsgBox "Hare is the string: " & strString

Endlf

End Sub

Использование True и False вместо нуля

Поскольку True и False являются двоичными величинами, их оценивать проще, чем число 0. True и False можно использовать так:

Function ShowNumber(dblNumber as Double) as string

If dblNumber then

ShowNumber ="The number is " & dblNumber

Endlf

End Function

Быстрые ссылки на объекты

Для многократных ссылок на объекты следует использовать переменные. Гораздо быстрее обратиться к существующей переменной формы, элемента управления, отчета или запроса, чем снова обращаться к объекту. Вместо повторной ссылки на форму

Forms![frnMyForm].Height=500 Forms![frnMyForm].Width=500

можно объявить переменную и обратиться к ней следующим способом:

Dim frm as Form

Set fnn=Fonns ! [ frmMyFonn]

fnn.Height=500 frm.Width=500

При работе с большим количеством свойств объекта можно уменьшить число ссылок с помощью конструкции With...End With. Такой способ особенно полезен, когда путь, указанный в ссылке, достаточно длинный.

With Forms! [ frmMainForm] ! [txtCu stonier Name]

.left=200

.top=300

.height=200

.width=100

End With

Активную форму представляет ME, поэтому нет необходимости объявлять переменную или ссылаться на объект. Поскольку такая методика работает только в CBF, ее нельзя использовать в общих стандартных модулях.

With ME

.Height=500

.Width=500

End With

Использование быстродействующих массивов

Рекомендуется пользоваться массивами. Они хранятся в памяти и выполняются исключительно быстро. Расширение массивов занимает бесконечно малое количество времени, однако они используют только тот объем памяти, который им нужен в данный момент. Если неизвестно, сколько элементов необходимо поместить в массив, можно создать динамическое множество, а не один большой статический массив с массой пустого места.

Если необходимо очистить массив, но не разрушать при этом его структуру, можно использовать ключевое слово Erase. Это позволяет очистить массив определенного размера, не перестраивая его.

Erase MyArray

Наоборот, если нужно расширить массив, не разрушая содержащихся в нем данных, следует использовать Redim вместе с Preserve.

ReDim Preserve myArray(Ubound(myArray+l))

Массивы часто могут управлять данными из набора записей. Вместо постоянно открытого набора записей DAO с излишними затратами ресурсов можно применить метод GetRowsQ и закрыть набор записей. Это позволит освободить память и облегчить решение вопросов, связанных с многопользовательской средой. GetRowsQ использует один параметр: количество строк для загрузки в массив.

Dim db as Database

Dim rowArray as Variant

Dim rs as Recordset

Set db=CurrentDB()

Set rs=db.openrecordset("Quarterly Order»")

RowArray=rs.GetRows(rs.RecordCount)

rs.close

После загрузки данных в массив им можно манипулировать любым способом или поместить его в несвязанную форму.

Использование констант

Чтобы VBA получил текущее значение переменной, при каждом обращении оно должно быть найдено. Для констант определять значение не нужно. Кроме того, константы улучшают читабельность кода. Вместо ввода числа 12 для представления 12 месяцев года можно создать константу (возможно, с именем AllMonths) и установить ее равной 12. VBA считывает константу гораздо быстрее, а другие разработчики будут знать, о чем идет речь, когда увидят данную константу. Недостаток использования констант заключается в том, что их значение можно установить только один раз и нельзя изменять. Константы можно использовать только для представления значений, внутренних для данного приложения, либо для представления значений, которые не меняются в мире. Коротко говоря, константы можно использовать только для внутренних или неизменных значений.

Правильное использование закладок

Для возврата к предыдущей записи рекомендуется пользоваться закладками. Закладки представляют собой исключительно быстрый способ передвижения по записям в интерфейсе. Следует помнить, что существует два различных вида закладок: один — для форм, а другой — для наборов записей. Закладка формы представляет собой массив переменных, динамически присваиваемых каждой записи в базовой копии набора записей. Закладка DAO — это байтовый массив, идентифицирующий каждую запись в наборе записей. Закладки являются удобными средствами навигации, они разрушаются и воссоздаются вместе с наборами записей и копиями. Не следует на них полагаться, за исключением полностью контролируемых или простых ситуаций. Закладки не представляют собой записи и не имеют никакого отношения к начальному значению. Они представляют только временное положение записи в наборе строк. Любая манипуляция с данными должна проводиться с учетом методик разработки реляционной базы данных, а не с учетом текущего положения в наборе записей или копии. Поскольку разработчик должен всегда пользоваться методом повторного запроса после обновлений и удалений, он каждый раз разрушает и воссоздаст закладки. Если интерфейс использует закладки в таких обстоятельствах, необходимо тщательно отслеживать удаления и обновления.

Приведенный ниже код может использовать закладку формы для возврата к предыдущей записи после обновления:

Private Sub Findit_AfterUpdate()

Dim redone As Recordset

Dim recordID As Variant

Dim IDValue As Long

Set rsclone = Me.RecordsetClone

IDValue = Me![Findit]

recordID = "ID = " & IDValue

rsclone.FindFirst recorded

cm = rsclone.Bookmark

Me.Bookmark = bm

End Sub

Использование закладки для возврата к предыдущей записи позволяет ускорить выполнение на 1300% по сравнению с использованием FindFirst для решения той же задачи.

Закрытие и разрушение избыточных данных

Быстрый код — это чистый код. Необходимо убедиться, что по окончании работы с наборами записей они будут закрыты, а объектам, когда они будут не нужны, присвоены пустые значения. Ненужные объекты поглощают значительный объем памяти, который мог бы быть использован другими частями приложения.

rs.Close

Set db=Nothing

Использование SQL вместо DAO

Работать с наборами записей рекомендуется только в том случае, если нет другой альтернативы. Механизм баз данных Jet для манипуляций над данными и структурами данных оптимизирован на использование SQL. При каждой возможности вместо DAO следует использовать запросы SQL. Очень редко DAO работает быстрее, чем правильно составленный запрос. В отличие от DAO, запросы имеют план выполнения и используют преимущества индексации.

Если необходимо адресовать данные через объектную модель, рекомендуется вместо DAO использовать ADO. ADO — новый стандарт для манипуляций с адресацией данных и определением данных через объектную модель. Разработка дальнейших версий DAO прекращена, и никаких улучшений или расширений в будущем не последует.

Использование номеров индексов коллекций

При работе с коллекциями рекомендуется по возможности пользоваться их индексами. Номера индекса представляют собой внутреннюю идентификацию коллекции. Их использование приводит к гораздо более быстрому выполнению по сравнению с использованием любого другого свойства (например, имени) объекта коллекции. Приведенный ниже пример иллюстрирует два разных способа обращения к текущей базе данных. Ссылка на Currentdb() автоматически обновляет коллекции базы данных, что занимает определенное время. Первая ссылка (dbEngine(0)(0)) не обновляет коллекцию.

Set db=DBEngine(0)(0)

работает быстрее, чем

Set db=Currentdb()

Выражение

Set cntl=Forms!frmMyForm(O)

работает быстрее, чем

Set cntl=Forms![frmMyForm]![myControl]

Использование номера индекса элемента коллекции особенно полезно в циклах.

Создание быстрых циклов

При работе с циклами по коллекции рекомендуется использовать For...Each вместо For...Next. При работе с циклами по элементам управления на форме код

For Each cnti on fcm

Next

выполняется быстрее, чем простой цикл For...Each.

Если необходимо создать цикл по коллекции объектов, следует избегать ненужного обновления коллекции. Даже в маленькой базе данных обновление коллекции может значительно снизить быстродействие приложения.

При использовании цикла For—Next можно сэкономить время, не повторяя переменную в строке Next.

For i=l to 100

.... .необходимый код

Next

Преимущества такого подхода особенно заметны при использовании вложенных циклов. Кроме того, не следует пересчитывать предельное значение для строки For. Верхнее предельное значение должно быть установлено перед входом в цикл.

reccount=rs.recordcount/2

For i=l to reccount

Next

Если не установить заранее значение верхнего предела, цикл пересчитывает значение при каждом проходе, а это — потеря времени.

Исключение IIF() из кода

Не рекомендуется использовать в коде функцию IIF(). Данная функция перед выдачей результата должна оценить вес выражения, содержащиеся в ней. Стандартная конструкция If...Then...Else работает быстрее.

Построение конструкции Select Case

При использовании конструкции Select Case наиболее часто встречающиеся случаи следует разместить в верхней части структуры. Поскольку проверяется каждый случай по порядку, такая компоновка позволяет обеспечить максимально быстрый выход из структуры.

Использование .Execute вместо RunSQL

По возможности следует избегать кодирования в стиле DoCmd. DoCmd представляет собой высший уровень кода VBA, — потомок макросов. В тех случаях, когда имеется альтернатива, ею следует обязательно воспользоваться.

Выражение

DoCmd RunSQL

работает медленнее, чем

Querydef.Execute

Использование A2KU_Timer

При настройке приложений рекомендуется пользоваться таймером A2KU_Timer, описанным в данной главе. Он работает примерно на 1000% быстрее, чем аналогичные конструкции.

Проверка эффективности транзакций

Транзакции не всегда экономят время. В одном случае можно рассчитывать на то, что транзакция увеличит скорость выполнения запросов. В другом случае это уже будет не так. Хотя транзакции, уменьшая количество обращений к диску, используют кэширование для повышения производительности запросов, они могут также замедлить их, создавая временные файлы данных на диске в ожидании возврата к пройденной точке. Транзакции по умолчанию неявны, поэтому узнать, повышают ли они производительность, можно только с помощью тестирования на реалистичном наборе данных в каждом отдельном случае.

Контроль обновлений

Необходимо отключить Application.Echo или контролировать обновление экрана. Обновление экрана занимает время, и это может замедлить выполнение приложения.

Использование ActiveX и метода раннего связывания

Следует воспользоваться преимуществами раннего связывания. При использовании элементов управления ActiveX необходимо убедиться, что существует ссылка на базовый OCX-файл для данного элемента. Для осуществления проверки выполните команду Tools | References (Сервис | Ссылки). Раннее связывание может значительно повысить производительность.

Преобразования в архитектуре клиент/сервер

При разработке приложения следует учитывать возможность преобразований в SQL-Server или Oracle. При этом можно применить запросы к серверу и использовать хранимые процедуры на сервере. Данный подход позволяет значительно повысить производительность.

Хлеба и зрелищ...

Если добиться большой скорости выполнения трудно, следует хотя бы придать вашему программному продукту вид. Если процесс выполняется слишком долго, так что это надоедает пользователю, необходимо создать какую-либо разновидность экрана статуса, позволяющего осветить ход процесса и дающего знать, что приложение не "зацикливается". Пользователи часто думают, что приложение работает быстро только потому, что видят индикатор хода процесса.

Резюме

В данной главе описаны методики оптимизации приложения. Время реакции хорошо работающего приложения обычно составляет менее одной секунды для большинства операций получения данных. Методики, изложенные в данной главе, помогут разработчику придерживаться такого стандарта.

Простейший способ обеспечения быстрой работы приложения заключается в модернизации компьютера, на котором оно выполняется. Кроме того, можно изменить установки в системном реестре, чтобы они соответствовали требованиям выполняющегося процесса. В этой главе подробно описано составление таблиц и запросов, обеспечивающее приложению основу для достижения высокой производительности. Рассмотрены подходы к дизайну интерфейса, помогающие создать надлежащий вид приложения и позволяющие уведомить пользователей о ходе процесса. Наконец, изложены способы кодирования, обеспечивающие использование каждой миллисекунды для повышения быстродействия приложения.

Начав с аппаратного обеспечения, пробуя различные описанные подходы и проверяя их в той или иной ситуации, разработчик сможет добиться самой высокой производительности приложения. Совокупное применение приведенных методик может иметь поразительный эффект в каждом конкретном случае.