Контроль запитів за допомогою SQL Monitor., C / C + +, Програмування, статті

SQL Monitor використовується для контролю запитів, пересилаються клієнтським
додатком серверу баз даних за допомогою BDE, і їх результатів, а також
вимірювання часу між ними. Для його запуску слід вибрати пункт SQL Monitor
з меню Database C + + Builder. Головне вікно SQL Monitor складається з двох частин. В
верхній частині відображаються послідовно генеруються SQL-пропозиції і
відомості про відгуки сервера, а також порядковий номер і час їх настання, а
у нижній частині – повний текст SQL-запиту. Список, який відображається у верхньому вікні,
можна зберегти у файлі для подальшого аналізу. На рис.1 представлений типовий
висновок відомостей при роботі програми, розглянутого в попередній статті даного
циклу.



Рис.1. Висновок відомостей про виконання сервером запитів в SQL
Monitor

При використанні SQL Monitor можливий вибір типів відображуваних відомостей. Їх
можна вибрати в діалозі Trace Options, що викликається з меню Options.



Рис.2. Діалог Trace Options для вибору дій,
відображаються в SQL Monitor

SQL Monitor дозволяє відображати відомості про такі дії:


Використання SQL Monitor є найпростішим (хоча і не єдиним)
засобом тестування продуктивності інформаційних систем в архітектурі
клієнт / сервер, і ефективність застосування більшості розглянутих нижче
прийомів їх оптимізації можна проконтролювати за його допомогою.

Мінімізація звернень до сервера та мережі


Мінімізація зв’язків з сервером впливає на продуктивність всіх складових
частин інформаційної системи – клієнта, сервера та мережі. Зайві зв’язку з сервером
призводять до створення додаткових об’єктів (таких, як TDatabase) в клієнтському
додатку, генерації додаткових запитів до сервера для з’ясування прав
користувача на доступ до тих чи інших об’єктів бази даних, а також до
непродуктивної використання ресурсів сервера. Для мінімізації зв’язків з
сервером можна використовувати такі прийоми, як використання в явному вигляді
компонента TDatabase замість неявного їх створення, використання кешування
даних і структури, зберігання даних про метаданих у клієнтському додатку,
використання локальних фільтрів та ін

Використання компонента TDatabase


При використанні декількох компонентів TDataSet слід мати на увазі, що
кожен з них прагне під час виконання створити неявно свій об’єкт TDatabase
для зв’язку з сервером. Якщо ж помістити компонент TDatabase на форму або в
модуль даних на етапі проектування додатку, і пов’язати з ним всі компоненти
TDataSet, вказавши його ім’я в якості значення властивості DatabaseName цих
компонентів, всі вони будуть використовувати одну загальну зв’язок, забезпечену цим
компонентом.

Використання параметра SQLPASSTHRU MODE


Ще один спосіб мінімізації зв’язків з сервером полягає у зміні
значення параметра SQLPASSTHRU MODE компонента TDatabase (або псевдоніма,
створеного утилітою конфігурації BDE). Цей параметр визначає, чи можуть
використовуватися загальні з’єднання з базою даних запитами, згенерували
додатком (наприклад, за допомогою компонента TQuery), і запитами,
згенерували самою бібліотекою BDE (наприклад, при реалізації навігаційних
методів компонента TTable). Значенням цього параметра за умовчанням є NOT
SHARED, що дозволяє уникнути можливих конфліктів при многопользовательском
оновленні даних, але створює окремі з’єднання з базою даних для обох
типів запитів.

Найбільш ефективним з точки зору мінімізації з’єднань з базою даних
значенням цього параметра в більшості випадків є значення SHARED
AUTOCOMMIT. При використанні цього значення зміни кожного запису в таблицях
негайно фіксуються сервером незалежно від типу викликав їх запиту, але при
цьому обидва типи запитів можуть використовувати один і той же з’єднання з базою
даних. Цей режим найбільш близький до режиму, в якому використовуються мережеві
версії настільних СУБД. Однак оскільки сервер в цьому випадку повинен негайно
фіксувати результати зміни записів, він ініціює і завершує окрему
транзакцію при зміні кожного запису, що може призвести до перевантаження сервера
та мережі і до зниження продуктивності замість очікуваного її підвищення. Тому
ефективність використання такого режиму повинна бути обов’язково перевірена
шляхом тестування.

