Добавить в избранное   Сделать стартовой   Главная   E-mail   Форум   Мой блог 
   
Cертификации

Errors

ETL

FAQ (по темам)

GIS

Web

wiki

Администрирование

Безопасность

Книги
Oracle, ...

Новости

ОС

Программирование

Проектирование БД

Производительность

Скачать

Советы

Тестирование

Установка

FAQ - по базам данных
FAQ - по базам данных
Установка СУБД
Oracle
Sybase
MySQL
PostgreSQL
MS SQL Server
Interbase, Firebird
Другие DB
Администрирование
Oracle
MySQL
Sybase
PostgreSQL
MS SQL Server
Interbase, Firebird
IBM DB2
Другие DB
Проектирование БД
Статьи
ETL
Теория БД
ErWin
Designer 2000
PowerDesigner
Хранилища данных
CASE средства
OLAP
Бизнес - анализ (BI)
Производительность
Oracle
MSSQL
Interbase, Firebird
IBM DB2
MySQL
PostgreSQL
SYBASE
Безопасность БД
Oracle
MS SQL Server
Инъекция SQL
Программирование
Transact-SQL
PL/SQL
C++
XML
SQL
PostgreSQL
MDX
Java
VBA Excel
Книги по базам
Oracle
Заказ книг
ОС
Установка и настройка
UBUNTU
ОС
Установка и настройка
UBUNTU
FAQ
FAQ - по базам данных

Пособие по Btrieve для SQL-программистов.

Печать E-mail

Введение. Данный документ - краткая инструкция по использованию в DELPHI прямых обращений к ядру CУБД Btrieve, написанная для людей с SQL-мышлением, которым судьба-индейка подсунула свинью в виде производственной необходимости временно (или, не дай Создатель, постоянно) использовать это чудо Pervasive-вской мысли. Мне самому пришлось долго и больно перестраиваться с незамутненных SQL-понятий работы с данными на суровую логику программирования более низкого уровня. Данная шпаргалка призвана облегчить этот процесс, и содержит параллели между основными SQL-командами (select/insert/update/delete) и вызовами функции BTRVID, описанной по принципу "черного ящика". Сразу скажу, что не являюсь специалистом по Btrievу (т.е. могу путаться в Btrievовских терминах), большая часть информации получена эмпирическим путем, все примеры использовались в жизни, отлажены и работают.

Отдельно замечу следующее. Вообще говоря, в природе существует Pervasive ODBC, его заменитель Titan Btrieve, и, возможно, кое-что еще, что позволяет обращаться к Btrieve-данным через SQL-запросы. Однако во-первых, это дело работает на порядок медленнее, а во-вторых, в определенных ситуациях (например в моей - мне пришлось организовывать перекачку данных из Informix SQL-сервера в программу "Парус", использующую Btrieve. С Informixом, сами понимаете, никаких проблем не было) из-за специфического формата данных, определяемого конкретным программным продуктом, использование ODBC порой оказывается в принципе невозможным. Например, его (ODBC) приводят в смятение нулевые байты в текстовых полях. И последнее. По правильному, для работы с Btrievом нужно устанавливать Btrievовский Engine, пытаться добиться от него стабильной работы, поганить реестр и все такое. (Может, конечно, это со мной что-то не так, но мне пришлось через это пройти. Хотя на моей работе тратить дни на то, чтобы заставить продукт проявить заявленные возможности считается непозволительной роскошью). Вообще говоря, можно ограничиться набором DLL-библиотек из Pervasive.SQL 2000 Workgroup Engine, которые достаточно переписать в директорию с приложением, или в директорию, прописанную в путях, чтобы можно было работать с Btrievом. Ну - удачи! 

Необходимое окружение. Прежде всего в директорию, содержащую исходники Delphi-приложения, нужно поместить вышеупомянутые DLL и файлы Btrapi32.pas и BtrConst.pas из Pervasive SDK 2000 и в uses прописать BtrConst и BtrApi32. Если кому интересно назначение этих файлов, может посмотреть сам - там все достаточно прозрачно. Затем в type нужно прописать следующие два типа:

CLIENT_ID = packed record
    networkandnode : array[1..12] of char;
    applicationID : array[1..3] of char;
    threadID : smallint;
end;

VERSION_STRUCT = packed record
    version : smallint;
    revision : smallint;
    MKDEId : char;
end;

а в var следующие переменные:

client : CLIENT_ID; {вручную не переинициализировать!}
versionBuffer : array[1..3] of VERSION_STRUCT; {вручную не переинициализировать!}
status : smallint; {вручную не переинициализировать!}
posBlock : string[128]; {вручную не переинициализировать!}
dataBuffer : array[0..255] of char;
keyBuf : string[255];
keyNum : smallint;
dataLen : word;

Физический смысл большинства из этих переменных мне ясен смутно, но он и не важен. Будем считать их частью черного ящика. Переменные, помеченные комментарием {ВРУЧНУЮ НЕ ПЕРЕИНИЦИАЛИЗИРОВАТЬ!} являются системными, Btrieve сам их как-то заполняет и потом с ними работает. Остальные же несут разную смысловую нагрузку в зависимости от действия.

Подключение к Btrieve-БД (SQL: Database & Connect в одном флаконе). Чтобы производить любые операции с данными Btrieve, нужно в первую очередь подконнектиться к базе. Чтобы подключиться, нужно выполнить следующий фрагмент кода:

fillchar(keyBuf, sizeof(keyBuf), #0);
keybuf := '<Полный путь к любому из *.btr файлов в директории, где лежит БД>' + #0;
fillchar(client.networkAndNode, sizeof(client.networkAndNode), #0);
client.applicationID := 'MT' + #0; {так надо}
сlient.threadID := 50; {так надо}
fillchar(versionBuffer, sizeof(versionBuffer), #0);
dataLen := sizeof(versionBuffer);
status := BTRVID(B_VERSION, {системная константа}
    posBlock, {системная}
    versionBuffer, {системная}
    dataLen, {см.выше}
    keyBuf[1], {см.выше}
    0,
    client); {системная}

if status = B_NO_ERROR значит подконнектилось успешно, иначе - по каким-то причинам не получилось. Причины (из известных) могут быть следующие - приложение не нашло необходимые DLL-ки, неправильно указан путь в keybuf, кривой btr-файл. Подробно ошибки описаны в Pervasive SDK 2000 хэлпах.

Отключение от Btrieve-БД (SQL: Disconnect & Close database).
dataLenHead := 0;
status := BTRVID( B_STOP, {системная константа}
    posBlock, {системная}
    DataBuffer, {см.выше}
    dataLen, {см.выше}
    keyBuf[1], {см.выше - предыдущий раздел}
    0, {}
    client ); {системная}


Желательно в конце работы это делать. Во избежание.

Открытие таблицы(SQL: Аналог отсутствует). Для выполнения любой операции с таблицей (select/insert/update/delete) необходимо сначала ее открыть. Следующий фрагмент открывает одну отдельно взятую таблицу:

fillchar(keyBuf, sizeof(keyBuf), #0);
keybuf := '<Полный путь к *.btr файлу, где лежит таблица>' + #0;
fillchar(dataBuffer, sizeof(dataBuffer), #0);
dataLen := 0;
status := BTRVID(B_OPEN, {системная константа}
    PosBlock, {системная}
    dataBuffer, {см.выше}
    dataLen, {см.выше}
    keyBuf[1], {см.выше}
    0,
    client) {системная}

Если status != B_NO_ERROR, значит, что-то не сложилось. Или файл кривой, или путь неправильный, или с ним уже кто-то работает, или таблица уже открыта, или см. ошибки. Вообще говоря, можно открывать сразу несколько таблиц, и одновременно с ними работать. Но в этом случае для корректной работы нужно для каждой таблицы завести свой набор переменных

posBlock,
dataBuffer,
keyBuf,
keyNum,
dataLen

Наверное, можно даже применить массивы.

Закрытие таблицы(SQL: Аналог отсутствует). Все то же самое, как в открытии таблицы. Единственное отличие - B_CLOSE вместо B_OPEN. Для вящей корректности после окончания работ с таблицей нужно ее закрыть.

Добавление записи(SQL: INSERT). Для работы с конкретной таблицей необходимо:

1) Чтобы на эту таблицу было наложено заклинание эксклюзивного доступа... (Sorry, пиво...) Продолжаю на трезвую голову. Так вот: для работы с таблицей на самом деле необходимо следующее-
1) Должна быть описана структура таблицы. Например (таблица хозяйственных операций "Паруса"):

type
OPERHEAD_STRUCT = packed record
ISN : integer;
opDate : array[0..8] of char;
agnFrom : array[0..15] of char;
agnTo : array[0..15] of char;
docType : array[0..5] of char;
docNumb : array[0..12] of char;
docDate : array[0..8] of char;
basType : array[0..5] of char;
basNumb : array[0..12] of char;
basDate : array[0..8] of char; {см. ссылку в тексте}
resSum : double;
spMark : array[0..5] of char;
opCont : array[0..80] of char;
invSign : smallint;
subAgn : array[0..15] of char;
appName : array[0..8] of char;
appIsn : longint;
resMSum : double; {см. ссылку в тексте}
sysNumb : array[0..3] of char;
regNumb : longint;
opMngCnt : array[0..80] of char;
end;

var
OperheadRecord : OPERHEAD_STRUCT;

Важно! Для корректной работы должен быть соблюден размер полей в байтах. Через Database Explorer можно посмотреть структуру Btrieve-таблицы, с которой нужно работать. Там мы увидим, что поле resMSum, например, типа FLOAT и размером 8 байт. Этим размером и свойствами в Delphi обладает тип double. Поля типа CHAR в Btrieve имеют две характеристики - логическую длину и физическую (на байт больше). Это также видно в DBE. Поле basDate, например, логически длиной 9 байт, а физически - 10. Нужно исходить из логической длины, и поля типа CHAR описывать как начинающиеся с нуля массивы of char. Поле basDate в этом случае представляется как массив 0..8, т.е. 9 байт.

2) Данная запись должна быть проинициализирована. Вообще Btrieve все аналоги SQL-операций может выполнять только с записью целиком. Поэтому перед каждым "INSERT"ом или "UPDATE"ом нужно, чтобы все поля записи были заполнены нужным образом. Специфика заполнения такова:

а) Текстовые поля:
StrPCopy(OperheadRecord.agnto, AsString со значением + #0);
    { собственно присвоение значения }
CharToOem(OperheadRecord.agnto,OperheadRecord.agnto);
    { конвертация - если необходимо. CharToOem всего лишь одна из набора }
    { конвертационных функций из MustDie SDK }


б) Числовые поля:
OperheadRecord.resmsum := AsFloat со значением;

Здесь главное - совместимость типов. Достаточно легко определяется.

в) Автоинкременты:
fillchar(OperheadRecord.isn , SizeOf(OperheadRecord.isn), #0);
Независимо от типа. Просто заполняем их нулевыми байтами, Btrieve сам присвоит нужное значене.

3) Собственно INSERT :
dataLen := sizeof(OPERHEAD_STRUCT);
status := BTRVID(B_INSERT, {системная константа}
    PosBlock, {системная}
    OperheadRecord, {собственно запись - см.выше}
    dataLen, {см.выше}
    keyBuf, {путь к таблице - см.выше}
    -1,
    client) ; {системная}

