Н. 3. Елманова, C++Builder 3.0, архитектура "клиент/сервер" и Internet
вателем (UDF - User Defined Functions), а некоторые не поддерживают. Поэтому при смене платформы хранимые процедуры скорее всего потребуется переписывать. Отметим также, что чаще всего серверные хранимые процедуры создаются путем ручного кодирования и для их создания, как правило, не существует удобных визуальных средств разработки и отладки наподобие имеющихся в C++Builder. Поэтому при принятии решения о создании тех или иных хранимых процедур не мешает оценить возможные трудозатраты - иногда может оказаться, что они не стоят ожидаемого эффекта. Если же хранимые процедуры применяются активно, еще большего повышения производительности при их использовании можно достичь, минимизируя число и объем передаваемых на сервер параметров. Очевидно, что передать на сервер целое число намного проще, чем переслать длинную символьную строку; поэтому при планировании хранимых процедур с подобными параметрами есть смысл подумать о перепроектировании базы данных и создании, например, таблиц-справочников либо, при небольших объемах таких таблиц, о хранении'их на рабочей станции или организации соответствующих массивов.	-—
7.3.2   Использование  предварительной подготовки запросов
При использовании компонентов TQuery нередко бывает полезно употребить метод PrepareQ, особенно если компонент TQuery содержит параметризованный запрос. Метод Ргераге() осуществляет пересылку запроса на сервер, где он оптимизируется и компилируется, а при открытии запроса на сервер в этом случае посылаются только его параметры. Особенно заметным повышение производительности может оказаться тогда, когда параметризованные запросы с различными значениями параметров повторяются часто - в этом случае повторная подготовка запроса не потребуется. Если же метод Ргераге() не вызывается явно, он будет автоматически вызываться неявно каждый раз при пересылке параметров, инициируя пересылку всего текста запроса на сервер.
Что касается передаваемых на сервер параметров запроса, их число и объем рекомендуется минимизировать точно так же, как и в случае параметров хранимых процедур.
7.3.3   Использование  представлений (View) и параметризованных запросов
Нередко начинающие программисты используют динамическое создание запросов на этапе выполнения, изменяя содержимое строкового массива, содержащегося в свойстве SQL компонента TQuery (например, периодически модифицируя предложение WHERE). При часто повторяющихся за-
152
Оптимизация приложений C++Builder в архитектуре "клиент/серее"
заставит сервер при выполнении внутреннего подзапроса перебрать все строки таблицы, проверяя соответствие каждой записи указанному условию, тогда как запрос вида
SELECT * FROM <имя таблицы> WHERE EXISTS (SELECT * FROM <имя таблицы> WHERE <условие>)
заставит сервер перебирать записи до нахождения первой записи, удовлетворяющей указанному условию. Лишний перебор записей на сервере, естественно, занимает некоторое время - чудес не бывает.
Многие приемы оптимизации связаны с использованием индексов. Если какое-либо поле таблицы часто употребляется в предложении WHERE, сравнивающем его значение с какой-либо константой или параметром, наличие индекса для этого поля ускоряет подобные операции. По этой же О причине рекомендуется индексировать внешние ключи у таблиц с большим   j числом записей. Однако следует иметь в виду, что поддержка индексов замедляет операции вставки записей, поэтому при проектировании данных следует взвесить все "за" и "против" создания индексов, а еще лучше провести соответствующее тестирование, заполнив таблицы случайными данными (для этой цели можно написать соответствующее приложение, а еще лучше воспользоваться готовыми средствами тестирования типа SQA Suite). Говоря о применении индексов, следует также обратить внимание на то, что при использовании индексированных полей в качестве аргументов функций наличие индекса не влияет на скорость выполнения запроса - индекс в этом случае не используется, так как нет никакой гарантии, что данная функция будет монотонной.
Особо следует отметить проблемы, связанные с использованием вложенных запросов. Дело в том, что скорость выполнения запроса существенно зависит от числа уровней вложенности подзапросов (время выполнения примерно пропорционально произведению числа записей в таблицах, используемых в подзапросах). Фактически проверка соответствия условию WHERE каждой записи из внешнего подзапроса инициирует выполнение внутреннего подзапроса, что особенно заметно сказывается при большом числе записей. В практике одного из авторов был случай, когда при приведении в порядок одной из используемых корпоративных информационных систем после выполнения нескольких обычных запросов на обновление данных в таблице с несколькими десятками тысяч записей, выполнявшихся в течение нескольких секунд, был инициирован вложенный запрос на обновление данных к этой же таблице. Этот запрос выполнялся более двух часов (чего, вообще говоря, и следовало ожидать). Поэтому использовать вложенные^ запросы следует тодыйсив., тех случаях, когда без них нельзя обойтись. Альтернативой применения вложенных запросов может служить
153