Третє можливе значення цього параметра – SHARED NOAUTOCOMMIT. У цьому
випадку обидва типи запитів можуть також використовувати один і той же з’єднання з
базою даних, причому без завершення транзакцій після редагування кожної
запису. Однак у цьому випадку контроль за завершенням транзакцій слід
здійснювати в клієнтському додатку. Подібний режим може бути вельми
ефективний, так як перенавантажують сервер транзакції автоматично не
ініціюються після редагування кожного запису, але при його використанні можуть
виникати конфлікти і непередбачувані зміни даних при спробі
одночасного редагування однієї і тієї ж записи різними користувачами.
Тому даний режим слід використовувати тільки в тому випадку, якщо ймовірність
подібних колізій мала.

Кешування метаданих на робочій станції


Ще один спосіб мінімізації зв’язків з сервером полягає у використанні
кешування структури таблиць на робочій станції. У цьому випадку знижується число
звернень до сервера з метою визначення метаданих, тобто кількості стовпців у
використовуваних в додатку таблицях, їхніх імен і типів даних. Для цієї мети
використовуються наступні параметри псевдоніма бази даних (або компонента
TDatabase):
ENABLE SCHEMA CACHE – чи дозволено кешування метаданих;

SCHEMA CACHE SIZE – кількість таблиць, структура яких кешується;

SCHEMA CACHE TIME – час зберігання інформації в кеші в секундах; значення -1
відповідає часу зберігання даних в кеші до закриття програми;
SCHEMA
CACHE DIR – каталог для кешування метаданих.

Застосування кешування метаданих може істотно підвищити
продуктивність клієнтських додатків і знизити навантаження на мережу. Однак
застосовувати його можна тільки в тому випадку, якщо структура таблиць не змінюється в
Протягом роботи програми. Якщо ж у процесі роботи програми проводиться
додавання або видалення стовпців, створення або видалення індексів (не обов’язково
цим же додатком), створення і видалення тимчасових таблиць, інформація в кеші
може виявитися не відповідає дійсності, що може призвести до
помилок, пов’язаних з недопустимими типами даних, неприпустимими перетвореннями
типів та ін У цьому випадку застосовувати кешування метаданих не рекомендується.

Використання нащадків TField в клієнтському додатку


Іншим способом зберігання на робочій станції додатку відомостей про метадані
є використання компонентів – нащадків TField. Так як відповідні
об’єкти зберігають відомості про структуру таблиць безпосередньо у додатку, на
етапі виконання не проводиться звернень на сервер з метою отримання
метаданих. Використання нащадків TField краще, ніж використання
методів FieldByName () або властивості Fields, так як останні використовують
звернення до сервера для отримання відомостей про типи полів. Обмеження на
застосування компонентів – нащадків TField такі ж, як і в попередньому випадку –
їх використання рекомендується при стабільній структурі таблиць. Крім цього,
зміна структури даних на сервері може зажадати модифікації програми
і, як наслідок, установку його нової версії на робочі станції.

Кешування даних на робочій станції


Крім кешування метаданих нерідко застосовується і кешування на робочій
станції самих даних. Для цієї мети слід встановити рівним true значення
властивості CachedUpdates відповідного компонента TDataSet. У цьому випадку всі
внесені користувачем зміни зберігаються в локальному кеші. Збереження
даних на сервері проводиться за допомогою методу ApplyUpdates () компонента
TDataSet, а метод CommitUpdates () очищає кеш. У цілому такий метод знижує
мережевий трафік і сумарне число з’єднань з сервером, тому що, по-перше, при
редагуванні даних в кеші не потрібно наявності з’єднання з сервером, а
по-друге, збереження декількох записів з кешу на сервері може бути
здійснено шляхом виконання однієї-єдиної транзакції. Крім цього,
знижується сумарне число блокувань записів на сервері, так як в процесі
редагування даних в кеші необхідності в блокування немає.