Статус, понятно, должен оказаться равен B_NO_ERROR.

Позиционирование/Поиск(SQL: SELECT & FETCH в одном флаконе). Я для работы обошелся всего тремя опциями - B_GET_EQUAL, B_GET_FIRST, B_GET_NEXT. Вообще их там больше. Но поскольку даже в совокупности они все равно не заменят всю прелесть SQL, лучше потратить силы не на изучение всяких нюансов сомнительной ценности, а на то, чтобы избежать использования Btrieve в принципе :-). Для начала - общая информация. B_GET_EQUAL предназначен для выборки одной записи из таблицы. Если результатом запроса будет выборка из более чем одной записи, он вернет первую и на этом успокоится. B_GET_FIRST инициализирует выборку из более чем одной записи, и возвращает первую из выборки (скажу сразу - сортировка мне была не нужна, насчет нее ничего не знаю.). Если дальше мы будем выполнять B_GET_NEXT, то за каждое выполнение будем получать по одной следующей записи из выборки. Причем если упрется в конец файла, то выдаст status с кодом 9. В противном случае похоже, что просто пойдет дальше по файлу, несмотря на то, что начиная с какой-то записи все последующие уже не будут отвечать условиям выборки. Скажу сразу, что здесь я не уверен до конца в своих знаниях, могу ошибаться. Короче, у меня в программе в WHILE с GET_NEXTами стоит проверка сразу двух условий - чтобы status был B_NO_ERROR и чтобы полученные значения условиям выборки отвечали. Хотя бы одно не выполнилось - из WHILE выхожу. Пока работает. И последнее. Все эти GETы могут искать только по полям, описанным в индексах таблицы. Свое мнение об этом деликатно опускаю. Теперь - собственно описание синтаксиса. Для того, чтобы выполнить B_GET_EQUAL, нужно:

а) Описать структуру индексов. Например, в уже упомянутой таблице OPERHEAD есть следующие индексы:

type
OPERHEAD_INDEX7 = packed record
    appIsn : longint;
    appName : array[0..8] of char;
end;
OPERHEAD_INDEX0 = packed record
    ISN : integer;
end;

var
OperHeadIndex0 : OPERHEAD_INDEX0; {индекс номер 0}
OperHeadIndex7 : OPERHEAD_INDEX7; {индекс номер 7}


Информацию об индексаx можно посмотреть в том же DBE.

б) Далее - код:

OperheadIndex0.isn := значение AsInteger; {присвоение параметров поиска}
fillchar(OperheadRecord , SizeOf(OperheadRecord), #0);
dataLen := sizeof(OPERHEAD_STRUCT);
status := BTRVID(B_GET_EQUAL, {системная константа}
    PosBlock, {системная}
    OperheadRecord, {сюда будет возвращен результат поиска}
    dataLen, {см.выше}
    OperheadIndex0, {см.выше}
    0, {номер индеска - см.выше}
    client); {системная}


Если status <> B_NO_ERROR and <> B_KEY_VALUE_NOT_FOUND - значит, чего-то нашлось.

Использование других GETов осуществляется по этой же схеме. Единственная разница - вместо B_GET_EQUAL нужно поставить B_GET_FIRST или B_GET_NEXT. Важно! B_GET_EQUAL может использовать только уникальные индексы! B_GET_FIRST и B_GET_NEXT - любые.

Обновление записи(SQL: UPDATE). Самое главное - здесь рассмотрен вариант обновления одной записи. У меня сложилось впечатление, что вообще в Btrieve можно обновлять и удалять только по одной записи, но поскольку у меня не хватило терпения изучить HELP до конца, наверняка утверждать не могу. Итак: Непосредственно перед действием обновления у нас должен быть выполнен B_GET_EQUAL, который спозиционирует указатель в таблице именно на ту запись, которую мы будем апдейтить. ( У меня есть подозрение, что информация о позиционировании хранится в PosBlockе. Впрочем, какая разница?) Далее мы должны полностью заполнить запись таблицы (в нашем случае - OperheadRecord). Вообще говоря, поскольку только что выполнился B_GET_EQUAL, OperheadRecord у нас уже заполнен. Мы можем просто переприсвоить те поля, какие хотим изменить. Специфику заполнения см. в разделе, посвященном INSERTу. Далее - код:

dataLen := SizeOf(OPERHEAD_STRUCT);
status := BTRVID(B_UPDATE, {системная константа}
    posBlock, {системная}
    OperheadRecord, {см.выше}
    dataLen, {см.выше}
    keyBuf, {путь к таблице - см.выше}
    -1,
    client) {системная}


Status, сами понимаете, должен оказаться B_NO_ERROR. Если все в порядке.

Удаление записи(SQL: DELETE). Использование практически совпадает с B_UPDATE. В смысле, сначала должен быть B_GET_EQUAL и все такое. Фрагмент кода немного отличается - B_DELETE вместо B_UPDATE, и DataBuffer вместо OperheadRecord - какой смысл передавать данные, если запись сейчас будет удалена?

fillchar(dataBuffer, sizeof(dataBuffer), #0);
dataLen := SizeOf(OPERHEAD_STRUCT);
Status := BTRVID(B_DELETE, {системная константа}
    PosBlock, {системная}
    DataBuffer, {см.выше}
    dataLen, {см.выше}
    KeyBuf, {путь к таблице - см.выше}
    -1,
    client) {системная}


Про status уже и не говорю.

Заключение. Вот, собственно и все, что мне известно. В принципе, в разрозненном виде это все есть и в Pervasive SDK 2000 Help, и в Инете, но мне, например, понадобилось две недели, чтобы вычленить именно эту информацию, абстрагироваться от ненужной, и c прискорбием убедиться в несостоятельности обычного Pervasive ODBC. Может, данный документ кому-то сэкономит время. Хотя лучше всего, если эта информация вам не понадобится вовсе!

 
 
« Пред.   След. »
     

Последние добавленные статьи
Поиск
Ссылки
Главная
Скачать
Курсы
Роль АБД (SYSDBA)
Карта сайта
Автостекла
Контакты
Войти на сайт
Популярные статьи
Online - тесты
1Z0-042
Rambler's Top100 МЕТА - Украина. Рейтинг сайтов хостинг от freehost.com.ua

Все права защищены.SYSDBA 2010 | Если у Вас есть хороший материал пришлите его нам.