Причини переходу від BDE до ADO


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

Отже, для зберігання атрибутивної інформації був обраний формат MS Access,
який має та обставина, що всі таблиці зберігаються в одному файлі (в
відміну від Paradox і Dbase) і не вимагає при цьому запущеного сервера, як, до
Приміром, Interbase. Необхідна також зв'язок з файлами форматів dbf і db для
завантаження / вивантаження даних до / з БД. Для написання програми ми використовуємо Delphi
4, а для підключення до файлів БД використано BDE. І все це було чудово, але
от з'явилися дві важливі обставини:

Var
AStream: TBLOBStream;
Data: Integer;
Begin
/ / Відкриваємо таблицю (звичайний TTable)
ATable.Open;
/ / Створюємо потік.
AStream: = TBLOBStream (ATable.CreateBLOBStream (ATable.FieldByName ("Поле ")));
/ / Що-небудь читаємо з нього.
AStream.Read(Data, SizeOf(Data));
/ / Звільняємо потік і закриваємо таблицю.
AStream.Free;
ATable.Close;
End;

Здавалося б – абсолютно тривіальний код. АЛЕ! Рядок, де проводиться читання
з потоку, викликає виняткову ситуацію – "External error – EEFFACE". І в
вихідних текстах VCL від Delphi 5 ми знаходимо приголомшливе пояснення – це,
виявляється, "C + + Exception". Цікаво, а при чому тут C + +? Єдина відповідь,
який я знаю, – Delphi написана на C + +.

Плюс до всього, якщо ви запускаєте цю програму з-під Delphi – помилка не
виникає, а якщо запускаєте її прямо в Windows – помилка буде неодмінно. Я
дійшов у своїх пошуках до викликів прямих викликів BDE API – ось там-то помилка і
виникає, так що я думаю тут чергова помилка BDE, хоча я використав на той
момент саму останню версію з сайту Inprise – BDE 5.11.

Так що, панове, якщо Ви використовуєте щось подібне у своїх програмах, то
знайте, що під Windows NT 4.0/Windows 2000 Ваші програми працювати не будуть.
Найцікавіше, що компоненти з бібліотеки VCL, які використовують подібний
метод для отримання даних (наприклад, TDBRichEdit) теж не працюють!

Отже, цих двох причин виявилося достатньо для нашої фірми, щоб почати
перехід від BDE до ADO.

ADO і файли формату MS Access



-Учителю, чому ти обдурив мене? Ти сказав, що Вейдер зрадив і вбив мого
батька, а тепер виявилося, що він і є мій батько!

-Твій батько: Його спокусила темна сторона сили. Він більше не був Анекіном
Скайукером і став Дартом Вейдером. Тому хороша людина, який був твоїм
батьком, був знищений. Так що, те, що я тобі сказав, було правдою: з
певної точки зору:

-З певної точки зору?

-Люк: ти ось побачиш сам: що дуже багато істин залежать від нашої точки
зору.
(Зоряні війни. Епізод 6.)


До чого я навів цю цитату – в результаті всієї цієї роботи я прийшов до висновку,
що у нас, програмістів, і у Microsoft різний погляд на фразу "Забезпечується
доступ до даних ". Ми (ну або, принаймні, я) у цій фразі бачимо наступне
зміст "забезпечується доступ до даних для їх перегляду і редагування
(Тобто редагування, видалення і додавання нових даних) ". Що має на увазі
Microsoft можна тільки здогадуватися, але явно, що без особливих проблем досягається
тільки перегляд даних. Крім того, практично всі приклади в літературі
обмежуються отриманням даних саме для перегляду, після чого слід
кілька бадьорих фраз і все закінчується. Як говориться вище – різні точки
зору:

Отже, перш за все, робота була обмежена умовою розробки в Delphi 4.
Причин цього багато, але до цієї статті це відношення не має. Просто – програма,
розроблена в Delphi 4 повинна працювати через ADO. Тому приступили до пошуку
компонент, що забезпечують таку роботу. Знайшли їх досить багато, як платних,
так і безкоштовних. Все, що буде написано, однаково і для всіх варіантів і навіть
для Delphi5. Виняток становить тільки робота з закладками в Delphi 5.

ADO була взята на той момент сама остання версія з сайту Microsoft – це
ADO 2.6.

Отже, візьмемо файл mdb формату MS Access 97. Його можна зробити з допомогою хоча
б самого Access. І створимо там невелику таблицю, наприклад, таку:

Object_ID Integer – ідентифікатор об'єкта на карті
Object_Description Text (50) – опис об'єкта на карті


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

Ніби як все нормально і доступ до даних ми отримали.

А тепер давайте, уявимо себе користувачами і спробуємо що-небудь
виправити або додати. Наприклад, додамо кілька порожніх записів і спробуємо
внести туди дані. Додаємо. Нормально. Тепер внесемо дані і натиснемо POST. І
що ми бачимо?




Ага. Цікаво, а при чому тут ключ, якщо у нас на таблицю ключ не накладено?
Пробуємо додати новий запис, видалити запис без Object_ID. Результат однаковий
– Все те саме повідомлення про помилку. І що ж робити? Запускаємо MS Access, пробуємо
там, і бачимо, що там все відмінно. Стало бути, що щось не так ми робимо з ADO. І
тут ми згадуємо, що коли ми створювали таблицю в MS Access, він пропонував
створити ключові поля для цієї таблиці. А після довгих пошуків в ADO SDK я знайшов
цьому таке пояснення: ADO припускає, що таблиця буде в першій нормальній
формі. Якщо хто не пам'ятає головна вимога першої форми – відсутність
повторюваних записів.

У даному випадку ми не можемо створити ключ на те, що є. Що ж робити? І
тут приходить на розум просте рішення: додамо ще одне поле, щоб кожний запис
була однозначно визначена (тобто якийсь внутрішній ідентифікатор). Щоб не
думати про вміст цього нового поля, робимо зовсім просто – нехай це буде
автоінкрементні полі, і створимо на нього первинний ключ. Дуже добре! Робимо, – все
працює. Поки ми не додаємо більше одного запису. Якщо ми їх додамо поспіль
декілька, ми побачимо дуже цікаву ситуацію як на картинці.



Що тут цікавого? А те, що вміст Internal_ID для всіх цих записів
дорівнює нулю, хоча це автоінкрементні полі! І Table.Refresh тут не допомагає!
Тільки закриття і подальше відкриття таблиці призводить до того, що ми бачимо те,
що і очікувалося.



А поки ми не маємо правильних ідентифікаторів, наявність такого поля не дає
нічого. Вище наведені помилки будуть продовжувати сипатися як з рогу достатку.
Але от тільки закривати – відкривати таблицю щоразу після додавання нової
запису для того, щоб автоінкрементні поле приймало правильні значення – це
сильно. Так не піде. Ось так ADO, подумав я, а давай-но спробуємо MS Access
2000. І тут виявилося, що там все нормально працює: додаємо запис, робимо
збереження (Post) автоінкрементні поле відразу приймає правильне значення.

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

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

Ну а щоб користувач не бачив цього внутрішнього ідентифікатора (адже він
потрібен тільки нам) робимо це поле невидимим. Сподіваюся, що всі знають, що це
робиться через TField.Visible: = FALSE.

Хто-небудь може заперечити: а навіщо нам такий ідентифікатор, ми можемо запису
ідентифікувати з яких-небудь своїх полях. Заради Бога! Але тут є ще одна
проблема і ця проблема називається закладки.

Проблеми закладок немає в Delphi 5, тому що там навколо Bookmark зроблений клас
ними керуючий, а я маю на увазі роботу із закладками через ADO. Дивимося знову ж таки
в ADO SDK і бачимо там такий опис:

“Recordset.Bookmark: Встановлює або повертає закладку, яка
однозначно визначає поточну запис у Recordset. При створенні або відкритті
об'єкта Recordset кожна з його записів отримує унікальну закладку. Для того
щоб запам'ятати положення поточного запису, слід присвоїти поточне значення
властивості Bookmark змінної. Для швидкого повернення до збереженої у
змінної вказівником поточного запису в будь-який час після переходу на іншу
запис слід вказати у значенні властивості Bookmark об'єкта Recordset значення
цієї змінної ".

Здавалося б, які проблеми? А ось які: значення, що повертається завжди одне і
теж для будь-якого запису. І коли ми встановлюємо цей, з дозволу сказати,
Bookmark, нічого не відбувається. І тільки наш внутрішній ідентифікатор допоможе в
такій ситуації, крім того, його значення завжди має сенс, навіть після закриття
і повторного відкриття таблиці, що, загалом-то, зручно.

Після того як все запрацювало, я вирішив перевірити швидкість роботи ADO. У нас
може бути ситуації, коли в таблицю додається відразу велика кількість
записів, наприклад, 50-60 тисяч записів за раз. Так ось, коли використовувалася
BDE, така операція займала максимум 10 хвилин. Вгадайте, чому стало одно це
час при використанні ADO? Мінімум 25 хвилин на тій же самій машині. Якщо після
цього мені будуть говорити, що ADO швидше BDE мало не в 2 рази – дозвольте
мені з Вами не погодитися.

Отже, для нормальної роботи ми повинні мати таблиці в першій нормальній
формі, для цього робимо автоінкрементні поле з унікальним індексом. Крім того,
якщо ми можемо додавати більше одного запису за один раз і потім відразу можливо
будемо їх редагувати, нам треба використовувати файли MS Access 2000.

ADO і файли xBASE і Paradox


Отже, ми змогли налагодити роботу через ADO до файлів формату MS Access. Але ж
ми можемо і повинні використовувати файли xBase і Paradox як обмінних файлів.

Спробуємо це зробити. Всі приклади які я бачив у книгах працюють однаково
– Через "Microsoft OLE DB provider for ODBC". А всі редактори, які роблять
рядок підключення, завжди показують тільки mdb файли в діалозі, в якому
задається шлях до файлу БД. Щось тут нечисто, подумав я – а як же той же самий
Access це робить? Адже явно не через ODBC, отже, є якась хитрість.

Після приблизно тижневих пошуків в Інтернеті рішення було знайдено. Так,
дійсно можна використовувати "Microsoft Jet 4.0 OLE DB Provider". Щоб не
розповідати довго, уявімо, що у нас на диску D у корені лежить файл Test.dbf
формату dBase 5.0.

Рядок конекту для цього випадку буде виглядати так:

"Provider = Microsoft.Jet.OLEDB.4.0; Data Source = D:; Extended Properties = dBase 5.0;
mode=Read|Write|Share Deny None;Persist Security Info=True”;

І це все.

Найцікавіше в усій це рядку – секція "Extended Properties".

Щоб знати, що конкретно для різних форматів треба писати в Extended
properties, загляньте до реєстру Windows на наступну гілку:

HKEY_LOCAL_MACHINESoftwareMicrosoftJet4.0ISAM Formats

Там перераховані всі підтримувані в даному випадку формати.

Після дослідів над форматом dbf виявилося, що все вище сказане для формату
mdb абсолютно не відноситься до цього формату – і всі вимоги про першу форму
можна і не дотримуватися! Загалом, загадка природи.

А ось формат Paradox – це виявилася пісня на менша, ніж mdb. І ось чому
– Тут всі вимоги про першу формі таблиці в дії, але ж ми не можемо
створювати таблицю, потім говорити користувачеві "Чуєш, мужик, а тепер метнувся,
запустив Paradox і створив первинний ключ на цю таблицю. А потім натиснеш на ОК і
ми продовжимо ". Це несерйозно. Стало бути, цей ключ треба створювати нам самим.