Використання локальних фільтрів при невеликих обсягах даних


Якщо компонент TDataSet доставляє на робочу станцію невеликий за обсягом
набір даних, який можна порівняти з розміром кеша робочої станції (визначеного
параметрами MINBUFSIZE і MAXBUFSIZE системних налаштувань BDE), він буде повністю
кешуватися на робочій станції. У цьому випадку застосування локальних фільтрів
більш переважно, ніж використання запитів з пропозицією WHERE,
направляються на сервер, оскільки в першому випадку не потрібно звернення до
серверу.

Оптимізація використання сервера


Використання збережених процедур


При виконанні багаторазово повторюваних дій, які використовують дані з
сервера (наприклад, при статистичній обробці містяться в таблицях даних)
продуктивність інформаційної системи можна підвищити, використовуючи збережені
процедури сервера замість SQL-запитів, що генеруються клієнтським додатком. Справа
в тому, що переданий з клієнтського застосування SQL-запит сервером
оптимізується, компілюється і лише потім виконується, а збережені процедури
сервера вже містяться в оптимізованому і скомпільованому вигляді, тому
обробка даних з їх використанням вимагає менших витрат часу, особливо
при невеликому числі і сумарному обсязі переданих параметрів процедури.

Проте слід мати на увазі, що збережені процедури пишуться на процедурному
розширенні SQL використовуваного сервера. Існують офіційні стандарти
непроцедурного мови SQL ANSI / ISO SQL-86, SQL-89 і SQL-92, але на сьогоднішній
день не існує стандартів на процедурні розширення цієї мови. Кожна
серверна СУБД має свій набір процедурних розширень, що відрізняється від
відповідних розширень інших СУБД. Деякі сервера, наприклад Borland IB
Database, підтримують створення і використання в процедурах функцій,
визначених користувачем (UDF – User Defined Functions), а деякі не
підтримують. Тому при зміні платформи збережені процедури, швидше за все,
потрібно переписувати. Відзначимо також, що найчастіше серверні збережені
процедури створюються шляхом ручного кодування, і для їх створення, як правило,
не існує зручних візуальних засобів розробки й налагодження зразок
наявних в C + + Builder. Тому при прийнятті рішення про створення тих чи інших
збережених процедур не заважає оцінити можливі трудовитрати – іноді може
виявитися, що вони не стоять очікуваного ефекту.

Якщо ж процедури, що застосовуються активно, ще більшого підвищення
продуктивності при їх використанні можна досягти, мінімізуючи число і обсяг
переданих на сервер параметрів. Очевидно, що передати на сервер ціле число
набагато простіше, ніж переслати довгу символьну рядок, тому при плануванні
збережених процедур з подібними параметрами є сенс подумати про
перепроектуванні бази даних і створення, наприклад, таблиць-довідників або,
при невеликих обсягах таких таблиць, про зберігання їх на робочій станції або
організації відповідних масивів.

Використання попередньої підготовки запитів


При використанні компонентів TQuery нерідко буває корисно використовувати
метод Prepare (), особливо якщо компонент TQuery містить параметризований
запит. Метод Prepare () здійснює пересилання запиту на сервер, де він
оптимізується і компілюється, а при відкритті запиту на сервер в цьому випадку
надсилаються тільки його параметри. Особливо помітним підвищення продуктивності
може виявитися тоді, коли параметризрвані запити з різними значеннями
параметрів повторюються часто – в цьому випадку повторна підготовка запиту не
буде потрібно. Якщо ж метод Prepare () не викликається явно, він буде автоматично
викликатися неявно кожного разу при пересиланні параметрів, ініціюючи пересилання всього
тексту запиту на сервер.

Що каcается переданих на сервер параметрів запиту, їхня кількість і обсяг
рекомендується мінімізувати точно так само, як і у випадку параметрів збережених
процедур.

