Таблица 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-файл имеет следующий вид:
14:37:08 SQL Prepare: ORACLE - UPDATE "HOLDINGS" SET "PUR_PRICE"=:1 WHERE "ROWID"=:2
14:37:08 SQL Execute: ORACLE - UPDATE "HOLDINGS" SET "PUR_PRICE"=:1 WHERE "ROWID"=:2
14:37:08 SQL Stmt: ORACLE - Close
14:37:08 SQL Prepare: ORACLE - SELECT "ACCT_NBR" ,"SYMBOL" ,"SHARES" ,"PUR_PRICE" ,"PUR_DATE" ,"ROWID" FROM "HOLDINGS" WHERE "ACCT_NBR"=:1
14:37:08 SQL Execute: ORACLE - SELECT "ACCT_NBR" ,"SYMBOL" ,"SHARES" ,"PUR_PRICE" ,"PUR_DATE" ,"ROWID" FROM "HOLDINGS" WHERE "ACCT_NBR"=:1
14:37:08 SQL Misc: ORACLE - Set rowset size
14:37:08 SQL Stmt: ORACLE - Fetch
14:37:08 SQL Stmt: ORACLE - EOF
14:37:08 SQL Stmt: ORACLE - Close
14:37:08 SQL Prepare: ORACLE - UPDATE "HOLDINGS" SET "PUR_PRICE"=:1 WHERE "ROWID"=:2
И так далее, пока не кончатся все записи:
14:37:10 SQL Prepare: ORACLE - SELECT "ACCT_NBR" ,"SYMBOL" ,"SHARES" ,"PUR_PRICE" ,"PUR_DATE" ,"ROWID" FROM "HOLDINGS" WHERE "ACCT_NBR"=:1
14:37:10 SQL Execute: ORACLE - SELECT "ACCT_NBR" ,"SYMBOL" ,"SHARES" ,"PUR_PRICE" ,"PUR_DATE" ,"ROWID" FROM "HOLDINGS" WHERE "ACCT_NBR"=:1
14:37:10 SQL Misc: ORACLE - Set rowset size
14:37:10 SQL Stmt: ORACLE - Fetch
14:37:10 SQL Stmt: ORACLE - EOF
14:37:10 SQL Stmt: ORACLE - Close
Отметим, что это еще не самый большой набор запросов для данного случая, так как при обновлении таблицы было использовано значение UpWhereKeyOnly свойства UpdateMode компонента Table1, при котором запросы на обновление одной записи имеют минимальный набор проверяемых параметров.
При использовании кнопки Use SQL log-файл имеет совершенно другой вид:
14:35:51 SQL Prepare: ORACLE - UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE-10
14:35:51 SQL Transact: ORACLE - Set autocommit on/off
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:
14:35:51 SQL Prepare: ORACLE - SELECT "ACCT_NBR" ,"SYMBOL" ,"SHARES" ,"PUR_PRICE" ,"PUR_DATE" ,"ROWID" FROM "HOLDINGS" WHERE "ACCT_NBR"=:1
14:35:51 SQL Execute: ORACLE - SELECT "ACCT_NBR" ,"SYMBOL" ,"SHARES" ,"PUR_PRICE" ,"PUR_DATE" ,"ROWID" FROM "HOLDINGS" WHERE "ACCT_NBR"=:1
14:35:51 SQL Misc: ORACLE - Set rowset size
14:35:51 SQL Stmt: ORACLE - Fetch
14:35:51 SQL Stmt: ORACLE - EOF
14:35:51 SQL Stmt: ORACLE - Close
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
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
14:35:51 SQL Misc: ORACLE - Set rowset size
14:35:51 SQL Stmt: ORACLE - Fetch
Если из текста обработчика события Button1Click удалить строку
Table1->Refresh();,
то действия с 5-го по 14-е выполняться не будут. Кроме того, при нажатии на эту же кнопку несколько раз подряд log-файл будет иметь следующий вид:
14:11:36 SQL Prepare: ORACLE - UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE-10
14:11:36 SQL Execute: ORACLE - UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE-10
14:11:40 SQL Stmt: ORACLE - Reset
14:11:40 SQL Execute: ORACLE - UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE-10
14:14:17 SQL Stmt: ORACLE - Reset
14:14:17 SQL Execute: ORACLE - UPDATE HOLDINGS SET PUR_PRICE=PUR_PRICE-10
14:14:19 SQL Stmt: ORACLE - Reset
Как видим, компиляция запроса сервером осуществляется в этом случае только один раз.
Итак, мы видим, что "клипперный" стиль программирования при работе с SQL-серверами абсолютно неприемлем - он приводит к перегрузкам сервера, сети и рабочей станции одновременно, а разница в скорости выполнения заметна даже при небольшом объеме таблицы и использовании локального сервера, поэтому, анализируя причины низкой производительности приложений, стоит посмотреть - а нет ли в клиентском приложении подобных фрагментов кода?
В заключение хотелось бы отметить, что оптимизация клиент-серверных информационных систем должна производиться с учетом результатов анализа производительности и тщательного тестирования, возможно, не только с помощью SQL Monitor, но и с помощью специальных средств тестирования, обладающих дополнительными функциональными возможностями.