Quick Guide: dbExpress
Ответы на часто задаваемые вопросы
Алексей Макаренко
Олег Перехрест
27 апреля 2004г
Q. Что нового в технологии dbExpress в связи с выходом Delphi7
A. Обновлены драйвера dbExpress для Informix SE, Oracle 9i,
DB2 7.2, InterBase 6.5, MySQL 3.23.49 и для MS SQL Server 2000. А вот
SQL Links Borland не рекомендует использовать,
более того - работа над усовершенствованием
SQL Links прекращается. Сообщается также, что
эта технология не будет поставляться
вместе с Delphi после 2002 года. В виде замены
рекомендовано использовать dbExpress для
доступа к базам данных SQL серверов.
Прекращена также поддержка CORBA-соединений
компонентами DataSnap.
Закладка dbExpress включает новый компонент
TSimpleDataSet для использования с простыми,
двухуровневыми приложениями баз данных (TSimpleDataSet
заменил собой TSQLClientDataSet).
Q. Какие преимущества технологии dbExpress
A. Данная технология обладает следующими
важными преимуществами:
- Средства dbExpress гораздо проще с точки зрения установки и компактнее по сравнению с их предшественником BDE;
- Технология dbExpress является межплатформенной, которая может работать как в Windows (Delphi), так и в Linux (Kylix);
- Для dbExpress можно создавать новые драйвера. При этом достаточно реализовать опубликованные интерфейсы и разработать библиотеку, обладающую методами доступа к базе данных.
Q. Какие отличия dbExpress от остальных механизмов доступа к данным, и в первую очередь от BDE
A.
- dbExpress обеспечивает более простой и быстрый доступ к удаленным БД благодаря использованию однонаправленных наборов данных;
- Так же, как и BDE, dbExpress обрабатывает пользовательские запросы и хранимые процедуры, однако понятие открытия таблицы базы данных в новом механизме отсутствует (в BDE открытие таблицы БД не только занимало большую часть ресурсов на стороне клиента, но и блокировало ресурсы сервера);
- В отличие от BDE, dbExpress возвращает только однонаправленные наборы данных, не подлежащие редактированию. Для кэширования, фильтрации записей и индексирования возвращаемого множества записей, а также для навигации по нему необходимо использовать дополнительные компоненты, например ClientDataSet;
- BDE кэширует метаданные, делая их доступными в последующих запросах. dbExpress этого не делает, а работа с метаданными на этапе проектирования осуществляется с использованием основного механизма доступа к данным;
- При работе с удаленными SQL-базами данных через BDE время обращения к базе данных увеличивается за счет выполнения внутренних запросов, обеспечивающих навигацию по таблицам, доступ к BLOB-объектам и получение метаданных. dbExpress выполняет только запросы пользователей, отсутствие дополнительных операций помогает оптимизировать доступ к БД;
- dbExpress управляет буфером записей с помощью внутренних механизмов, тогда как BDE требует, чтобы клиентская часть приложения выделяла память для хранения записей. Использование клиентского буфера может вызвать ошибку, если клиент выделит недостаточно памяти для хранения данных;
- dbExpress не поддерживает локальные базы данных типа Paradox, dBase или FoxPro. Borland рекомендует перейти к использованию СУБД InterBase. Также можно воспользоваться драйверами сторонних производителей;
- dbExpress содержит драйверы для таких СУБД, как InterBase, Oracle, DB2, Informix, MSSQL и MySQL. Для использования других серверов существует три возможности: перенос данных в формат одной из поддерживаемых СУБД, написание собственного драйвера или же использование драйверов сторонних разработчиков для работы с вашей СУБД.
Q. Что такое однонаправленный набор данных
A. Суть однонаправленных наборов данных заключается в том, что они не буферизируют данные при навигации или модификации. В отличии от используемых в BDE двунаправленных наборов данных с буферизацией в оперативной памяти, однонаправленные наборы отличаются большей эффективностью, но обладают и некоторыми ограничениями:
- Однонаправленные наборы данных обладают всего лишь двумя навигационными методами: First() и Next(). Попытка вызова иных методов, например Last() или Prior(), приведет к исключению. Такое ограничение не удивительно, так как многие СУБД поддерживают лишь однонаправленные курсоры. Некоторые, методы такие как работа с закладками (Bookmarks), представляют собой просто заглушки;
- Данные однонаправленных наборов нельзя редактировать, поскольку для них не выделяется буфер, пригодный для редактирования. Свойство CanModify всегда равно False, и попытки перевода датасета в режим редактирования всегда приводят к неудаче. Данная возможность полезна, при формировании отчетов. Редактирование данных, однако, может осуществляться с помощью SQL команды UPDATE или используя архитектуру provider/resolver (компоненты TDataSetProvider/TClientDataSet или TSimpleDataSet);
- Однонаправленные наборы данных не поддерживают фильтрацию, поскольку отсутствие буфера не позволяет создавать набор для нескольких записей. При попытке фильтрации вызывается исключение. Все ограничения на диапазон выбираемых записей должны содержаться в SQL команде;
- Однонаправленные наборы данных не поддерживают подстановочные (lookup) поля, т.к при этом необходимо буферизировать возможные значения такого поля.
Q. Что такое provider/resolver
A. Это механизм, который отвечает за выдачу данных по запросу пользователя и проведение изменений в базе данных. Данный механизм состоит из двух частей:
- Провайдер. Эта часть отвечает за выборку данных из массива по запросу пользователя, передачу их пользователю вместе с нужными метаданными и хранение их в памяти на время работы с ними, а также ведет протокол изменений переданных пользователю записей;
- Ресолвер. Эта часть обеспечивает внесение изменений в базу данных. При обновлении базы данных серверу передается протокол изменений, в соответствии с которым начинается транзакция, и проводятся изменения в БД.
Q. Какие преимущества использования механизма provider/resolver
A.
- Короткое время жизни транзакций
Долгие транзакции заставляют сервер БД удерживать блокировки, которые снижают возможности многопользовательской обработки данных и отнимают ресурсы сервера. При архитектуре provider/resolver, транзакция существует только в момент, когда применяются обновления. Таким образом снижается требование к ресурсам и уменьшается вероятность блокировок, особенно при большом количестве пользователей сервера БД; - Дает возможность редактировать любые записи
Записи, возвращаемые многотабличными выборками, хранимыми процедурами или нередактируемыми view не могут быть изменены напрямую. Но имеется возможность указать при помощи свойства ProviderFlags у объектов TField, какие столбцы должны обновляться, а в событии DataSetProvider.OnGetTableName - какая именно таблица должна обновляться. Или написать обработчик события BeforeUpdateRecord, с установкой Applied := True. При этом большинство нередактируемых данных станут редактируемыми; - Быстрая сортировка и поиск
Поскольку ClientDataSet хранит записи в памяти, они могут быть быстро отсортированы. Для повышения скорости сортировки или поиска, можно создать индексы над данными ClientDataSet либо во время разработки, либо во время выполнения приложения; - Автоматическое агрегирование
ClientDataSet может производить сложные вычисления, такие как Sum(Price) -Sum(Cost). Также можно группировать вычисления сумм по полю или комбинации полей. Доступные агрегаты Sum, Min, Max, Count и Avg; - Просмотр подмножества данных
Выражения фильтрации могут использовать синтаксис WHERE для отображения подмножества записей ClientDataSet, без необходимости конструировать запрос на клиенте и отправлять его каждый раз на сервер; - Множество видов данных
Возможность "клонировать" курсор ClientDataSet (метод CloneCursor) позволяет просматривать одни и те же данные различными способами, одновременно. Например, можно просматривать одни и те же данные, отсортированные по разным столбцам; - Вычисляемые столбцы на клиенте
Также имеется возможность добавлять вычисляемые столбцы к ClientDataSet во время разработки. Поскольку вычисления производятся скомпилированным Delphi кодом, они выполняются очень быстро и могут быть более сложными, чем вычисления, производимые на сервере; - Ограничение, которого нет
Может показаться, что у хранения записей в памяти есть ограничение по количеству таких записей, с которыми можно работать. Но обычно приложения разрабатываются таким образом, чтобы выбирать небольшой объем данных для минимизации сетевого трафика и загрузки сервера БД. Даже если нужно работать с необычно большим количеством записей, помните что 10 тысяч записей, каждая по 100 байт, занимают 1 мегабайт памяти. В случаях, когда объем данных действительно большой, компоненты ClientDataSet и DataSetProvider имеют свойства и события, которые позволяют выбирать часть записей, редактировать их, удалять из памяти и затем получать новую порцию записей.
Q. Соответствие компонентов доступа к базам данных, различных технологий
A.
| InterBase Express | ADO | BDE | dbExpress | Комментарий |
| TIBDatabase | TADOConnection | TDatabase | TSQLConnection | Установление соединения с БД. |
| TIBTable | TADOTable | TTable | TSQLTable | Однонаправленный не редактируемый набор данных. |
| TIBQuery | TADOQuery | TQuery | TSQLQuery | Однонаправленный не редактируемый набор данных. |
| TIBStoredProc | TADOStoredProc | TStoredProc | TSQLStoredProc | Однонаправленный не редактируемый набор данных. |
| TIBDataSet | TADODataSet | нет аналога | TSQLDataSet | TSQLDataSet объединяет в себе возможности компонентов SQLTable, SQLQuery и SQLStoredProcedure. |
| TIBSQLMonitor | нет аналога | утилита SQL Monitor | TSQLMonitor | Отслеживает все инструкции SQL, проходящие между компонентом SQLConnection и сервером базы данных, к которому он подключен. |
| нет аналога | нет аналога | TBDEClientDataSet | TSimpleDataSet | Используется при разработке 2-ух уровневых приложений. Является комбинацией SQLDataSet, DataSetProvider, ClientDataSet и позволяет читать и редактировать данные. |
| нет аналога | нет аналога | TBatchMove | нет аналога | При необходимости, придется повторить данную функциональность. |
| &hbsp; | TSession | нет аналога | Механизм TSession в технологии dbExpress не нужен. | |
| TUpdateSQL | нет аналога | Возможности TClientDataSet заменяют механизм CachedUpdates. | ||
| TNestedDataSet | нет аналога | Возможность обработки вложенных наборов данных встроена в TDataSetProvider и TClientDataSet. |
Q. Что такое Borland MyBase
A. Данные содержащиеся в ClientDataSet могут быть сохранены или загружены как дисковый файл в двоичном, либо в XML формате. Это позволяет ClientDataSet функционировать как однопользовательская система реляционной базы данных. Данную возможность можно использовать следующим образом:
- Создавать приложения в соответствии с моделью "портфеля", при которой пользователь портативного ПК выбирает данные из сервера базы данных и сохраняет их локально. Затем пользователь отсоединяется от сети, вставляет, удаляет и обновляет записи; потом сохраняет данные и протокол изменений, снова подключается к сети и выполняет обновления в базе данных;
- Использовать для импорта/экспорта данных в XML;
- Использовать в качестве временной, хранящейся в памяти таблицы, которая может создаваться и ликвидироваться «на лету».
Недостатки:
- Данные должны быть размещены в памяти во время осуществления доступа;
- В предыдущих версиях Delphi (на Delphi7 - не знаю), импорт/экспорт данных в XML формате, вызывал небольшую утечку памяти.
Q. Где можно найти альтернативные драйвера для dbExpress
A.
| dbExpress driver name | Platforms supported | Database supported | Download URL |
| MySQL dbExpress | N/A | MySQL 3.22.x | |
| DbxSQLite | N/A | SQLite 2.8.0 |
www.sqlite.org www.bcp-software.nl |
| dbExpress Pervasive 2000i SQL | Windows | Pervasive 2000i SQL | marcin@festus.com.pl |
| pgExpress Driver | Windows/Linux | PostgreSQL | http://www.vitavoom.com |
| dbExpress for Client Access/400, dbexpca400.dll | Windows 9x/NT/2k with Client Access installed | DB2/400 (AS/400 databases) | http://www.sawatzki.de http://codecentral.borland.com/codecentral/ccweb.exe/listing?id=15909 |
| SQLTHINK.DLL / libsqlthink.so.0 | Windows, Linux | ThinkSQL RDBMS | http://www.thinksql.co.uk |
| dbExpress for ADO | Windows 9x/NT/2K with MDAC | Anything with an OLEDB provider | http://islamov.com/dbexpress/ |
| dbExpress for ODBC | Windows 9x/NT/2K with ODBC | Anything with an ODBC driver | http://islamov.com/dbexpress/ |
| dbExpress for MS SQL | Windows 9x/NT/2K with DB-LIB | MS SQL Server 7/2K | http://islamov.com/dbexpress/ |
| dbxoodbc - Open Source DbExpress driver for ODBC | Microsoft Windows. May work on Linux, but untested | Any ODBC compliant database
|
http://sourceforge.net/projects/open-dbexpress/
http://cvs.sourceforge.net/cgi-bin/viewcvs.cgi/open-dbexpress/dbxoodbc/ |
| dbExpress for Informix | Windows 9x/NT/2K, Linux | Informix 7,9 | http://www.luxena.com/download.htm |
| Easysoft dbExpress-ODBC Gateway | Microsoft Windows. Linux. | Any ODBC compliant database. | http://www.easysoft.com/products/9999/platforms.phtml?product=2202 |
| dbExpress Driver for Sybase | Windows 9x/NT/2K | Sybase ver.: 11, 12.0, 12.5 | http://www.visoco.com/download/ |
| dbExpress Driver for Oracle | Windows 9x/NT/2K/Linux | Oracle 9i, Oracle 8i, Oracle 8 and Oracle 7 | http://www.crlab.com/dbexp.html |
| dbExpress for MS SQL Server | Windows 9x/NT/2K | MS SQL Server 2000 and MS SQL Server 7 | http://www.crlab.com/download.html#dbx |
| dbExpress Informix IDS 9.2 driver | Windows 9x/NT/2K | Informix IDS 9.2 | http://codecentral.borland.com/codecentral/ccweb.exe/listing?id=17693 |
Иногда имеет смысл исправлять ошибки и в midas: http://www.distribucon.com/midasbug/
Midas.dll Memory Manager Patch Version 2.04: http://codecentral.borland.com/codecentral/ccweb.exe/listing?id=20524
Q. В некоторых примерах, имеются
упоминания о компоненте TSQLClientDataSet, но Delphi7 и
Help его не находят
A. В Delphi7 данный компонент, не рекомендуется
к использованию, он заменен на TSimpleDataSet. Для поддержки старых приложений,
исходники компонента TSQLClientDataSet размещены в
каталоге "...\Demos\Db\SQLClientDataset".
Q. Для чего нужен компонент TSimpleDataSet
A. Данный компонент появился в Delphi7, как замена TSQLClientDataSet. Он комбинирует в себе TSQLDataSet, TDataSetProvider и TClientDataSet и является простой заменой TQuery при переходу с BDE на dbExpress. Также экономится время в процессе разработки, так как вместо трех компонент используется один. Однако данный компонент имеет недостатки:
- Не поддерживает многозвенные приложения;
- Невозможно подключить дочерний TDataSet для получения вложенных наборов данных;
- События встроенного TDataSetProvider не экспортированы;
- Свойство Options встроенного TDataSetProvider не экспортировано. Нет возможности устанавливать параметры провайдера во время разработки или выполнения;
- Если используется только Borland MyBase (база данных встроенная в TClientDataSet), то лучше использовать ClientDataSet отдельно, для уменьшения используемых ресурсов;
- Свойства и методы встроенного TSQLDataSet не эквивалентны свойствам TQuery. Поэтому использование TSimpleDataSet при переносе проектов с BDE могут потребовать больше изменений в коде.
Q. Как обойти недостатки компонента TSimpleDataSet
A. Необходимо разместить компоненты TSQLQuery,
TDataSetProvider и TClientDataSet на форму или модуль данных.
Установить свойство DataSetProvider.DataSet на SQLQuery, ProviderName на
ClientDataSet. Выбрать все три компонента, и в главном меню среды
выбрать - Component | Create Component Template. Указать имя класса, компонента и палитры для нового шаблона. Теперь можно
использовать три компонента так же легко как один компонент
TSimpleDataSet.
Q. Почему появились новые типы данных TFMTBCDField и TSQLTimeStampField
A.
TFMTBCDField - в этот объект помещаются все числовые значения, которые не вмещаются в double precision. Внутри данные хранятся, в переменной типа TBCD, являющийся настоящим числом BCD с максимальной точность 32 цифры. Для выполнения математических операций на данным типом столбца, используется свойство asVariant.
TSQLTimeStampField - используется для хранения даты и времени. Внутри данные хранятся, в переменной типа TSQLTimeStamp (это запись с отдельными полями для года, месяца, дня, часов, минут, секунд и миллисекунд), что предотвращает потерю точности. TSQLTimeStampField имеет методы as.... для конвертации даты и времени в другие типы данных.
Q. Как управлять транзакциями
A. Класс TSQLConnection имеют методы StartTransaction, Commit,
Rollback и позволяет работать с несколькими транзакциями, активными в один и тот же момент. Для поддержки такого режима методы StartTransaction, Commit и Rollback принимают параметр
TTransactionDesc, объявленный следующим образом:
TTransactionDesc = packed record
TransactionID : longword;
GlobalID : longword;
IsolationLevel :
TTransactionIsolationLevel;
CustomIsolation: longword;
end;
Для каждой отдельной транзакции необходим объявить переменную типа TTransactionDesc и установить TransactionId в число, которое должно быть уникально между всеми активными транзакциями. Поле GlobalId используется только в Oracle для распределенных транзакций. IsolationLevel может быть xilDirtyRead, xilReadCommitted или xilRepeatableRead. Поле CustomIsolation пока не поддерживается.
Пример:
var
T: TTransactionDesc;
begin
T.TransactionID := 1;
T.IsolationLevel := xilREADCOMMITTED;
SQLConnection.StartTransaction(T);
end;
Для обеспечения одного из главных достоинств dbExpress - короткое время жизни транзакции, необходимо придерживаться следующей схемы:
- Пользователь редактирует данные в ClientDataSet;
- Старт транзакции;
- Вызов ApplyUpdates;
- Commit или Rollback.
Q. Как управлять загрузкой параметров
подключения во время выполнения приложения
A. Для загрузки значений параметров
подключения во время выполнения приложения
необходимо присвоить значение True свойству
LoadParamsOnConnect. При загрузке приложения
компонент класса TSQLConnection определяет
размещение файла dbxconnections.ini на основании
содержимого ключа системного реестра Connection
Registry File в ветви HKEY_CURRENT_USER\Software\Borland\DBExpress.
Изменение значения этого ключа можно
сделать в момент установки приложения, а
также не забыть включить в поставку сам
файл dbxconnections.ini.
Q. Как отсортировать записи в TSQLDataSet
A. Для типа команды:
- ctQuery: порядок сортировки записей при выборке данных определяется конструкцией SQL ORDER BY, заданной в тексте запроса;
- ctTable: порядок сортировки по умолчанию определяет SQL сервер. Для изменения порядка сортировки в этом случае необходимо в свойстве SortFieldNames указать имена полей сортировки, разделив их точкой с запятой. При генерации запроса на выборку таблицы данные поля будут вставлены в запрос в конструкции ORDER BY. Данный способ пригоден и для команды ctQuery. При этом в тексте SQL команды не должно содержаться ORDER BY. Однако первый описанный способ сортировки для типа команд ctQuery более предпочтителен;
- ctStoredProc: порядок сортировки определяется в самой хранимой процедуре.
Q. Как выполнять команды DDL и DML
A. Для выполнения команд DDL и DML, в TSQLConnection имеется два метода:
- ExecuteDirect. Имеет всего один параметр - выполняемую команду SQL. Если возвращается значение 0, то команда выполнена успешно, иначе возвращается код ошибки dbExpress. Кода ошибок можно найти в файле dbExpress.pas;
- Execute. Кроме самой команды SQL, может содержать список значений для входных параметров и указатель на результирующий набор данных (например если SQL команда возвращает набор). Если в качестве указателя на результирующий набор данных передать nil и команда SQL вернет набор данных, то он не будет учтен, но и не будет ошибок и исключений.
При многократном исполнении одних и тех же SQL команд, их следует параметризировать. Для облегчения заключения текстовых значений в кавычки, следует использовать функцию QuotedStr.
Пример:
SQLDataSet1 := TSQLDataSet.Create(nil);
try
SQLConnection1.Execute('select * from filials', nil, SQLDataSet1);
...
finally
SQLDataSet1.Free;
end;
Q. Как создать базу данных в InterBase
A. Так как с помощью метода ExecuteDirect не удается создать базу данных, то можно воспользоваться следующим способом (или низкоуровневым API):
- Создать пустую базу данных;
- Создать ресурс например с именем EMPTYDB, с помощью команды. EMPTYDB RCDATA DISCARDABLE "путь к EMPTY.GDB" и поместить данную команду в файл EmptyDB.RC;
- С помощью компилятора ресурсов откомилировать: brcc32 EmptyDB.RC;
- Полученный файл EmptyDB.RES, подключить в приложение с помощью {$R EmptyDB.RES}.
Пример:
{$R EmptyDB.RES}
procedure CreateDB(const DatabaseName: string);
var
HRsrc: THandle;
Stream: TResourceStream;
begin
HRsrc := FindResource(HInstance, PChar('EMPTYDB'), RT_RCDATA);
if HRsrc <> 0 then
begin
Stream := TResourceStream.Create(HInstance, 'EMPTYDB',
RT_RCDATA);
try
Stream.SaveToFile(DatabaseName);
finally
Stream.Free;
end;
end;
end;
Q. Как TDataSetProvider определяет изменена ли запись другим пользователем
A. Для этих целей компонент DataSetProvider имеет свойство UpdateMode. Когда провайдер генерирует операторы SQL для обновления базы данных, каждый оператор UPDATE или DELETE включает условие WHERE для идентификации записи. Если UpdateMode установлен в:
- upWhereAll. В WHERE включены все поля. Обеспечивает наиболее высокую степень уверенности в том, что запись не изменялась со времени ее первоначального извлечения из базы данных. Если два пользователя редактируют одну и ту же запись, то первый пользователь может эту запись обновлять, тогда как второй пользователь получит сообщение об ошибке "Another user changed the record". Если в дальнейшем понадобится уточнить, какие именно поля следует проверять, следует установить значение pfInWhere в False;
- upWhereChanged. В WHERE включены только ключевые и модифицированные поля. Два пользователя могут одновременно редактировать одну и ту же запись, если только редактируются разные поля;
- upWhereKeyOnly. В WHERE включены только ключевые поля. Существующая запись всегда замещается новой. Реализуется принцип "выигрывает последний".
Q. Как обновлять результаты многотабличных выборок, процедур и не редактируемых view
A. Имеется три метода:
- Если запись включает столбцы из одной таблицы (например возвращаемые хранимой процедурой), то необходимо создать обработчик OnGetTableName, возвращающий имя таблицы которую необходимо обновить;
- Для обновления многотабличных выборок, где надо обновлять записи только одной таблицы. Необходимо установить ProviderFlags конкретных полей для указания, что именно эти поля надо обновлять и создать OnGetTableName, возвращающий имя нужной таблицы. После этого провайдер будет генерировать операторы SQL для обновления таблицы автоматически;
- Если нужно обновлять много таблиц для каждой записи, то
необходимо также обрабатывать событие
BeforeUpdateRecord, в котором можно создавать
любое количество запросов, опираясь на
различные факторы, например на значение
параметра UpdateKind и значение полей в наборе
DataSet. При просмотре или модификации
записей, содержащихся параметре DeltaDS
необходимо использовать свойства OldValue и
NewValue соответствующего объекта TField (при
использовании свойств TField.Value или TField.AsXXX
может быть непредсказуемый результат).
Пример:
procedure TForm1.DataSetProvider1BeforeUpdateRecord(Sender: TObject;
SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;
UpdateKind: TUpdateKind; var Applied: Boolean);
var
SQL: string;
Connection: TSQLConnection;
begin
//Будем использовать соединение переданного набора данных
Connection := (SourceDS as TCustomSQLDataSet).SQLConnection;
case UpdateKind of
ukInsert: begin
SQL := 'Вставка записи в 1-ую таблицу';
Connection.Execute(SQL, nil, nil);
SQL := 'Вставка записи в 2-ую таблицу';
Connection.Execute(SQL, nil, nil);
end;
ukModify: begin
SQL := 'Изменение 1-ой таблицы';
Connection.Execute(SQL, nil, nil);
SQL := 'Изменение 2-ой таблицы';
Connection.Execute(SQL, nil, nil);
end;
ukDelete: begin
SQL := 'Удаление из 1-ой таблицы';
Connection.Execute(SQL, nil, nil);
SQL := 'Удаление из 2-ой таблицы';
Connection.Execute(SQL, nil, nil);
end;
end;
//Уведомляем провайдер, что изменения выполнены вручную
Applied := True;
end;
Q. Где разместить бизнес-логику
A. Так как в обработчике BeforeUpdateRecord
провайдера имеется возможность проверять
запись перед ее обновлением, и изменять
значения полей - то это лучшее место для
размещения бизнес логики. Если в данном
обработчике будет производиться
модификация полей, перед отправкой на
сервер базы данных, то необходимо
установить poPropogateChanges в True. Теперь провайдер будет отсылать изменения обратно в ClientDataSet для обновления записей, хранимых в памяти.
Q. Что такое клонирование данных
A. Это способность клиентского набора данных TClientDataSet, клонировать информацию из другого набора данных. При этом создается только одна физическая копия данных, к которой получают доступ два и более различных наборов данных. Изменения в одном наборе данных немедленно отражаются на представлении данных другого набора. Зачем это надо:
- просмотр копии набора данных без использования текущего указателя записи первичного набора данных;
- к копии можно применять различные фильтры и диапазоны, не зависимо от первичного набора данных.
Пример:
var
cdsClone: TClientDataSet;
begin
cdsClone := TClientDataSet.Create(nil);
try
cdsClone.CloneCursor(ClientDataSet1, False, False);
...
finally
cdsClone.Free;
end;
end;
Q. Какие функции доступны для локальной фильтрации клиентских наборов данных TClientDataSet
A.
| Функция | Описание | Пример |
| = | Проверка равенства | ID=100 |
| <> | Проверка неравенства | ID<>100 |
| < | Меньше чем | ID<100 |
| > | Больше чем | ID>100 |
| <= | Меньше или равно | ID<=100 |
| >= | Больше или равно | ID=>100 |
| BLANK | Пустое строковое поле (не тоже что NULL) | Name=BLANK |
| IS NULL | Проверка на нулевое значение | ID IS NULL |
| IS NOT NULL | Проверка на ненулевое значение | ID IS NOT NULL |
| AND | Логическое И | (ID=100) AND (Name=BLANK) |
| OR | Логическое ИЛИ | (ID=100) OR (Name=BLANK) |
| NOT | Логическое ОТРИЦАНИЕ | NOT (Name=BLANK) |
| + | Сложение (чисел, строк и дат/времени) | |
| - | Вычитание (чисел и дат/времени) | |
| * | Умножение (чисел) | |
| / | Деление (чисел) | |
| Upper | Верхний регистр | Upper(Name)='PEGAS' |
| Lower | Нижний регистр | Upper(Name)='pegas' |
| SubString | Вырезать подстроку | SubString(Name, 3)='AS' |
| Trim | Вырезание начальных и конечных символов строки | Trim(Name) или Trim(Name, '.') |
| TrimLeft | Вырезание начальных символов строки | TrimLeft(Name) или TrimLeft(Name, '.') |
| TrimRight | Вырезание конечных символов строки | TrimRight(Name) или TrimRight(Name, '.') |
| Year | Возвращает год из значения даты | Year(BDay)=2003 |
| Month | Возвращает месяц из значения даты | Month(BDay)=4 |
| Day | Возвращает дня из значения даты | Day(BDay)=8 |
| Hour | Возвращает часы (24 часовой формат) из значения времени | Hour(MTime)=14 |
| Minute | Возвращает минуты из значения времени | Minute(MTime)=40 |
| Second | Возвращает секунды из значения времени | Second(MTime)=0 |
| GetDate | Возвращает текущую дату и время | BDayTime < GetDate |
| Date | Возвращает дату из значения дата/время | Date(BDayTime) |
| Time | Возвращает время из значения дата/время | Time(BDayTime) |
| LIKE | Частичное сравнение строк | Name LIKE '%PEGAS%' |
| IN | Поиск во множестве значений | Month(BDay) IN (10, 11, 12) |
| * | Частичное сравнение строк | Name = '*AS' |
Q. Зачем и как использовать свойство SchemaName
A. С помощью данного свойства можно управлять какие таблицы и процедуры видны соответствующими компонентами. Например для СУБД Oracle, пользователь имеющий права администратора будет видеть все таблицы всех пользователей (владельцев). Каждый пользователь зарегистрированный в СУБД Oracle получает свою схему для разработки. Имя пользователя и есть именем схемы (SchemaName). Таким образом подключение к базе данных может быть произведено одним пользователем, но с помощью SchemaName можно пользоваться схемой другого пользователя. Если свойство будет заполнено, то имна таблиц и процедур будут выглядеть так:
SchemaName.TableName или SchemaName.ProcedureName
Для СУБД Informix, в данном свойстве можно указать: Имя_базы:владелец_таблицы. И тогда запрос к таблице access с SchemaName=odessa9:informix, будет выглядеть так: select * from odessa9:informix.access.
Q. Как работать с BLOB полями
A. Для работы с BLOB полями посредством технологии dbExpress, Delphi предоставляет два класса TBlobField (и его наследники TGraphicField и TMemoField) и TClientBlobStream (для поддержки метода TCustomClientDataSet.CreateBlobStream).
Для примера создадим в Oracle таблицу:
CREATE TABLE TEST (
ID NUMBER,
BL BLOB,
CL CLOB);
Запись в BLOB поле:
var
Stream: TStream;
begin
with ClientDataSet1 do
begin
Open;
Insert;
FieldByName('ID').AsInteger := 1;
Stream := CreateBlobStream(FieldByName('BL'), bmWrite);
try
Image1.Picture.Bitmap.SaveToStream(Stream);
finally
Stream.Free;
end;
Post;
ApplyUpdates(0);
end;
end;
Важно: Необходимо освобождать поток созданный CreateBlobStream, до вызова метода Post. Так как именно в деструкторе происходит физическая запись данных потока в в буфер TCustomClientDataSet.
или так
var
Stream: TMemoryStream;
begin
with ClientDataSet1 do
begin
Open;
Insert;
FieldByName('ID').AsInteger := 1;
Stream := TMemoryStream.Create;
try
Image1.Picture.Bitmap.SaveToStream(Stream);
TBlobField(FieldByName('BL')).LoadFromStream(Stream);
finally
Stream.Free;
end;
Post;
ApplyUpdates(0);
end;
end;
или так
var
myParams : TParams;
cSQL : string;
FileStream : TFileStream;
begin
SQLConnection1.ExecuteDirect('DELETE FROM BLOBS');
SQLConnection1.ExecuteDirect('INSERT INTO BLOBS(MYNUM, MYID) VALUES(1,
"PEGAS")');
cSQL := 'UPDATE BLOBS SET MYBLOB = :BlobParam WHERE MYNUM = 1';
myParams := TParams.Create;
myParams.Clear;
myParams.CreateParam(ftBlob, 'BlobParam', ptInput);
FileStream := TFileStream.Create('d:\tp38.bmp', fmOpenRead);
myParams.ParamByName('BlobParam').LoadFromStream(FileStream, ftBlob);
FileStream.Free;
SQLConnection1.Execute(cSQL, myParams);
myParams.Free;
end;
Чтение из BLOB поля:
var
Stream: TStream;
begin
with SQLQuery1 do
begin
Close;
Open;
Stream := CreateBlobStream(FieldByName('BL'), bmRead);
try
Image1.Picture.Bitmap.LoadFromStream(Stream);
finally
Stream.Free;
end;
end;
end;
В случае CLOB полей, можно обращаться напрямую к свойству Value поля.
Запись: FieldByName('CL').Value := Memo1.Lines.Text;
Чтение: Memo1.Lines.Text := FieldByName('CL').Value;
Для Informix, описанные выше подходы работоспособны, если:
- Перед работой с LOB полями надо установить глобальную переменную InformixLob в True. Так как по умолчанию DataSnap ресолвер генерит запросы в формате Oracle;
- Типы данных BLOB/CLOB не поддерживаются. Драйвер Borland - ошибка "Illegal attempt to use Text/Byte host variable.", Luxena "Field 'xxx' is unsupported data type 'fixed udt'". Вместо них рекомендуется использовать типы полей BYTE/TEXT.
Если свойство poFetchBlobsOnDemand компонента TDataSetProvider установлено в True, данные BLOB полей не будут передаваться в пакете данных. Для получения всех BLOB полей текущей записи необходимо явно вызвать метод TClientDataSet. FetchBlobs.
Если свойство TClientDataSet.FetchOnDemand установлено в True, то клиентский набор данных вызывает метод FetchBlobs автоматически. Для извлечения BLOB полей вручную следует установить poFetchBlobsOnDemand в True, а FetchOnDemand в False.
Q. Способы развертывания приложения, использующего dbExpress
A. Имеется два способа: в виде автономного
единого EXE файла, либо используя
динамически подключаемые библиотеки DLL.
Для создания автономного файла, необходимо
добавить в оператор USES ссылку на три DCU
файла, находящихся в директории Lib.
| Модуль | Необходимо включить |
| dbExpInt | Если приложение подключается к базе данных InterBase |
| dbExpOra | Если приложение подключается к базе данных Oracle |
| dbExpDb2 | Если приложение подключается к базе данных DB2 |
| dbExpMySQL | Если приложение подключается к базе данных MySQL 3.23.x (dbExpMyS для версии 3.22.x) |
| Crtl, MidasLib | Если приложение dbExpress использует клиентские наборы данных. Лучше подключать всегда, так как без них у Delphi7 - случается внутренняя ошибка компиляции приложения |
Для Informix и MS SQL невозможно создать автономный файла, так как поставляются только *.dll.
Использование динамически подключаемыех
библиотек DLL.
Работу механизма dbExpress в программе
выполняют две библиотеки. Их имена
совпадают с именами DCU файлов.
| Библиотека DLL | Используется |
| dbExpInt.dll | Если приложение подключается к базе данных InterBase |
| dbExpOra.dll | Если приложение подключается к базе данных Oracle |
| dbExpInf.dll | Если приложение подключается к базе данных Informix |
| dbExpMSS.dll | Если приложение подключается к базе данных MS SQL |
| dbExpDb2.dll | Если приложение подключается к базе данных DB2 |
| dbExpMySQL.dll | Если приложение подключается к базе данных MySQL 3.23.x (dbExpMyS.dll для версии 3.22.x) |
| Midas.dll | Если приложение dbExpress использует клиентские наборы данных. Также данную библиотеку необходимо регистрировать в реестре. |
Все дополнительные библиотеки должны находится либо в директории вашей программы, либо в директориях указанных в переменной PATH.
Q. Распространение библиотеки MIDAS.DLL и
совместимость с предыдущими версиями
A. По утверждению Borland наилучшим вариантом
есть поставка MIDAS.DLL из поставки Delphi7.
"Version of MIDAS.DLL from Delphi 7 (7.0.4.453). Fixes many bugs, and is backwards compatible with Delphi 5 and 6
(a claim has been made that TByteField is not backwards compatible, and XML files saved in prior versions need to be regenerated with this
DLL)."
Имеется два пути распространения:
- отдельная поставка библиотеки MIDAS.DLL. Для среды Windows необходимо инсталлировать библиотеку в системный каталог Windows (или system32) и зарегистрировать ( regsvr32.exe MIDAS.DLL). Для Kylix, необходимо инсталлировать в каталог указанный в переменной среды LD_LIBRARY_PATH. Преимущества: уменьшение размеров исполняемых файлов (примерно на 200 КБайт), в среде Windows экономия памяти за счет разовой загрузки MIDAS.DLL и разделения ее всеми запущенными приложениями;
- включение в приложение. Для этого необходимо добавить MidasLib в секцию Uses вашего приложения. После этого MIDAS.DLL не будет загружаться в момент запуска приложения, и соответственно не надо будет поставлять. Преимущества: ликвидация потенциальных проблем перезаписывания библиотеки другими приложениями, более простое распространение, в среде Linux разделяемые библиотеки не разделяются в памяти (каждая копия библиотеки загружается в свой процесс).
Q. Для компиляции полностью автономного
приложения, в раздел Uses был добавлен модуль
dbExpress соответствующей СУБД. Но при
компиляции выдается ошибка "[Fatal Error]
Internal error: L3169"
A. В раздел Uses, необходимо вручную
добавить модули Crtl, MidasLib.
Q. Ошибка "ORA-01722: invalid number"
A. Происходит при попытке записи в базу числовых значений. Такое происходит если разделителем целой и дробной части числа является запятая. Для решения проблемы надо устанавливать параметр сессии NLS_NUMERIC_CHARACTERS='.'.
Q. Ошибка "DBXError: Invalid Field Type", при работе с ORACLE и использования параметров типа число или дата
A. Для решения проблемы необходимо конвертировать все числовые типы полей в тип ftBCD, ftFMTBCD или в ftString. Остальные числовые типы полей драйвером Oracle не поддерживаются (также смотри Ошибка "ORA-01722: invalid number"). Или перейти на драйвер стороннего производителя (например http://www.crlab.com/dbx/dbxoda.exe, trial версия ограничена выборкой 6-и полей).
Пример работы с параметрами:
for i := 0 to Params.Count
- 1 do
begin
case VarType(Params[i].Value) of
varInteger,
varSmallInt,
varShortInt,
varWord,
varByte,
varLongWord:
Params[i].AsString
:= VarToStr(Params[i].Value);
varSingle,
varDouble,
varCurrency:
Params[i].AsBCD
:= StrToCurr(VarToStr(Params[i].Value);
varDate:
Params[i].AsSQLTimeStamp
:= DateTimeToSQLTimeStamp(VarToDateTime(Params[i].Value));
end;
Q. Ошибка "Access violation at address 77FCBF00 in module 'ntdll.dll'. Write of address FB426801"
A. Происходит при использовании драйвера dbexpora.dll (из Delphi 7.0 (Build 4.453), размер файла 171008 байт) в многопоточных приложениях, при открытии и закрытии соединений с базой данных. Данная ошибка проявляется, если установлен клиент Oracle 8.1.6. Для решения данной проблемы рекомендуется обновить версию клиента Oracle до версии 8.1.7 или использовать dbexpora.dll из поставки C++ Builder 6.0 (размер файла 166400 байт).
Q. Ошибка "The ordinal 960 could not be located
in the dynamic link library ISQLT09A.DLL", при подключении к
Informix
A. Необходимо обновить клиента Informix, по
крайней мере до версии 2.70.
Q. Ошибка "dbExpress Error: Invalid Username/Password.", при
подключении к Informix
A. Следует убедиться что в параметрах
соединения "User_Name", "Password" - заданы
корректные значения. К сожалению, данная
ошибка также выдается, и в других случаях.
Также следует убедиться, что корректны
параметры "HostName" (должен
соответствовать значению параметра "Informix
Server", созданного с помощью утилиты Setnet32) и
"DataBase".
Q. Ошибка "Not in transaction" на Informix
A. Это ошибка в поставляемом драйвере (все
таки beta версия). Происходит так как метод
StartTransaction(T), выдает команду COMMIT без BEGIN WORK (на
Oracle это допустимо, на Informix нет). Данная
ошибка обходится вызовом: SQLConnection1.ExecuteDirect('begin
work'); К сожалению придется данную команду
вставлять везде, перед редактированием
наборов данных, так как вызов ApplyUpdates(0)
автоматически посылает COMMIT. На драйвере от
Luxena данной ошибки нет.
Q. Компонент TSQLStoredProc, подключенный к СУБД Informix, в DesignTime показывает пустой список процедур в свойстве StoredProcName
A. Драйвер поставляемый Borland ориентирован на 9-ю версию Informix. Поэтому на 7-ой версии, список процедур недоступен. Следует использовать альтернативные драйвера для Informix. Возможно при динамическом создании данного класса, и заполнения его свойств - все будет работать.
Q. Ошибка "Error loading MIDAS.DLL"
A. Если приложение использует дополнительные DLL, то при первом запуске приложения в реестре добавляются ссылки на файл Midas.dll. При перемещении или удалении файла из данного места все приложения использующие Midas.dll перестают работать. Для восстановления работоспособности необходимо вручную удалить данные записи из реестра или выполнить повторную регистрацию: regsvr32.exe Midas.dll.
Q. Ошибка "Operation not applicable"
A. Данная ошибка проявляется, если на компьютере зарегистрирована версия Midas.dll поставляемая например с Delphi5 (другие приложения разработанные в Delphi5 или установлена среда Delphi5), а приложение было разработано на Delphi7. Так как Midas.dll реализует com-сервер, который регистрируется в реестре, то единственным выходом является переход к использованию Midas.dll поставляемой с Delphi7. Рекомендуется скопировать/заменить файл Midas.dll расположенный в каталоге %SystemRoot%\System32. А также выполнить регистрацию: regsvr32.exe Midas.dll.
Q. Ошибка "dbExpress Error: Operation Not Supported", при вызове TSQLQuery.RecordCount.
A. Для драйвера Oracle, также предшествует ошибка "ORA-00942: table or view does not exists". Имена таблиц рекомендуется брать в двойные кавычки и писать заглавными буквами, так как имена чувствительны к регистру. Так как на Informix такое не наблюдается, то есть вероятность, что это ошибка драйвера, и возможно в дальнейшем будет исправлено.
Также нельзя пользоваться свойством RecordCount, если:
- Запрос представляет собой хранимую процедуру;
- Запрос представляет собой параметризированный запрос;
- Запрос представляет собой соединение
нескольких таблиц.
Q. Ошибка "List index out of bounds (-1)"
A. Выдается компонентом TClientDataSet при присвоении ему данных, которые были получены при использовании аналитических функций Oracle 8i производящих неявную сортировку данных. Для решения проблемы, необходимо установить свойство poRetainServerOrder в True у TDataSetProvider.
Q. Как можно понять, ошибка ли в настройке свойств драйвера или это его "фича"
A. Это легко сделать с помощью компонента TSQLMonitor. С его помощью можно контролировать все операторы SQL, передаваемые компонентом SQLConnection серверу баз данных. Информация может быть сохранена в файл, компонент TMemo, или обработана любым другим способом. Аналогом для BDE была утилита SQL Monitor.
На примере вопроса "Компонент TSQLStoredProc, подключенный к СУБД Informix, в DesignTime показывает пустой список процедур в свойстве StoredProcName", выясняется что на сервер Informix посылается запрос вида:
select 0, '', t.owner, t.procname, t.isproc, t.numargs, t.numargs, t.procid from informix.sysprocedures t where ( ( t.procid > 175) ) order by t.procname ASC, t.procid ASC
Из него видно что из системной таблицы informix.sysprocedures запрашивается поле isproc - которое на 7-ой версии не существовало, поэтому запрос оказывается не работоспособным.
Copyright © 2004 Алексей Макаренко&Олег Перехрест Специально для Delphi Plus
| 2011 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2010 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2009 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2008 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2007 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2006 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2005 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2004 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2003 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2002 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2001 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 2000 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
| 1999 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
- Компания по разработке программного обеспечения
- Услуги аутсорсинга в области программирования
- Как продлить срок службы картриджей
- Мошенничество во Всемирной Паутине. Осторожно: фишинг!
- Web-студия
- Как легально поднять уровень индекса цитирования.
- Мы реально сможем помочь вам в управлении предприятием
- Создание сайтов – популяризация вашего замысла
- Свой сайт. Управление ресурсом
- Семантическое ядро сайта или правила подбора ключевых фраз
- Программирование в среде Delphi 8 for .NET
- Практикум по Delphi для решения прикладных задач
- Фундаментальные алгоритмы и структуры данных в Delphi
- Delphi 6. Программирование на Object Pascal
- Delphi и технология COM
- Delphi в шутку и всерьез: что умеют хакеры
- Программирование в Delphi глазами хакера
- Delphi 2005. Секреты программирования
- Искусство создания компонентов Delphi
- Приемы программирования в Delphi на основе VCL
- Программирование баз данных в Delphi 7
- Программирование баз данных в Delphi
- Программирование в среде Delphi
- Программирование в Delphi 7
- Язык SQL в Delphi 5