Використання уявлень (View) і параметризованих запитів.


Нерідко починаючі програмісти використовують динамічне створення запитів на
етапі виконання, змінюючи вміст строкового масиву, що міститься в
властивості SQL компонента TQuery (наприклад, періодично модифікуючи пропозицію
WHERE). При часто повторюваних запитах такого типу це не найоптимальніший
спосіб пересилання запитів на сервер, оскільки в цьому випадку обов’язково
здійснюється попередня підготовка запитів, що полягає у пересиланні
всього тексту на сервер, а також оптимізації та компіляції його сервером. Більш
кращим у цьому випадку є використання параметризованих запитів
і методу Prepare (), або використання уявлень (View) сервера,
представляють собою не що інше як зберігається на сервері заздалегідь
скомпільований запит. В останньому випадку можна уникнути не тільки зайвих
повторних компіляцій запиту сервером, але і зайвої перевантаження клієнта
генерацією запитів.

Використання властивості UpdateMode


Властивість UpdateMode компонентів TDBDataSet визначає склад оператора WHERE,
генерується BDE при оновленні даних. Розглянемо, яким вийде оператор
WHERE при редагуванні поля SYMBOL що міститься на сервері Oracle Workgroup
Server копії таблиці HOLDINGS з вхідної в комплект поставки C + + Builder бази
даних BCDEMOS при різних значеннях цієї властивості. Згенеровані
SQL-пропозиції можна поспостерігати за допомогою SQL Monitor.

За замовчуванням значенням властивості UpdateMode є UpWhereAll, і в цьому
випадку BDE генерує пропозицію WHERE, що містить всі поля таблиці. При цьому
згенерований оператор SQL, якщо тільки він не перевизначений за допомогою
компонента TUpdateSQL, буде виглядати наступним чином:

UPDATE “HOLDINGS” SET “SYMBOL”=:1 WHERE “ACCT_NBR”=:2 AND “SYMBOL”=:3 AND
“SHARES”=:4 AND “PUR_PRICE”=:5 AND “PUR_DATE”=:6 AND “ROWID”=:7.

Цей спосіб визначення змінних рядків таблиці є самим повільним
(Особливо у випадку таблиць з великим числом полів), але і найбільш надійним, так
як практично гарантує достовірну ідентифікацію записи в будь-якій ситуації,
навіть у разі відсутності ключових полів (якщо, звичайно, таблиця задовольняє
вимогу реляційної моделі, що свідчить, що кожен запис має бути
унікальна і, отже, повинна володіти унікальним набором полів).

Одним з інших можливих значень цієї властивості є UpWhereChanged,
при якому в реченні WHERE містяться тільки поля, змінені в даному
запиті, і ключові поля. У цьому випадку запит має наступний вигляд:

UPDATE “HOLDINGS” SET “SYMBOL”=:1 WHERE “ROWID”=:2 AND “SYMBOL”=:3

Такий запит виконується швидше, але в цьому випадку можливі колізії при
багато користувачів роботі. Наприклад, один користувач зчитує запис для
редагування в клієнтську програму, інший відразу після цього її видаляє, а
Треті створюють нову з тими ж значеннями змінних полів і тими ж значеннями
ключових полів. Саме ця нова запис і буде модифікуватися замість
ліченої. Однак такий випадок малоймовірний, особливо якщо що стали непотрібними
первинні ключі видалених записів якийсь час не використовуються (наприклад, при
створення ключів за допомогою генераторів послідовностей).

Третім можливим значенням властивості UpdateMode є UpWhereKeyOnly. В
цьому випадку пропозиція WHERE містить тільки ключове поле:

UPDATE “HOLDINGS” SET “SYMBOL”=:1 WHERE “ROWID”=:2

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

Підвищення ефективності SQL-запитів


Ефективне програмування на SQL – тема вельми обширна, гідна
окремої статті (і навіть не однієї). Можливість і результативність використання
багатьох прийомів оптимізації нерідко залежить від особливостей використовуваного сервера
баз даних і управляє його роботою операційної системи. Тому тут ми лише
коротко перерахуємо найбільш часто вживані прийоми оптимізації
SQL-пропозицій.