Добре, запускаємо довідку за MS Jet SQL і шукаємо розділ створення індексів або
первинних ключів. Знаходимо таке:

CREATE INDEX ім'я_індексу ON названіе_табліци (названіе_поля) WITH PRIMARY.
ALTER TABLE названіе_табліци ADD CONSTRAINT імя_ограніченія PRIMARY KEY (названіе_поля)

Всі далі сказане абсолютно однаково для обох варіантів.

Припустимо, що наша таблиця називається ExpTbl.db і поле, на яке ми
хочемо накласти первинний ключ, називається IntrernalID. Добре, підключаємося до
таблиці і задаємо такий рядок SQL для виконання:

CREATE INDEX My_Index ON ExpTable (InternalID) WITH PRIMARY


Запустимо на виконання. Ого, а що це ми бачимо? Ось ті на – чергове
повідомлення про помилку. При цьому повідомлення як завжди дуже змістовне
стосовно до нашого випадку.



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

Висновок один – знову чергову вимогу ADO, яке одразу не зрозумієш. Гаразд,
запускаємо он-лайн MS MSDN і робимо запит на PARADOX. Бачимо щось близько 50
документів. І десь в 35-36 документі я знайшов відповідь маленькими літерами внизу
екрану! Зараз я вам скажу в чому проблема – тримайтеся міцніше: ім'я первинного
ключа повинно збігатися з назвою таблиці, а імена індексів з іменами полів.
Неслабо.

Виправляємо SQL:

CREATE INDEX ExpTable ON ExpTable (InternalID) WITH PRIMARY

Запускаємо, дивимося – все відмінно.

Щоб ніхто більше мучився з цією справою, я хотів би навести самі значущі
обмеження для драйвера PARADOX, які я знайшов в MSDN:


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

Отже, для роботи через ADO з файлами xBase або Paradox, нам необхідно
вказувати потрібний драйвер у секції Extended Properties і в секції Data Source
тільки шлях до файлу. Для xBase на цьому всі труднощі закінчені, а от для
Paradox необхідно завдання первинного ключа як для формату MS Access, при цьому
є певні обмеження при завданні назв ключів, так само як і
можливих індексів.

Те, про що мова піде далі вже не відноситься до організації роботи з таблицями
xBase і Paradox через ADO, а скоріше згадка про один корисному досвіді.

Для додавання даних в ці таблиці, ми можемо вставляти їх по одній
(Table.Append (Insert); Table.Post), а можемо скористатися варіантом SELECT:
INTO, INSERT: INTO. Поговоримо тепер саме про другий варіант роботи.

Дивимося файл довідки MS Jet SQL.

SELECT поле_1 [, поле_2 [, …]] INTO новаяТабліца [IN внешняяБазаДанних]
FROM джерело

Гаразд, пробуємо. Нехай ми маємо в якості джерела даних mdb файл і хочемо
зберегти дані з таблиці SourceTable в таблицю формату Paradox 7.0
TestTable.db, розташовану в корені диска D:.

Здавалося б:

SELECT * INTO [TestTable.DB] IN “D:” FROM Source Table

Ні, чергова помилка. Ось, що ми бачимо.



Ага, добре, давайте спробуємо вказати таблицю в дорозі:

SELECT * INTO [TestTable] IN “D: TestTable.DB”
FROM Source Table

Одержимо чергове повідомлення про помилку.



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

SELECT * INTO [Paradox 7.x; DATABASE = D:]. [TestTable # DB] FROM SourceTable

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

Найбільш приголомшливе це назва розділу MSDN, де я знайшов цю відповідь – "Як,
використовуючи ADO, відкрити таблицю Paradox, захищену паролем ". Як ЦЕ має
ставлення до цього синтаксису SQL, я так і не зрозумів, чесно кажучи.

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

При написанні статті використано матеріали:

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


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

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

Ваш отзыв

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

*

*