Якщо потрібно визначити наявність в таблиці записів, що задовольняють
якомусь умові, слід віддати перевагу використання предиката EXIST запитом,
обчислюється число таких записів. Запит виду

SELECT * FROM <ім'я таблиці> WHERE (SELECT COUNT (*) FROM <ім'я таблиці> WHERE <умова>)> 0
змусить сервер при виконанні
внутрішнього підзапиту перебрати всі рядки таблиці, перевіряючи відповідність
кожного запису вказаною умові, тоді як запит виду

SELECT * FROM <ім'я таблиці> WHERE EXISTS (SELECT * FROM <ім'я таблиці> WHERE <умова>)
змусить сервер перебирати запису до
знаходження першого запису, що задовольняє вказаною умові. Зайвий перебір
записів на сервері, природно, займає деякий час – чудес не буває.

Багато прийомів оптимізації пов’язані з використанням індексів. Якщо будь-яка
поле таблиці часто використовується в реченні WHERE, порівнює його значення з
будь-якої константою або параметром, наявність індексу для цього поля прискорює
подібні операції. З цієї ж причини рекомендується індексувати зовнішні ключі
у таблиць з великою кількістю записів. Проте слід мати на увазі, що підтримка
індексів уповільнює операції вставки записів, тому при проектуванні даних
слід зважити всі “за” і “проти” створення індексів, а ще краще – провести
відповідне тестування, заповнивши таблиці випадковими даними (для цієї мети
можна написати відповідну програму, а ще краще – скористатися
готовими засобами тестування типу SQA Suite).

Говорячи про використання індексів, слід також звернути увагу на те, що
при використанні індексованих полів в якості аргументів функцій наявність
індексу не впливає на швидкість виконання запиту – індекс у цьому випадку не
використовується.

Особливо слід відзначити проблеми, пов’язані з використанням вкладених
запитів. Справа в тому, що швидкість виконання запиту істотно залежить від
числа рівнів вкладеності підзапитів (час виконання приблизно пропорційно
добутку числа записів у таблицях, що використовуються в підзапит). Фактично
перевірка відповідності умові WHERE кожного запису із зовнішнього підзапит
ініціює виконання внутрішнього підзапит, що особливо помітно позначається
при великому числі записів. У практиці автора трохи більше року тому був випадок,
коли при приведенні в порядок однієї з використовуваних корпоративних
інформаційних систем після виконання кількох звичайних запитів на оновлення
даних у таблиці з декількома десятками тисяч записів, що виконувалися протягом
декількох секунд, був ініційований вкладений запит на оновлення даних до цієї
ж таблиці. Цей запит виконувався більше двох годин (чого, власне кажучи, і
слід було очікувати). Тому використовувати вкладені запити слід тільки в тих
випадках, коли без них не можна обійтися. Альтернативою використання вкладених
запитів може служити фільтрація результатів звичайного запиту в клієнтському
додатку або послідовне виконання декількох запитів зі створенням
тимчасових таблиць на сервері.

Оптимізація клієнтської програми


Методи оптимізації клієнтської програми мало чим відрізняються від методів
оптимізації звичайних програм C + + Builder. Зазвичай оптимізація полягає в
підвищення швидкодії програми і в зниженні обсягу використовуваних ресурсів
операційної системи.

Зниження кількості споживаних ресурсів можливо різними способами.
Основний принцип їх економії – не використовувати ресурси даремно. Саме тому
рекомендується в додатках, що використовують велику кількість форм, створювати їх
динамічно та знищувати, як тільки вони стають непотрібними (що відрізняється від
установок менеджера проектів за замовчуванням, які припускають автоматичне
створення всіх форм відразу ж). Однак при цьому слід пам’ятати, що модуль
даних, що містить компоненти доступу до даних, що використовуються інтерфейсними
елементами динамічно створюваної форми, повинен бути створений до створення самої
форми, щоб уникнути виняткової ситуації, пов’язаної зі зверненням до
неіснуючому об’єкту.

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

Ще одним способом економії ресурсів клієнтського програми є
використання більш економічних інтерфейсних елементів у випадках, де це
можливо (наприклад, TDBText або TLabel замість TDBEdit, TLabel замість TDBMemo при
відображенні полів, редагування яких не передбачається, TDBGrid замість
TDBControlGrid і т.д.).

Ще один прийом, що підвищує швидкодію клієнтського додатка, полягає
в скороченні числа операцій, пов’язаних з виведенням даних з таблиць на екран,
наприклад, при “прокручування” великої кількості рядків в компонентах типу
TDBGrid або TDBCtrlGrid в процесі навігації по набору даних або будь-якої їх
обробки. У цьому випадку рекомендується на час відключати зв’язок інтерфейсних
елементів з компонентом TDataSource, встановивши значення його властивості Enabled
рівним false (приклад використання цього прийому буде наведено нижче).

Про навігаційних методи та “кліпперном” стилі програмування


Говорячи про оптимізацію клієнт-серверних інформаційних систем, хотілося б
окремо зупинитися на одній дуже поширеною помилку, яку здійснюють
програмістами, які мають великий досвід роботи з настільними СУБД і засобами
розробки, що базуються на xBase-мовами, такими, як Clipper, dBase, FoxPro і
ін При використанні засобів розробки такого роду будь-яка зміна даних
у таблиці згідно з якимись правилами здійснюється зазвичай шляхом створення
циклу типу:

USE HOLDINGS
GO TOP
DO WHILE !EOF()
PUR_PRICE=PUR_PRICE+10

SKIP
ENDDO
CLOSE

У наведеному фрагменті xBase-коду PUR_PRICE – ім’я поля таблиці HOLDINGS,
підданого зміні.

При переході до архітектури клієнт / сервер і засобам розробки,
підтримує SQL, спочатку виникає природне бажання продовжувати писати
подібний код, використовуючи цикли і навігацію по таблиці. Це не так страшно в
випадку використання C + + Builder з настільними СУБД – локальний SQL, здатний
бути альтернативою в цьому випадку, в кінцевому підсумку також ініціює перебір
записів таблиці. Взагалі кажучи, те ж саме відбувається і при виконанні запиту
типу UPDATE HOLDINGS SET PUR_PRICE = PUR_PRICE +10 на сервері баз даних, але
подібний цикл є внутрішнім процесом сервера, в якому не задіяні
ні клієнт, ні мережу. Однак при використанні “кліпперного” стилю
програмування бібліотека BDE зовсім не зобов’язана здогадуватися, що мав на увазі
програміст, який написав подібний цикл, і генерує зовсім не такі запити!

Розглянемо простий приклад. Створимо копію таблиці HOLDINGS.DBF із входить до
комплект поставки C + + Builder бази даних DBDEMOS на якому-небудь сервері баз
даних, наприклад, Personal Oracle (скориставшись, наприклад, утилітою Data
Migration Wizard з комплекту поставки Borland C + + Builder). Потім створимо нове
додаток, що складається з однієї форми, що включає компоненти TDBGrid, TTable,
TDataSource, TQuery, TDBNavigator і три кнопки (рис.3).



Рис.3. Головна форма програми для тестування
SQL-запитів, що генеруються BDE

Встановимо наступні значення властивостей використовуваних компонентів (табл.1):


Таблиця 1.




















































Компонент Властивість Значення
DBNavigator1 DataSource DataSource1
DBGrid DataSource DataSource1
Button1 Caption “Use SQL”
Button2: Caption “Update records”
Button3: Caption “Exit”
DataSource1 DataSet Table1
Table1 DatabaseName ORACLE7
TableName HOLDINGS
UpdateMode UpWhereKeyOnly
Table1PUR_PRICE FieldName “PUR_PRICE”
Query1 DatabaseName ORACLE7
SQL “UPDATE HOLDINGS SET
PUR_PRICE=PUR_PRICE+10”

Тепер створимо обробники подій, пов’язані з натисканням на кнопки. Кнопка
Update records реалізує аналог фрагмента xBase-коду, наведеного вище:

void __fastcall TForm1::Button2Click(TObject *Sender)
{
Table1->First();
DataSource1-> Enabled = false; / / Не будемо знущатися над відеоадаптером!
while (!Table1->Eof)
{
Table1->Edit();
Table1PUR_PRICE->Value=Table1PUR_PRICE->Value+10;
Table1->Next();
}
DataSource1-> Enabled = true; / / Подивимося, що вийшло …
}

Тимчасове відключення зв’язку між DataSource1 і Table1 в даному обробнику
подій зроблено для того, щоб виключити перемальовування компонента DBGrid1 при
зміні кожного запису.

Кнопка Use SQL реалізує виконання одиночного SQL-запиту UPDATE HOLDINGS
SET PUR_PRICE=PUR_PRICE+10:

void __fastcall TForm1::Button1Click(TObject *Sender)
{
Query1->Prepare();
Query1->ExecSQL();
Table1-> Refresh (); / / Подивимося на результат …
}

Скомпілювавши додаток, запустимо SQL Monitor і подивимося, які запити
генеруються BDE при натисканні на ці кнопки.

При використанні кнопки Update records log-файл має наступний вигляд:


  1. 14:37:08 SQL Prepare: ORACLE – UPDATE “HOLDINGS” SET “PUR_PRICE”=:1 WHERE
    “ROWID”=:2
  2. 14:37:08 SQL Execute: ORACLE – UPDATE “HOLDINGS” SET “PUR_PRICE”=:1 WHERE
    “ROWID”=:2
  3. 14:37:08 SQL Stmt: ORACLE – Close
  4. 14:37:08 SQL Prepare: ORACLE – SELECT “ACCT_NBR” ,”SYMBOL” ,”SHARES”
    ,”PUR_PRICE” ,”PUR_DATE” ,”ROWID” FROM “HOLDINGS” WHERE “ACCT_NBR”=:1
  5. 14:37:08 SQL Execute: ORACLE – SELECT “ACCT_NBR” ,”SYMBOL” ,”SHARES”
    ,”PUR_PRICE” ,”PUR_DATE” ,”ROWID” FROM “HOLDINGS” WHERE “ACCT_NBR”=:1
  6. 14:37:08 SQL Misc: ORACLE – Set rowset size
  7. 14:37:08 SQL Stmt: ORACLE – Fetch
  8. 14:37:08 SQL Stmt: ORACLE – EOF
  9. 14:37:08 SQL Stmt: ORACLE – Close
  10. 14:37:08 SQL Prepare: ORACLE – UPDATE “HOLDINGS” SET “PUR_PRICE”=:1 WHERE
    “ROWID”=:2

І так далі, поки не закінчаться всі записи:


  1. 14:37:10 SQL Prepare: ORACLE – SELECT “ACCT_NBR” ,”SYMBOL” ,”SHARES”
    ,”PUR_PRICE” ,”PUR_DATE” ,”ROWID” FROM “HOLDINGS” WHERE “ACCT_NBR”=:1
  2. 14:37:10 SQL Execute: ORACLE – SELECT “ACCT_NBR” ,”SYMBOL” ,”SHARES”
    ,”PUR_PRICE” ,”PUR_DATE” ,”ROWID” FROM “HOLDINGS” WHERE “ACCT_NBR”=:1
  3. 14:37:10 SQL Misc: ORACLE – Set rowset size
  4. 14:37:10 SQL Stmt: ORACLE – Fetch
  5. 14:37:10 SQL Stmt: ORACLE – EOF
  6. 14:37:10 SQL Stmt: ORACLE – Close

Зазначимо, що це ще не найбільший набір запитів для даного випадку, так
як при оновленні таблиці було використано значення UpWhereKeyOnly властивості
UpdateMode компонента Table1, при якому запити на оновлення одного запису
мають мінімальний набір перевірених параметрів.

При використанні кнопки Use SQL log-файл має зовсім інший вигляд:


  1. 14:35:51 SQL Prepare: ORACLE – UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE-10
  2. 14:35:51 SQL Transact: ORACLE – Set autocommit on/off
  3. 14:35:51 SQL Execute: ORACLE – UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE-10
    14:35:51 SQL Stmt: ORACLE – Close

Решта SQL-запити, що містяться в log-файлі, генеруються BDE при
виконанні методу Refresh () компонента Table1:


  1. 14:35:51 SQL Prepare: ORACLE – SELECT “ACCT_NBR” ,”SYMBOL” ,”SHARES”
    ,”PUR_PRICE” ,”PUR_DATE” ,”ROWID” FROM “HOLDINGS” WHERE “ACCT_NBR”=:1
  2. 14:35:51 SQL Execute: ORACLE – SELECT “ACCT_NBR” ,”SYMBOL” ,”SHARES”
    ,”PUR_PRICE” ,”PUR_DATE” ,”ROWID” FROM “HOLDINGS” WHERE “ACCT_NBR”=:1
  3. 14:35:51 SQL Misc: ORACLE – Set rowset size
  4. 14:35:51 SQL Stmt: ORACLE – Fetch
  5. 14:35:51 SQL Stmt: ORACLE – EOF
  6. 14:35:51 SQL Stmt: ORACLE – Close
  7. 14:35:51 SQL Prepare: ORACLE – SELECT “ACCT_NBR” ,”SYMBOL” ,”SHARES”
    ,”PUR_PRICE” ,”PUR_DATE” ,”ROWID” FROM “HOLDINGS” WHERE ((“ACCT_NBR” IS NULL OR
    “ACCT_NBR”> :1)) ORDER BY “ACCT_NBR” ASC
  8. 14:35:51 SQL Execute: ORACLE – SELECT “ACCT_NBR” ,”SYMBOL” ,”SHARES”
    ,”PUR_PRICE” ,”PUR_DATE” ,”ROWID” FROM “HOLDINGS” WHERE ((“ACCT_NBR” IS NULL OR
    “ACCT_NBR”> :1)) ORDER BY “ACCT_NBR” ASC
  9. 14:35:51 SQL Misc: ORACLE – Set rowset size
  10. 14:35:51 SQL Stmt: ORACLE – Fetch

Якщо з тексту обробника події Button1Click видалити рядок

Table1->Refresh();,

то дії з 5-го по 14-е виконуватися не будуть. Крім того, при натисканні на
цю ж кнопку кілька разів поспіль log-файл буде мати наступний вигляд:


  1. 14:11:36 SQL Prepare: ORACLE – UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE-10
  2. 14:11:36 SQL Execute: ORACLE – UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE-10
  3. 14:11:40 SQL Stmt: ORACLE – Reset
  4. 14:11:40 SQL Execute: ORACLE – UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE-10
  5. 14:14:17 SQL Stmt: ORACLE – Reset
  6. 14:14:17 SQL Execute: ORACLE – UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE-10
  7. 14:14:19 SQL Stmt: ORACLE – Reset

Як бачимо, компіляція запиту сервером здійснюється в цьому випадку тільки
один раз.

Отже, ми бачимо, що “кліпперний” стиль програмування при роботі з
SQL-серверами абсолютно неприйнятний – він призводить до перевантажень сервера, мережі і
робочої станції одночасно, а різниця в швидкості виконання помітна навіть при
невеликому обсязі таблиці та використанні локального сервера, тому, аналізуючи
причини низької продуктивності додатків, варто подивитися – а чи немає в
клієнтському додатку подібних фрагментів коду?

На закінчення хотілося б відзначити, що оптимізація клієнт-серверних
інформаційних систем повинна здійснюватися з урахуванням результатів аналізу
продуктивності і ретельного тестування, можливо, не тільки за допомогою SQL
Monitor, але і за допомогою спеціальних засобів тестування, що володіють
додатковими функціональними можливостями.

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*