-Музыка

 -Подписка по e-mail

 

 -Поиск по дневнику

Поиск сообщений в xpackpackax

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 09.01.2009
Записей:
Комментариев:
Написано: 1835





dip-shop.ru

Пятница, 09 Октября 2009 г. 15:47 + в цитатник
dip-shop.ru - интернет ресурс, с помощью которого вы найдете любую научную работу.
Можете заказать реферат, курсовую, диплом или даже диссертацию, а можете купить уже готовую работу. В любом случае она будет выполнена на высоком уровне.
Все заказы выполняются с учетом пожеланий клиента. Также вы можете заказать переработку уже готового научного проекта с учетом требований вашего руководителя.
До тех пор, пока работа не будет принята, вы можете смело обращаться к исполнителям за доработкой.
магазин дипломов

Ремонт окон

Пятница, 09 Октября 2009 г. 15:39 + в цитатник
ГСС РЕАЛ ПРОФ СЕРВИС - ремонт и обслуживание оконных конструкций и систем.
На сайте компании вы найдете всю полезную информацию относительно обслуживания окон. Для удобства работы с интернет ресурсом все статьи тематически рубрикованы, таким образом ознакомиться с услугами будет легко и просто.
Для начала рекомендую прочитать главу о тонировке окон. Здесь вы узнаете о преимуществах тонирования стекол, технологии тонирования и другую полезную информацию.
ресурс о ремонте пластиковых окон

Фото

Четверг, 08 Октября 2009 г. 15:08 + в цитатник
Интернет ресурс с эротическими фото. Огромный выбор, удобная рубрикация по тема. Если с темой ещё не определились воспользуйтесь быстрым выбором или просмотрите весь архив, выбирая нужный месяц :)
Эротические фотоальбомы. Легкая эротика с улицы, фото звезд и много-много чего ещё совершенно бесплатно!
Девушки

Ярмарка образования

Четверг, 08 Октября 2009 г. 14:58 + в цитатник
"Ярмарка образования" проводится уже более десяти лет, и с каждым годом все больше студентов отправляется за рубеж для получения образования. На октябрьской ярмарке преимущественно обсуждается обучение в Европе, а на февральских - в северной Америке. За границей вы можете получить второе высшее образование или повысить квалификацию (например, отучившись на бакалавра в России получить звание магистра за рубежом).
Времени осталось совсем мало!

ВРЕМЯ ПРОВЕДЕНИЯ

10 октября 2009 г. - Москва
11 октября 2009 г. - Санкт-Петербург

Торопитесь! получить бесплатный билет можно здесь

Уникальные окна ПВХ по антикризисным ценам!

Среда, 07 Октября 2009 г. 21:22 + в цитатник
www.OknaCom.ru - это сайт, на котором вы без труда найдете всю интересующую вас информацию на тему пластиковых окон.
На этом интернет ресурсе вы узнаете о том, что такое пластиковое окно вообще, как правильно их установить, как правильно эксплуатировать и т.п.
Прямо сейчас вы можете разобраться в типах окон и их конструкции. Если у вас возникнут вопросы по сотрудничеству, вы можете обсудить их, воспользовавшись контактами, указанными внизу каждой страницы сайта.
Окна ПВХ

Все книги.su

Среда, 07 Октября 2009 г. 21:09 + в цитатник
vse-knigi.su - огромный книжный интернет ресурс. Все файлы доступны бесплатно и в разных форматах, таким образом вы не потратите средств на что, чтобы достать книгу, и сил на её перекодировку.
На поиск нужного материала уйдут считанные минуты - работает удобная система поиска. А если вы ещё не определились, что конкретно вам нужно, воспользуйтесь одним из видов сортировки: по автору, по алфавиту, по жанру или по серии. Советую начать, например, с этого жанра.
скачать книгу

Каталог автоинструкторов России

Среда, 07 Октября 2009 г. 15:45 + в цитатник
Быстро найти автоинструктора вам поможет этот интернет- ресурс. Огромный каталог лучших автошкол и автоинстукторов страны.
Все инструкторы - квалифицированные высококлассные специалисты. Без труда можете найти себе инструктора, у которого будете брать частные уроки вождения автомобиля с АКПП или с МКПП.
Выбор действительно велик, поэтому вы обязательно будете иметь дело с инструктором, который обучит вас на подходящем автомобиле, в подходящее для вас время!
частный автоинструктор

Обратные вызовы BDE32 для получения статуса операций

Вторник, 06 Октября 2009 г. 22:53 + в цитатник
Данный совет показывает как в Delphi 2.01 можно использовать функцию BDE DbiCallBack для получения значения линейки прогресса при длительных пакетных операциях, связанных с движением данных.

Дополнительная документация, описывающая вызовы функций BDE, находится в файле BDE32.HLP (расположенном в каталоге, где установлен 32-битный IDAPI).

При создании функций обратного вызова BDE, BDE будет осуществлять "обратный вызов" функций вашего приложения, позволяя тем самым извещать ваше приложение о происходящих событиях, а в некоторых случаях передавать информацию обратно BDE.

BDE определяет несколько возвращаемых типов, которые могут быть установлены для обратного вызова:
состояние больших пакетных операций.
запросы для передачи информации вызывающему оператору.
Данный совет подробно описывает обратный вызов типа cbGENPROGRESS, позволяющий изменять полоску прогресса в соответствии с состоянием операции.

Чтобы это сделать, необходимо сперва вызвать функцию DbiGetCallBack(), возвращающую дескриптор обратного вызова, который мог быть уже установлен (с этими параметрами), и сохранить информацию в структуре данных. Затем установить свой обратный вызов, заменяя им любой установленный до этого.

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

BDE каждый раз возвращает вашему приложению сообщение, содержащее количество обработанных записей, или же процентное соотношение обработанных записей, также передаваемое в виде целого числа. Ваш код должен учитывать эту ситуацию. Если процентное поле в структуре обратного вызова больше чем -1, можно сделать вывод что передан процент и можно сразу обновить линейку прогресса. Если же это поле меньше нуля, обратный вызов получил текстовое сообщение, помещенное в поле szTMsg и содержащее количество обработанных записей. В этом случае вам понадобится осуществить грамматический разбор текстового сообщения, преобразовать остальные строки в целое, затем вычислить текущий процент обработанных записей, и только после этого изменить линейку прогресса.

Наконец, после осуществления операции с данными, вам необходимо "отрегистрировать" ваш обратный вызов, и вновь установить предыдущую функцию обратного вызова (если она существует).

Для следующего примера необходимо создать форму и расположить на ней две таблицы, компонент ProgressBar и кнопку.

----- Демонстрационный код ---------
unit Testbc1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls, Grids, DBGrids, DB, DBTables, ComCtrls;

type
TForm1 = class(TForm)
Table1: TTable;
BatchMove1: TBatchMove;
Table2: TTable;
Button1: TButton;
ProgressBar1: TProgressBar;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

uses Bde; {Здесь расположены Dbi Types и Procs}

{$R *.DFM}

{тип структуры данных для сохранения информации о предыдущем обратном вызове}
type
TDbiCbInfo = record
ecbType: CBType;
iClientData: longint;
DataBuffLn: word;
DataBuff: pCBPROGRESSDesc;
DbiCbFn: pointer;
end;
type
PDbiCbInfo = ^TDbiCbInfo;

{Наша функция обратного вызова}

function DbiCbFn(ecbType: CBType;
iClientData: Longint;
CbInfo: pointer): CBRType stdcall;
var
s: string;
begin
{Проверяем, является ли тип обратного вызова тем, который мы ожидаем}
if ecbType = cbGENPROGRESS then
begin
{если iPercentDone меньше нуля, извлекаем число}
{обработанных записей из параметра szMsg}
if pCBPROGRESSDesc(cbInfo).iPercentDone < 0 then
begin
s := pCBPROGRESSDesc(cbInfo).szMsg;
Delete(s, 1, Pos(': ', s) + 1);
{Вычислям процент выполненного и изменяем линейку прогресса}
Form1.ProgressBar1.Position :=
Round((StrToInt(s) / Form1.Table1.RecordCount) * 100);
end
else
begin
{Устанавливаем линейку прогресса}
Form1.ProgressBar1.Position :=
pCBPROGRESSDesc(cbInfo).iPercentDone;
end;
end;
{существовал ли предыдущий зарегистрированный обратный вызов?}
{если так - осуществляем вызов и возвращаемся}
if PDbiCbInfo(iClientData)^.DbiCbFn <> nil then
DbiCbFn :=
pfDBICallBack(PDbiCbInfo(iClientData)^.DbiCbFn)
(ecbType,
PDbiCbInfo(iClientData)^.iClientData,
cbInfo)
else
DbiCbFn := cbrCONTINUE;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
CbDataBuff: CBPROGRESSDesc; {Структура DBi}
{структура данных должна хранить информацию о предыдущем обратном вызове}
OldDbiCbInfo: TDbiCbInfo;
begin
{Убедимся в том, что перемещаемая таблица открыта}
Table1.Open;
{Убедимся в том, что таблица-приемник закрыта}
Table2.Close;
{получаем информацию о любом установленном обратном вызове}
DbiGetCallBack(Table2.Handle,
cbGENPROGRESS,
@OldDbiCbInfo.iClientData,
@OldDbiCbInfo.DataBuffLn,
@OldDbiCbInfo.DataBuff,
pfDBICallBack(OldDbiCbInfo.DbiCbFn));
{регистрируем наш обратный вызов}
DbiRegisterCallBack(Table2.Handle,
cbGENPROGRESS,
longint(@OldDbiCbInfo),
SizeOf(cbDataBuff),
@cbDataBuff,
@DbiCbFn);

Form1.ProgressBar1.Position := 0;
BatchMove1.Execute;

{если предыдущий обратный вызов существовал - вновь устанавливаем его,}
{в противном случае "отрегистрируем" наш обратный вызов}
if OldDbiCbInfo.DbiCbFn <> nil then
DbiRegisterCallBack(Table2.Handle,
cbGENPROGRESS,
OldDbiCbInfo.iClientData,
OldDbiCbInfo.DataBuffLn,
OldDbiCbInfo.DataBuff,
OldDbiCbInfo.DbiCbFn)
else
DbiRegisterCallBack(Table2.Handle,
cbGENPROGRESS,
longint(@OldDbiCbInfo),
SizeOf(cbDataBuff),
@cbDataBuff,
nil);

{Показываем наш успех!}
Table2.Open;

end;

end.

Обработка исключений EDBEngineError

Вторник, 06 Октября 2009 г. 22:52 + в цитатник
Информация, описывающая условия возникновения ошибки BDE, может быть получена приложением с помощью исключения EDBEngineError. Перехват и обработка в приложениях исключений EDBEngineError осуществляется с помощью конструкции try..except. При наступлении исключения EDBEngineError должен быть создан объект EDBEngineError, различные поля в котором могут быть использованы программистом для определения источника ошибки и принятия решения по исправлению ситуации. Для данного типа исключения может генерироваться более чем одно сообщение об ошибке. Для получения нужной информации необходимо "проиграть" все сообщения.

Вот поля, наиболее соответствующие нашему контексту:
ErrorCount: тип Integer; указывает количество ошибок в свойстве Errors; отсчет начинается с нуля.
Errors: тип TDBError; набор записей типа структуры, содержащей информацию о каждой специфической сгенерированной ошибке; каждая запись доступна через номер индекса, имеющего тип Integer.
Errors.ErrorCode: тип DBIResult; указывает код ошибки BDE для ошибки в текущей записи Errors.
Errors.Category: тип Byte; категория ошибки, ссылается на поле ErrorCode.
Errors.SubCode: тип Byte; субкод значения ErrorCode.
Errors.NativeError: тип LongInt; код удаленной ошибки, возвращаемой сервером; если ноль, то ошибка не является ошибкой сервера; в этом поле возвращается код запроса SQL.
Errors.Message: тип TMessageStr; если ошибка является ошибкой сервера, то сообщение сервера содержится в текущей записи Errors; если это не ошибка сервера, то это сообщение об ошибке BDE.
В случае наличия конструкции try..except, объект EDBEngineError создается непосредственно в секции except. После создания объекта исключения все его области становятся доступными, ссылка на объект может передаваться в другую процедуру для детального исследования ошибок. Метод передачи объекта EDBEngineError в специализированную процедуру для приложения предпочтительнее, поскольку делает код приложения более модульным и уменьшает общий объем кода, необходимого для синтаксического анализа объекта и получения информации о возникшей ошибке. В качестве альтернативы можно предложить идею создания специализированного компонента, предусматривающего описанную функциональность; такое решение позволяет распространять созданный в основе компонента код среди нескольких приложений. Приведенный ниже пример демонстрирует создание объекта DBEngineError, передачу его процедуре и синтаксический разбор объекта для получения информации об ошибке.

В конструкции try..except для создания экземпляра DBEngineError необходим синтаксис, показаный ниже:
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
begin
if Edit1.Text > ' ' then
begin
Table1.FieldByName('Number').AsInteger := StrToInt(Edit1.Text);
try
Table1.Post;
except
on E: EDBEngineError do
ShowError(E);
end;
end;
end;



В данной процедуре сделана попытка изменить значение поля таблицы и вызвать метод Post соответствующего компонента TTable. Ошибка при попытке отправить измененные данные перехватывается в секции try..except. В случае возникновения EDBEngineError, выполнение передается в секцию except, где происходит создание объекта EDBEngineError (E) и передача его процедуре ShowError. Обратите внимание на то, что в этой секции во внимание берутся только исключения EDBEngineError. В реальной программе код должен содержать проверку на другие типы исключений.

Процедура ShowError в качестве параметра получает ссылку на объект EDBEngineError и "опрашивает" его на предмет наличия информации об ошибках. В приведенном ниже примере информация об ошибках отображается в компоненте TMemo. В реальной жизни отображение ошибок может и не понадобится, но информация о них является логикой работы приложения, расширяющая его функциональность так, чтобы оно могло правильно реагировать на них. Первым шагом необходимо вычислить количество реально произошедших ошибок. Эта величина хранится в свойстве ErrorCounnt. Данное свойство имеет тип Integer и может использоваться для создания цикла, в теле которого можно по очереди получить информацию о каждой ошибке, содержащейся в объекте. В теле цикла поочередно ErrorCount-раз перебираются все содержащиеся в объекте ошибки (как вы помните, каждая ошибка представляет собой запись-элемент свойства Errors), "достается" о них вся информация и помещается в компонент TMemo.
procedure TForm1.ShowError(AExc: EDBEngineError);
var
i: Integer;
begin
Memo1.Lines.Clear;
Memo1.Lines.Add('Количество ошибок: ' + IntToStr(AExc.ErrorCount));
Memo1.Lines.Add('');
{Перебираем все записи Errors}
for i := 0 to AExc.ErrorCount - 1 do
begin
Memo1.Lines.Add('Сообщение: ' + AExc.Errors[i].Message);
Memo1.Lines.Add(' Категория: ' +
IntToStr(AExc.Errors[i].Category));
Memo1.Lines.Add(' Код ошибки: ' +
IntToStr(AExc.Errors[i].ErrorCode));
Memo1.Lines.Add(' Субкод: ' +
IntTooStr(AExc.Errors[i].SubCode));
Memo1.Lines.Add(' Реальная ошибка: ' +
IntToStr(AExc.Errors[i].NativeError));
Memo1.Lines.Add('');
end;
end;

Локальный общий доступ

Вторник, 06 Октября 2009 г. 22:46 + в цитатник
...я так понимаю, что LocalShare относится к ситуации, когда другие не-IDAPI приложения могут одновременно иметь доступ к одним и тем же файлам...

Примерно на такие мысли наталкивает поставляемая документация... к сожалению это не так.

LOCALSHARE=False говорит BDE о том, что он должен сам решать при необходимости вопрос о блокировке таблицы/записи в типичных ситуациях, например, когда BDE 'думает' что таблица находится на локальном диске он выключает блокировку для увеличения скорости доступа. К сожалению, логические диски общего пользования в сетях 'p-t-p' программно идентифицируются как локальные с предсказуемо липовыми результатами. Установка LOCALSHARE=True заставляет блокирующий механизм 'включаться' для всех дисков и, следовательно, решает эту проблему.

Конфликт IDAPI German и English

Вторник, 06 Октября 2009 г. 22:39 + в цитатник
Я просто установил DtopicsP v1.20 и DtopicsD (03-29-96). При запуске dtopics.exe возникает ошибка DB-Error $3E05 ('cannot load driver') (не могу загрузить драйвер).

Я нашел ответ в German Borland Forum. Ошибка происходит, если установлен German BDE. В этом случае в систему устанавливается вместо IDR10009.DLL (который присутствует в английской версии) файл IDR10007.DLL. После установки данного файла в каталог IDAPI все заработало как часы.

Это означает, что приложения, разработанные под English Delphi не будут работать под German или French Delphi.

Использование BDE в сети

Вторник, 06 Октября 2009 г. 22:38 + в цитатник
1) Может ли мое приложение иметь доступ к файлам, расположенным на сетевых дисках?

Да.

2) Когда я попытался это сделать, программа выдала сообщение об ошибке "Not initialized for accessing network files" (не инициализировано для доступа к сетевым файлам).

Вероятно вам необходимо задать правильный путь к каталогу в поле 'NET DIR' файла IDAPI.CFG. Директория должна быть одна и быть доступна всем пользователям приложения с применением одинаковых подключенных сетевых дисков. (т.е.: если NET DIR указывает на F:\PUBLIC\NETDIR, пользователи с подключенным сетевым диском и имеющим путь G:\NETDIR доступа не получат).

3) Возможно ли запустить приложение, относящееся к описываемой категории, с сетевого диска без установленного на локальной машине BDE (за исключением возможных ссылок в локальном файле WIN.INI на копии элементов программы BDE/IDAPI, расположенных на сетевом диске)?

Да. Установите BDE в сети и затем добавьте следующие секции в файл WIN.INI каждой рабочей станции:
[IDAPI]
CONFIGFILE01=F:\IDAPI\IDAPI.CFG
DLLPATH=F:\IDAPI

[Borland Language Drivers]
LDPath=F:\IDAPI\LANGDRV

...пути должны отражать текущее месторасположение каталога IDAPI.

4) Для установки "NET DIR" мне нужно запустить BDECFG на каждой рабочей станции или просто сделать это на "сервере"?

C помощью утилиты BDECFG отредактируйте файл IDAPI.CFG и сохраните его в сетевом каталоге IDAPI. Следовательно, вам необходимо проделать данную операцию всего-лишь один раз.

5) Если мне нужно сделать это только на сервере, то как все рабочие станции узнают о месторасположении сетевых файлов ("NET DIR")?

Рабочая станция открывает файл IDAPI.CFG из каталога, указанного в WIN.INI, и уже оттуда читает настройки NET DIR.

Запись буфера BDE на диск

Вторник, 06 Октября 2009 г. 22:37 + в цитатник
Общее:

Сделанные в таблице изменения непосредственно на диск не записываются до тех пор, пока таблица не будет закрыта. Потеря питания или сбой в системе может привести к потере данных и прочим неприятностям. Чтобы избежать этого, существует два прямых вызова Database Engine, дающих один и тот же результат. Эти функции - DbiUseIdleTime и DbiSaveChanges.

DbiSaveChanges(hDBICur):

DbiSaveChanges сохраняет на диске все обновления, находящиеся в буфере таблицы, связанной с курсором (hDBICur). Может быть вызвана из любого места программы. Например, можно при каждом обновлении записи сохранять на диске все изменения (добавьте dbiProcs в список используемых модулей):
procedure TForm1.Table1AfterPost(DataSet: TDataSet);
begin
DbiSaveChanges(Table1.handle);
end;



При таком способе можно не беспокоиться насчет потерь данных в случае потери питания или сбоя системы, которое может произойти после обновления записи.

DbiSaveChanges также можно использовать для того, чтобы временную таблицу (созданную с помощью DbiCreateTempTable) сделать постоянной.

Эта функция не применима к таблицам SQL.

DbiUseIdleTime:

DbiUseIdleTime может быть вызвана, если "Windows Message Queue" (очередь запросов Windows) пуста. Это позволяет Database Engine сохранить на диске "грязные буферы". Другими словами, выполняется операция DbiSaveChanges, но применительно ко ВСЕМ измененным таблицам. Тем не менее, данная операция не обязательно должна выполняться после каждого обновления записи, ее нужно приберечь для "холостого" периода (период простоя, idle).

В Delphi это может быть использовано таким образом (добавьте dbiProcs в список используемых модулей):
procedure TForm1.FormCreate(Sender: TObject);
begin
Application.onIdle := UseIdle;
end;

procedure Tform1.UseIdle(Sender: TObject; var Done: Boolean);
begin
DbiUseIdleTime;
end;



Некоторые замечания:

Использование обоих вызовов DbiUseIdleTime и DbiSaveChanges (после каждого обновления записи) излишне и сопровождается необязательными вызовами функций. Если приложение выполняет множественный ввод новых записей или их редактирование в течение небольшого периода времени, рекомендуем осуществлять вызов функции DbiUseIdleTime во время простоя клинта, а вызов DbiSaveChanges после осуществления "пакета" обновлений.

В случае, если в таблице выполняется не слишком много изменений, клиент может использовать вызов DbiSaveChanges после каждого постинга или же "повесить" на таймер вызов DbiUseIdleTime.

Демонстрация обратного вызова BDE

Вторник, 06 Октября 2009 г. 22:35 + в цитатник
Существует обратный вызов (callback) BDE, который вы можете использовать для получения уведомлений об изменении таблиц Paradox. Тем не менее от вас все же потребуется использование таймера. Функция обратного вызова инициируется при вызове функций, осуществляющих доступ к таблице. Ниже приведен код, демонстрирующий технику работы с описанным выше обратным вызовом:

TCMAIN.PAS:
unit tcmain;

{ Демонстрация cbTableChange }

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
DB, DBTables, ExtCtrls, DBCtrls, Grids, DBGrids, BDE, StdCtrls;

const

WM_UPDATETABLE = WM_USER + 1;

type

TForm1 = class(TForm)
Table1: TTable;
DataSource1: TDataSource;
DBGrid1: TDBGrid;
DBNavigator1: TDBNavigator;
Timer1: TTimer;
Button1: TButton;
procedure Table1AfterOpen(DataSet: TDataSet);
procedure FormCreate(Sender: TObject);
procedure Timer1Timer(Sender: TObject);
private
FChgCnt: Integer;
FCB: TBDECallback;
function TableChangeCallBack(CBInfo: Pointer): CBRType;
procedure UpdateTableData(var Msg: TMessage); message WM_UPDATETABLE;
end;

var

Form1: TForm1;

implementation

{$R *.DFM}

// Это функция, вызываемая функцией обратного вызова.

function TForm1.TableChangeCallBack(CBInfo: Pointer): CBRType;
begin

Inc(FChgCnt);
Caption := IntToStr(FChgCnt);
MessageBeep(0);
// Здесь мы не можем вызвать Table1.Refresh, делаем это позже.
PostMessage(Handle, WM_UPDATETABLE, 0, 0);
end;

// Данная функция вызывается в ответ на PostMessage (см. выше).

procedure TForm1.UpdateTableData(var Msg: TMessage);
begin

// Не пытайтесь вызвать обновление, если мы в "середине" редактирования.
if (Table1.State = dsBrowse) then
Table1.Refresh;
end;

procedure TForm1.Table1AfterOpen(DataSet: TDataSet);
begin

// Установка обратного вызова.
FCB := TBDECallback.Create(Self, Table1.Handle, cbTableChanged,
nil, 0, TableChangeCallBack);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin

Table1.DatabaseName := ExtractFilePath(ParamStr(0));
Table1.Open;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var

SeqNo: Longint;
begin

// События таймера просто осуществляют вызов DbiGetSeqNo для получения доступа к таблице.
// В противном случае мы не хотим делать обратный вызов, пока что-то делаем
// (типа прокрутки) для получения доступа к данным. DbiGetSeqNo вызывается в случае,
// если таблица не активна.
if Table1.State <> dsInActive then
DbiGetSeqNo(Table1.Handle, SeqNo);
end;

end.



TCMAIN.TXT:
object Form1: TForm1

Left = 270
Top = 230
Width = 361
Height = 251
Caption = 'Form1'
PixelsPerInch = 96
OnCreate = FormCreate
TextHeight = 13
object DBGrid1: TDBGrid
Left = 0
Top = 83
Width = 353
Height = 141
Align = alBottom
DataSource = DataSource1
TabOrder = 0
end
object DBNavigator1: TDBNavigator
Left = 96
Top = 4
Width = 240
Height = 25
DataSource = DataSource1
TabOrder = 1
end
object Button1: TButton
Left = 132
Top = 36
Width = 75
Height = 25
Caption = 'Button1'
TabOrder = 2
OnClick = Timer1Timer
end
object Table1: TTable
AfterOpen = Table1AfterOpen
DatabaseName = 'DBDEMOS'
TableName = 'VENDORS.DB'
Left = 16
Top = 8
end
object DataSource1: TDataSource
DataSet = Table1
Left = 52
Top = 8
end
object Timer1: TTimer
OnTimer = Timer1Timer
Left = 80
Top = 28
end
end

Архитектура BDE и его особенности при работе с SQL-серверами 2

Вторник, 06 Октября 2009 г. 22:33 + в цитатник
Фильтрация

Фильтрация TTable и TQuery происходит с учетом живого или мертвого кэша. Для TTable при наложении фильтра конструируется соответствующий SQL-запрос, а TQuery производит фильтрацию буквально при помощи Locate (т.е. сначала выбираются все записи в кэш, а затем идет фильтрация уже в кэше).

О вреде UNIQUE constraint

В Interbase уникальность поля можно обеспечить тремя способами: создать первичный ключ, создать unique constraint, и создать уникальный индекс. Но при чем здесь Interbase? А при том, что BDE открывает TTable по умолчанию с использованием уникального индекса. Если таблица одновременно содержит как первичный ключ, так и unique constraint, то в результате у таблицы 2 уникальных индекса. При обращении к списку индексов TTable берет для сортировки по умолчанию первый попавшийся. Если уникальность поля обеспечивается обычным уникальным индексом, то проблем нет. А вот если та же уникальность обеспечивается через UNIQUE constraint, то при backup/restore базы данных есть шанс что порядковые номера индексов поменяются (поскольку для IB это constraint целостности), и BDE будет брать в качестве первого попавшегося индекс от unique constraint вместо индекса от primary key. Вреда от этого, в общем, никакого нет, но в результате это вызывает нежелательный порядок сортировки по умолчанию в приложениях.

"Живые" запросы

Если способность TTable редактировать и удалять записи ни у кого не вызывает удивления, то TQuery требует, чтобы свойство RequestLive было установлено в True. Если при False запрос отправлялся непосредственно на сервер, то при True запрос предварительно обрабатывается локальным SQL (модуль IDSQL32.DLL). Это необходимо для того, чтобы TQuery смог сформировать запросы INSERT/UPDATE/DELETE на основании заданного SELECT. Для TTable построение таких запросов не представляет сложности, т.к. задано только имя таблицы, имена полей считаны и т.п. А существующий SQL-запрос нужно синтаксически разобрать, чтобы понять, сколько в нем используется таблиц, какие выбираются поля и из каких таблиц, и можно ли вообще сформировать запросы на вставку, обновление и удаление данных.

Именно таким разбором SQL и занимается Local SQL. Разумеется, он поддерживает весьма ограниченный синтаксис SQL, что не позволяет делать "живыми" запросы, использующие расширенные конструкции SQL, пользовательские функции или специфические для конкретного сервера особенности. Например, для организации живого запроса вместо
SELECT * FROM TABLE
WHERE FIELD STARTING WITH 'A'



придется писать
SELECT * FROM TABLE
WHERE FIELD LIKE 'A%'



Подобную замену еще можно пережить, но не всегда возможно найти замену конструкции, которую не понимает Local SQL, и прекрасно понимает сервер.

примечание:
вы сами можете убедиться в изложенном, поместив первый запрос в TQuery, переключив RequestLive в True. Попытайтесь установить Active компонента в True и посмотрите что получится.


Собственно, как вы поняли, на самом деле никаких "живых" запросов не существует. В SQL оператор SELECT выполняет только чтение, а вставить, обновить или удалить записи можно только операторами INSERT, UPDATE и DELETE, и никак иначе.

При переключении TQuery.RequestLive:=True TQuery начинает вести себя как TTable - т.е. он сначала разбирает запрос, извлекает оттуда имя таблицы, и потом выбирает информацию из системных таблиц о полях таблицы, индексах и т.п. Вы можете все это увидеть в SQL Monitor.

Кроме RequestLive можно еще воспользоваться и компонентом UpdateSQL. Об этом см. дальше в разделе CachedUpdates.

SQLQUERYMODE

Кроме RequestLive на выполнение запросов влияет и параметр алиаса или драйвера IB SQLQUERYMODE. Когда этот параметр установлен в LOCAL, BDE всегда производит разбор SQL-конструкций при помощи Local SQL. Если параметр установлен в "пусто", то BDE сначала пытается отправить SQL на сервер, а при получении ошибки пытается выполнить его Local SQL. При установленном параметре SERVER запросы всегда отправляются только на сервер (за исключением "живых").

Таким образом, при установке LOCAL запросы будут всегда выполняться локальным ядром SQL BDE, и функциональность SQL IB будет недоступна (не будут выполняться запросы с containing и др. синтаксисом, который не поддерживает Local SQL). Избавиться от такого поведения лучше всего установив раз и навсегда значение SERVER.

Refresh и атомарность запросов

Читатель уже после информации о живом и мертвом кэше, наверное, давно хочет спросить - а как же BDE видит новые записи, добавляемые другими приложениями? Да никак. С TTable все понятно - в любой момент можно вызвать refrech, что приведет к удалению "живого" кэша и переоткрытию TTable как мы уже видели в разделе о кэшах записей. TTable перед своим закрытием запоминает запись, на которой стоял курсор грида, и поэтому после открытия может спозиционироваться на эту же запись.

TQuery работает с "мертвым" кэшем, поэтому обновлять его невозможно. BDE не знает о том, какое из полей в запросе является первичным ключом, да и вообще по скольким таблицам построен запрос. Поэтому единственным вариантом для refresh является переоткрытие TQuery (Close/Open). Текущая запись при этом будет потеряна. Можно, правда, попытаться использовать TBookmark чтобы запомнить запись и вернуться к ней после открытия TQuery, но как и Locate это вызовет выборку всех записей с сервера в кэш TQuery и при большом количестве выбираемых записей может занять длительное время.

примечание:
Даже если компонент IBX IBTable и поддерживает Refresh, то он его выполняют точно таким же образом, что и BDE. А компонент IBDataSet выполняет Refresh только для одной, текущей, записи.


В чтение актуальных данных вмешивается еще и атомарность операторов SQL. Применительно к SELECT это означает, что он будет выбирать только те записи, которые существовали на момент выполнения этого SELECT. Это означает, что если открыть TQuery, а затем через 5 минут подсоединить его к гриду, то в нем будут видны только те записи, которые были в базе данных 5 минут назад. Даже если за это время это же самое приложение в этой же транзакции успело добавить, изменить или удалить 1 или сколь угодно большее количество записей, попадающих под условия выборки данного SELECT.

В буквальном смысле это означает, что если вставить запись в открытый select, то увидеть новую запись нельзя. Для этого придется переоткрыть запрос. По отношению к TQuery это справедливо, а вот TTable "обманывает" пользователя, помещая данные успешно вставленной записи прямо в свой собственный кэш. Таким образом, вставка в TTable как бы помещает данные прямо в открытую выборку. Чего, собственно, на самом деле на сервере не происходит.

Вообще перечитывание данных почти всегда вызывает проблемы. Сервер не уведомляет клиентов, что определенные записи изменились, появились или были удалены. EventAlerter может сообщить только информацию что некое событие (например, была изменена таблица) произошло, но не "номер записи". В многопользовательской среде перечитывание данных по таймеру может вызвать большой сетевой трафик. Да и кроме того, клиент обычно видит в гриде только какую-то часть данных, и идеальным вариантом было бы не только узнать, что в именно этой части данных произошли изменения, но и перечитать только эту часть. В итоге, самым разумным вариантом является помещение в приложение на нужную форму кнопки Refresh (Перечитать). Пусть пользователь решает, когда ему нужно это сделать.

примечание:
в отличие от атомарного SELECT, оператор FOR SELECT внутри процедур IB не является атомарным. Т.е. если в цикле FOR SELECT добавлять записи, то они могут попасть в область видимости FOR SELECT, и может произойти "бесконечный цикл". Также в IB неатомарной является конструкция INSERT INTO ... SELECT FROM.


Завершение транзакций

BDE устроен так, что компонент TDatabase может работать только с одной транзакцией одновременно. При этом может быть два режима - неявная работа с транзакциями (AUTOCOMMIT, NOAUTOCOMMIT), и явная работа с транзакциями (методы StartTransaction, Commit и Rollback). В режиме AUTOCOMMIT BDE самостоятельно завершает транзакцию и стартует новую при любых модификациях данных (insert/update/delete) или при вызове TStoredProc.ExecProc. Таким образом изменения автоматически сохраняются в базе данных. Однако чтение данных и вообще работа с ними может быть выполнена только в контексте транзакции. Т.е. вне транзакции с данными работать нельзя, т.к. не будет обеспечиваться целостность данных. При этом данные, прочитанные в одной транзакции, неактуальны для другой транзакции. Если посмотреть справку BDE32.HLP по функции dbiEndTran, то можно обнаружить, что BDE при завершении явной или неявной транзакции ведет себя следующим образом:

открытый query довыбирает данные.

открытый table закрывается

другие случаи я не упомянул, потому что IB SQL Link их не поддерживает. То есть при любом завершении транзакции (и открытии новой) данные будут перечитываться. Для TTable это не смертельно, т.к. он знает первичный ключ записи, на которой стоял курсор грида, и может перечитать немного данных, чтобы заново отобразить их. А вот для TQuery, который не знает никаких первичных ключей, происходит полная выборка всех данных, что эквивалентно вызову Locate, FetchAll или Last. Так что если ваше приложение при обновлении данных почему-то сильно тормозит, или возникают паузы, то нужно срочно смотреть в SQL Monitor, какие именно запросы перечитываются.

примечание:
иногда по неизвестным причинам BDE перечитывает запросы, которые совершенно этого не требуют. Например мне встречалась ситуация с неявным перевыполнением запроса при перемещении по grid-у detail-таблицы, причем запрос никак не был связан ни с master ни с detail-таблицами. Избавиться от проблемы не удалось.


Соответственно, чтобы предотвратить плохую производительность, нужно или держать минимум данных открытыми в TQuery, или стремиться к минимизации количества записей, выбираемых TQuery. Также можно открыть второй TDatabase, и работать например со справочными таблицами только в нем. Таким образом изменения будут идти в одном коннекте, и не будут вызывать завершение транзакции и перечитывание данных в другом. В компонентах прямого доступа это решается более простым способом, т.к. там поддерживается произвольное количество транзакций для одного коннекта. Есть, кстати, и оригинальное решение, которое позволяет использовать коннект TDatabase совместно с компонентами FreeIBComponents или IBX:
var
h: tisc_db_handle;

DB := TIBDatabase.Create(nil);

try
Dbtables.Check(DbiGetProp(HDBIOBJ(DMCommBilling.Database.Handle), dbNATIVEHNDL, @h, sizeof(tisc_db_handle), l));
DB.DBName := 'Cloned';
DB.Handle := h;
TR := TIBTransaction.Create(nil);

try



и так далее. Таким образом, в приложении BDE можно дополнительно обрабатывать данные в транзакциях IBX. Приложение получается комбинированным, поскольку для доступа к данным в новой транзакции придется использовать компоненты IBX.

Record/Key deleted

Надо сказать, что BDE облегчает жизнь программисту хотя бы тем, что перечитывает запись, которую собирается редактировать пользователь. Т.е. как только BDE переводит TTable или "живой" TQuery в режим Edit, он производит выборку текущей записи (по первичному ключу) и показывает для редактирования самые последние, актуальные, данные. Правда, пока пользователь редактирует запись, ее могут изменить или даже удалить другие пользователи - BDE никоим образом не "блокирует" запись, которая редактируется, т.к. в SQL вообще нет команды вроде "заблокировать запись". Поэтому после Post клиент может обнаружить, что его изменения не попадут в базу данных, т.к. запись уже изменилась или удалена. И обнаружит он это или нет, зависит от режима TDataSet.UpdateMode.

UpdateMode имеет 3 режима:
upWhereAll
По умолчанию - BDE пытается сделать UPDATE с внесением в условие WHERE всех значений полей, которые были ДО момента редактирования. Если при этом произошла ошибка, значит хотя бы одно поле у редактируемой записи уже было кем-то изменено (с момента входа в режим редактирования до момента Post).
upWhereChanged
BDE пытается сделать UPDATE с условием WHERE, проверяющим старые значения только измененных полей. Т.е. чтобы убедиться, что пользователь поменял именно те значения полей, которые видел, на новые. Если произошла ошибка, то это значит что одно из изменяемых полей было уже кем-то изменено.
upWhereKeyOnly
BDE обновляет запись, устанавливая в WHERE поиск записи только по ее первичному ключу.

Соответственно, если запись не найдена, то выдается упомянутое в заголовке сообщение Record/Key deleted. Обратите внимание, что успешное обновление записи в режимах upWhereChanged или upWhereKeyOnly может вызвать проблемы с конкурентным обновлением. Например, существует таблица TABLE, у которой три поля: ID, NAME и PRICE.

Два пользователя открывают таблицу. Один видит, что для данного имени товара неверно указана цена. Другой счел, что цена правильная, только имя товара указано с ошибкой. У обоих UpdateMode установлен в upWhereKeyOnly или upWhereChanged.

После изменения пользователи по очереди нажимают Post (вероятность одновременного нажатия достаточно низка, а кто из них нажал на кнопку первым не имеет значения). В результате оказалось изменено и название товара и его цена, и комбинация этих полей опять содержит неправильную информацию!

В данном частном случае избавиться от проблемы можно установкой UpdateMode только upWhereAll, чтобы запрос при обновлении проверял все зависимые поля. Или, можно подключить компонент TUpdateSQL и прописать для обновления данных запрос, который будет проверять на "старые" значения и имя товара и его цену. Однако работать с TUpdateSQL без CachedUpdates невозможно.

Другая причина, по которой может происходить сообщение Record/Key deleted - перечитывание данных после их обновления. BDE таким образом (по крайней мере для TTable) пытается вставить запись в нужное место (в порядке сортировки) кэша. Но если после вставки или обновления запись на сервере изменилась - другим пользователем, default-условием или триггером (с генератором) - то BDE не сможет ее найти и выдаст упомянутое сообщение.

Если запись от момента редактирования до момента перечитывания была изменена другим пользователем, то тут ничего нельзя сделать. Если это был default или триггер, то вполне возможно, что лучше отказаться от считывания таких полей в DBGrid (вызовите FieldEditor). Если же это поле первичного ключа, которому в триггере присваивается значение генератора, то вам явно стоит прочитать статью, которая за 4 года существования не потеряла своей актуальности.

Cached Updates

При работе без CachedUpdates изменения, производимые над данными, отправляются на сервер немедленно. Это достаточно удобно, т.к. позволяет немедленно обнаруживать конфликты изменений, но не всегда хорошо для сетевого трафика если нет явного управления транзакциями или приводит к накоплению версий записей при длительных явных транзакциях. В первую очередь режим CachedUpdates подходит для "блокировочных" серверов, в которых чтение данных блокирует их от изменения (например MS SQL, Sybase).

CachedUpdates позволяет накопить изменения, и затем "выстрелить" их на сервер одним пакетом. При этом время блокировок минимально, минимален также сетевой трафик, но существует высокая вероятность что данные уже успели измениться. Поэтому при использовании CU необходимо тщательно планировать именно процесс обращения к таблицам и режимы UpdateMode.

За более подробной информацией по CachedUpdates обращайтесь к документации или к книге Шумакова ("Delphi 3 и создание приложений баз данных", в том числе последующие издания для Delphi 4 и 5 в соавторстве с Фароновым), где все это очень хорошо описано. Нас сейчас CU больше интересует как замена RequestLive.

Действительно, "оживление" запроса выполняется следующим образом - к компоненту TQuery подключается компонент TUpdateSQL, в котором прописываются вручную или автоматически запросы на вставку, удаление или изменение записи. Заметьте, только одной записи. После включения CachedUpdates:=True при модификации данных именно эти запросы, а не конструируемые Local SQL при RequestLive=True, будут отправляться на сервер (отправляются они только в момент ApplyUpdates, а не в момент реального обновления записи).

Самым непонятным является то, почему связка TQuery и TUpdateSQL не может работать без CachedUpdates. Например компоненты IBX без проблем обеспечивают такой режим, да и вообще там у TIBQuery нет свойства RequestLive (т.к. нет парсера SQL на клиентской стороне). Т.е. в IBX, конечно, можно использовать CachedUpdates, но разве что при действительной в нем необходимости.

Гетерогенные запросы

BDE обладает уникальной способностью выполнять запросы, которые обращаются к таблицам, находящимся на разных серверах баз данных (или к таблицам разных форматов). Это так называемые "гетерогенные" запросы. Иногда их называют "распределенными", т.к. данные "распределены" по разным базам данных возможно одного и того же SQL-сервера.

Выполнить гетерогенный запрос можно следующим образом:
Открыть 2 или более TDatabase, каждый для соответствующей базы данных. Например, один компонент подсоединен как A к алиасу TEST, а другой, как
Открыть TDatabase, который подсоединен к драйверу типа STANDARD(т.е. к локальным таблицам. Существование оных необязательно). См. окно свойств TDatabase.
Выполнить запрос в компоненте TQuery, подсоединенном к "стандартному" TDatabase. В результате должна получиться такая "конструкция"

а запрос иметь вид
SELECT C.CLIENT_NAME
FROM ":A:CLIENTS" C, ":B:EMPLOYEE" E
WHERE E.EMP_NO = C.CLIENT_ID



Конечно, по смыслу это полная чушь, но зато показывает пример указания таблиц из разных базах данных. Еще один пример запроса можно найти по ключевой фразе 'heterogeneous joins' в BDE32.HLP.

Пока я готовил и проверял этот пример, установка Query1.Active в true вызывала страшные содрогания винчестера. Дело в том, что подобные запросы выполняются следующим образом:
Ядро Local SQL "разбирает" запрос, и выясняет, какие таблицы из каких баз данных используются в запросе
Данные из каждой таблицы вытаскиваются в локальный кэш (т.е. на клиента), в память или временные таблицы.
Извлеченные данные обрабатываются локальным SQL (join, where, order by и т.п.).

Однако происходит так не всегда. По крайней мере в моем тестовом случае Local SQL начал выполнять просто чудовищные операции:

Сначала для одной, а затем для другой таблицы был выполнен SELECT COUNT(*). Т.е. Local SQL сначала пытается понять, во что ему обойдется скачивание данных на клиентскую часть. Очевидно, записей в CLIENTS ему показалось мало, и он вытащил все записи из EMPLOYEE, а потом начал последовательно выбирать соответствующие записи из CLIENTS отдельными запросами для каждой записи (проверяя соответствие условия WHERE). Буквально SELECT ... FROM CLIENTS WHERE CLIENT_ID = ? ORDER BY CLIENT_ID ASC.

(зачем здесь нужен order by - неизвестно). Почему произошло не наоборот, т.е. меньшая таблица не была выбрана в память, неясно.

Можно даже не упоминать, что select count(*) на реальных данных может выполняться долго (даже без учета возможной сборки мусора). Не говоря о том, что в EMPLOYEE было 42 записи, и отдельных запросов к таблице CLIENTS получилось тоже 42.

Вот такая веселая арифметика. Зато получены четкие объяснения, почему "трещал" винчестер.

Однако, пусть даже и таким жутким способом, но BDE умеет выполнять гетерогенные запросы. Благодаря Local SQL и тому, что BDE умеет работать с локальными таблицами (которые он использует для хранения промежуточных данных таких запросов). Ни IBObjects, ни FIBC/IBX, ни IB API не имеют таких возможностей, и соответственно, не могут выполнять гетерогенные запросы.

Итог

После прочтения этой статьи может сложиться впечатление, что BDE вообще не пригоден для работы с SQL-серверами. На самом деле это не так. Если знать его архитектуру (надеюсь, статья вам в этом помогла), то можно снизить неэффективность BDE в приложениях до минимума.

Другой важный момент - скорость разработки. Она до сих пор остается самой высокой по сравнению с другими наборами компонент (даже с IBObjects). А скорость разработки - это в первую очередь более низкая стоимость разработки системы.

Кстати, может оказаться, что вся эта "неэффективность" в смысле большого объема передаваемых данных на вашей 100мбит сети и не проявится. А если сеть гигабитная, то вы вообще никакого лишнего трафика не заметите. И наоборот - для модемных соединений BDE, конечно, никуда не годится. Или если вам нужно тщательное планирование и управление транзакциями IB, то BDE здесь тоже делать нечего.

Есть и более жесткие критерии выбора - если вы собираетесь переходить на Kylix или IB6 (диалект 3), то c BDE придется расстаться. Если же в течение ближайшего года или полутора вы не собираетесь этого делать - забудьте об альтернативах, и продолжайте работать привычным способом.

Архитектура BDE и его особенности при работе с SQL-серверами 1

Вторник, 06 Октября 2009 г. 22:31 + в цитатник
Этот материал основан на изучении документации и справочных материалов по BDE и на собственном опыте. На самом деле информация из этого документа частично появлялась и раньше как в FAQ Borland так и в материалах других авторов (в частности Epsylon Technologies). Однако до сих пор большое количество разработчиков используют BDE. Но в последнее время все больше людей работают с SQL-серверами, и более популярными становятся компоненты прямого доступа - IBObjects/FreeIBComponents/IBExpress, Direct Oracle Access и другие. Кроме того, BDE не будет поддерживать Interbase 6.0 (диалект 3), да и вообще похоже, прекратит свое существование. В Delphi 6 наряду со старым BDE и в Kylix (Delphi и C++Builder для Linux) будет использоваться другая библиотека - dbExpress. Поэтому, чтобы поставить жирную точку (или крест, как хотите) на BDE, я и решил написать этот документ.

В большей степени этот текст напоминает то, что я читал на курсах по Delphi и разработке баз данных 3-4 года назад. Привет вам, курсанты! Можете прочитать этот документ хотя бы для того, чтобы освежить память.

Введение

Для начала вернемся лет на 10 назад. В те времена на компьютерах властвовали настольные СУБД - dBase, Paradox, FoxPro, Clipper и т.п. SQL-сервера в основном работали на мэйнфреймах. Среди форматов настольных СУБД был полный разнобой, и например, хотя Clipper, FoxPro и dBase работали с форматом DBF, использовать таблицы друг друга они фактически не могли из-за мелких, но существенных различий. Обмениваться данными в те времена между разными СУБД можно было разве что при помощи импорта-экспорта. Многие компании понимали, что так дальше продолжаться не может. Некоторые встраивали в свои продукты несколько "движков", но это приводило к распуханию продукта, да и чаще всего пользователи работали только с одним форматом данных, а не несколькими одновременно.

В 1990-м году Borland приобрел компанию Ashton-Tate, а вместе с ней и dBase (и Interbase). Таким образом у Borland появилось две настольные СУБД, с совершенно разными форматами - dBase и Paradox. Понятно, что для дальнейшего развития этих продуктов усилия по развитию форматов данных и работы с ними фактически удваивались. И в частности поэтому было принято решение создать некое универсальное ядро доступа к данным, которое могло бы работать с несколькими форматами данных единым образом. Созданию такого ядра также способствовало появление Windows, а следовательно и разделяемых библиотек - DLL. Можно было выпускать несколько продуктов, используя одни и те же dll доступа к данным. Это вполне соответствовало объектно-ориентированной концепции разработки ПО, которая не только использовалась в Turbo Pascal и в Turbo C++, но и при разработке собственных приложений Borland, таких как dBase, Paradox и Quattro (все для Windows).

примечание:
дальнейшая информация по датам взята из документа, подзаголовок "Evolution of BDE/IDAPI Technology: 1990 - 94".


Технология была названа Open Database Application Programming Interface - ODAPI, и впервые была использована в Quattro Pro 1.0 for Windows в сентябре 1992 года. В январе 1993-го эта же версия ODAPI 1.0 была использована в Paradox 1.0 for Windows, а затем и в dBase 1.0 for Windows. ODAPI пока поддерживал только форматы dBase и Paradox, и мог выполнять запросы к обоим форматам при помощи механизма Query By Example (QBE), пришедшего из Paradox for DOS.

справка:
драйверы ODBC 1.0 от Microsoft впервые появились в августе 1993 года. Информация из MSDN.


Всего через полгода, в сентябре 1993, ODAPI 1.1 уже поддерживала работу с SQL-серверами Interbase, Oracle, Sybase и Microsoft.

Версия 2.0 была переименована в IDAPI (слово Open было заменено на Integrated), и работами по расширению и стандартизации этого интерфейса уже занимался не только Borland, а целый комитет с IBM, Novell и Wordperfect включительно. В этой версии появился Local SQL - ядро для выполнения запросов SQL к локальным форматам данных, и IDAPtor - механизм для подключения ODBC-драйверов к IDAPI.

Последняя 16-ти разрядная версия IDAPI 2.5 использовалась в Delphi 1. Далее, начиная с 3.0 (12 января 1996 года в составе Paradox 5.0 for Windows), пошли 32-разрядные версии. Собственно, на этом развитие функциональности BDE закончилось. Добавлялись новые драйверы для доступа к SQL-серверам DB2, Informix, в BDE 3.5 появились кэшированные обновления (CachedUpdates), появился драйвер FoxPro и сопряжение с DAO, но все это происходило на протяжении достаточно длительного срока - с 1996 по 2000.

С одной стороны, функциональность BDE можно назвать даже избыточной. С другой стороны повлияла конкуренция со стороны Microsoft, стандарта ODBC. Собственно, по функциональности ODBC является подмножеством BDE, но Microsoft в те годы предпринимала очень активные действия по продвижению ODBC, и главным в этом был выпуск ODBC SDK, с помощью которого любая фирма могла разработать собственный ODBC-драйвер (надо сказать, что в те годы их было огромное количество, причем большинство было весьма низкого качества и невысокой производительности). А BDE был более "закрытым". Например, BDE SDK так и не увидел свет, и был доступен разве что избранным (я оказался в их числе, и надо сказать, что качество BDE SDK и удобство написания драйверов было на высоте). С третьей стороны, к этому времени WordPerfect был куплен Novell, Paradox также был продан Novell, а затем Corel, а IBM похоже просто потеряла к IDAPI интерес.

Короче, комитет IDAPI распался, а Microsoft задавил конкуренцией.

Несмотря на перечисленные негативные моменты, BDE активно использовался не только самим Borland, но и многими другими фирмами. Это Novell (продукт InForms), ReportSmith (впоследствии купленный и проданный Borland), CrystalReports (вплоть до версии 5.0 использовал BDE) и так далее.

Архитектура

Увлекшись историей я немного пропустил, зачем все это (BDE) делалось. Частичная цель упоминалась выше - предоставить универсальное ядро доступа к локальным форматам данных. Основная - обеспечить прозрачную работу приложений как с локальными форматами, так и с SQL-серверами. Как сейчас помню, что именно удобство при работе с SQL-серверами рекламировалось как основное. Однако в последние 2-3 года именно эта возможность вызывала наибольшее количество нареканий. Давайте рассмотрим архитектуру BDE.

Основная работа с BDE производится посредством внешнего интерфейса IDAPI (IDAPI32.DLL). Формат данных выбирается в псевдониме (alias) соединения, и в принципе дальше работа с разными форматами ничем не отличается. В том числе и неважно, как работает приложение с BDE - через компоненты VCL DB, которые используют функции BDE, или напрямую (все равно компоненты используют те же функции BDE).

Дальше функции IDAPI транслируют вызовы в функции соответствующего драйвера. Если это драйвер локального формата (dBase, Paradox, FoxPro), то драйвер формата сам работает с соответствующими файлами (таблицами и индексами). Если это SQL Link, то вызовы транслируются в вызовы функций API клиентской части конкретного SQL-сервера. Для каждого сервера SQL Link свой.

IDAPTOR (соединитель с ODBC) и интерфейс к DAO работает точно также как и SQL Link, т.е. просто транслирует вызовы BDE в вызовы ODBC или DAO, непосредственно к формату не имея никакого отношения.

Если посмотреть на файлы BDE, то можно подробно рассмотреть его составные части.
IDAPI32.DLL
Основной интерфейс
BLW32.DLL, BANTAM.DLL
Языковые функции
*.BTL
Файлы с языковыми кодировками.
IDBAT32.DLL
Операции пакетного копирования данных
IDDR32.DLL
Модуль работы с Data Repository
IDASCI32.DLL
Драйвер для работы с текстовым форматом
IDDAO32.DLL
Драйвер трансляции вызовов к DAO
IDODBC32.DLL
Драйвер трансляции вызовов к ODBC
IDPDX32.DLL
Драйвер для работы с форматом Paradox
IDDBAS32.DLL
Драйвер для работы с форматом dBase и FoxPro
IDQBE32.DLL
Ядро обработки запросов QBE
IDSQL32.DLL
Ядро обработки запросов SQL
SQLINT32.DLL
SQLLink-драйвер трансляции вызовов к Interbase API
SQLORA32.DLL
SQLLink-драйвер трансляции вызовов к Oracle Call Level Interface
SQL*32.DLL
Другие SQLLink-драйверы

Таким образом, при установке BDE "лишние" файлы можно без проблем выкинуть.

Также, надеюсь, понятно, почему BDE "не работает" с SQL-сервером, если не установлена клиентская часть этого сервера (то же самое по отношению к DAO - без дистрибутива DAO BDE не будет работать с файлами MS Access). Вообще клиентские части SQL-серверов несовместимы между собой абсолютно. Поэтому невозможно написать универсальный SQL Link.

Данный рисунок и список файлов, возможно, развеет популярный миф о том, что Delphi хорошо приспособлена для работы с Interbase. Как видите, Interbase для Delphi столь же равноправен, как скажем, Oracle или любой ODBC-драйвер. В отличие от продуктов Microsoft в BDE нет никаких "обходных" функций для работы со своими форматами, т.е. работа с IB ведется только через SQL Link (без sqlint32.dll BDE вообще не знает, что такое Interbase).

Отдельное место в архитектуре BDE и среди упомянутых файлов занимают Local SQL и QBE Engine. Эти механизмы запросов будут рассмотрены чуть дальше.

TTable и TQuery

TTable и TQuery являются основными компонентами, используемыми при программировании приложений баз данных (TStoredProc не в счет, и без него можно прекрасно обойтись, вызывая процедуры через select или execute в компоненте TQuery). TTable предоставляет доступ как к таблицам, а TQuery позволяет выполнять произвольные запросы. Если с TQuery все понятно - он выполняет тот запрос, который написан в свойстве TQuery.SQL - то TTable скрывает очень много подробностей своей работы от программиста. Без SQL Monitor увидеть все тонкости невозможно (если кто не знает - SQL Monitor находится в меню Database).

Итак, запустите Delphi, откройте SQL Monitor, положите на форму компонент TDatabase, подсоединитесь к серверу, затем положите компонент TTable, присоедините его к алиасу TDatabase и выберите любую таблицу из списка (свойство TableName). Переключитесь на SQL Monitor, сотрите все что там появилось, переключитесь обратно, и включите TTable.Active:=True; Смотрим в SQL Monitor (лог с самого начала):
первым запросом BDE хочет убедиться, что выбранная нами таблица существует.
второй запрос выбирает список полей выбранной таблицы, их названий, типов, условий проверки и т.п.
третий запрос выбирает информацию об индексах указанной таблицы. Определяется, есть ли среди них первичный ключ, и по каким полям построены индексы.
четвертый запрос почти повторяет второй, и выбирает информацию о полях - условия проверки, "вычисляемость" поля, допустимость NULL и прочее.
собственно, пятый запрос открывает таблицу, формируя запрос SELECT FIELD1, FIELD2, ... FROM TABLE ORDER BY PK_FIELD ASC.

Заметьте, что подобные запросы выполняются каждый раз при открытии таблицы (любой) компонентом TTable. Перечитывания этих данных можно избежать, если включить у используемого алиаса параметр ENABLE SCHEMA CACHE. При этом считанную первый раз информацию BDE размещает на диске в указанном каталоге (SCHEMA CACHE DIR) в специальном файле, кэширует информацию для SCHEMA CACHE SIZE количества таблиц, и держит эту информацию в кэше в течение SCHEMA CACHE TIME секунд (если -1, то вечно). Если структуры таблиц закэшированы, то при их изменении на сервере (например, добавили новое поле) приложение будет работать со старой структурой, что может вызвать серьезные проблемы в работе приложения. SCHEMA CACHE нужно использовать только тогда, когда структура базы данных определена окончательно и не изменяется. Если все же очень сильно хочется использовать кэширование структур таблиц, то не забывайте правильно установить параметр SCHEMA CACHE TIME. Или при первом за день подключении приложения к серверу сначала кэширование структур можно выключить, отсоединиться, включить и подсоединиться снова - таким образом в самом начале работы кэш структур таблиц будет создан, и будет использоваться в течение дня.

примечание:
параметры SCHEMA CACHE не имеют абсолютно никакого отношения к механизму Cached Updates или к кэшированию данных.


Вернемся к запросу, которым TTable открыл таблицу. В конце запроса стоит указание порядка сортирвки - ORDER BY FIELD ASC. По умолчанию TTable сортирует данные в порядке поля первичного ключа. И кстати, если пользоваться свойством TTable.IndexName, то все равно к запросу будет добавляться ORDER BY INDEXFIELD ASC. Таким образом получается, что свойство IndexName при работе с SQL-серверами бессмыслено. Вместо него нужно просто использовать свойство IndexFieldNames. Даже если в этом свойстве указать поле, по которому нет индекса, то все равно BDE "прицепит" к запросу ORDER BY FIELD ASC. Кстати, BDE абсолютно игнорирует направление индекса, и всегда в запросе добавляет ASC, даже если индекс по этому полю создан как DESCENDING (по убыванию). Получается, что отсортировать таблицу в TTable по убыванию нельзя.

примечание:
можно было бы отнести этот недостаток на SQL Link для IB, но вполне возможно что просто TTable не в состоянии кэшировать и обновлять данные, отсортированные по убыванию (см. дальше о кэше данных).


Кэширование данных

Как видно из предыдущего раздела, TTable работает с таблицами сервера не каким-то хитрым образом, а формируя самые нормальные SQL-запросы. И тут начинается самое интересное. Оказывается, при выполнении запроса сервер выдает записи клиенту (приложению) по очереди и по одной записи. Причем только "сверху вниз". Как только записи на сервере кончились, сервер сообщает клиенту об этом сигналом EOF вместо выдачи очередной записи. Конечно, в некоторых современных серверах есть произвольное позиционирование и проход по выборке не только сверху вниз но и в обратном порядке, но это требует от сервера достаточно больших ресурсов.

примечание:
разумеется, клиентская часть SQL-сервера может принимать записи от сервера "пачками". Но в любом случае получение записи инициируется только вызовом функции fetch, и по этой команде "выбирается" только одна запись. Т.е. приложение получает записи по одной независимо от того, буферизируются они на сервере/клиенте или нет.


Поскольку BDE - вещь универсальная, то он должен обеспечить возможность перемещения по записям вверх и вниз независимо от сервера. Т.е. он должен обеспечивать кэширование записей самостоятельно. Взял запись с сервера - положил в кэш. Это означает, что если вы открыли таблицу в 100 тысяч записей, и нажали в гриде Ctrl-End, то все 100 тысяч записей "приедут" к вам на клиентский компьютер. С таким пожиранием ресурсов надо как то бороться. Если в TQuery можно ограничить количество выбираемых записей условиями запроса, то в TTable этого сделать нельзя, поскольку как мы уже видели, TTable формирует запросы самостоятельно.

Живой и мертвый кэш, или TTable и TQuery

Для решения проблем кэширования TTable было введено два разных механизма кэширования. Для TTable - "живой" кэш, а для TQuery - "мертвый". Для простоты лучше сначала рассмотреть "мертвый" кэш. Вообще тут и рассматривать нечего - при перемещении по записям записи помещаются в кэш. Как только все записи помещены (пользователь "доехал" до конца выборки каким-либо способом), обращения к серверу прекращаются, и любые передвижения пользователя по записям совершаются только в кэше.

У "мертвого" кэша есть побочный эффект - если вызвать метод Locate для поиска записи, то TQuery принудительно выберет все записи в кэш, и только потом будет искать нужную запись в кэше. Собственно, пока запись не считана, неизвестно - попадает она под условие Locate или нет. Поэтому другим способом здесь искать записи невозможно.

"Мертвый" кэш существует до тех пор, пока запрос не будет закрыт (TQuery.Close).

Живой кэш более сложен, и для его понимания придется использовать чуть больше компонент на уже открытой в Delphi форме. Добавьте к TDatabase и TTable компоненты TDataSource и TDBGrid. Grid растяните по вертикали так, чтобы в нем было видно штук 5 записей (7, или 9, не больше). Желательно чтобы в таблице при этом было не меньше 20-30 записей. Поместите кнопку рядом с Grid-ом, в которой на OnClick напишите
Table1.IndexFieldNames := 'FIELD';



где FIELD - любое имя поля открытой таблицы, главное чтобы не поле первичного ключа. Наличие индекса по этому полю не обязательно.

Скомпилируйте приложение, запустите его (не забыв запустить SQL Monitor). Поместите курсор грида посередине, как показано на рисунке (на содержимое грида не обращайте внимания - у вас оно будет совершенно другим, в зависимости от выбранной таблицы).

Сотрите все в SQL Monitor. И нажмите кнопку Button1. Теперь возвращаемся к началу лога в SQL Monitor:
первый запрос выбирает значение поля, по которому нужно отсортировать данные.
второй запрос выбирает данные от текущей записи и "выше" - см добавку WHERE FIELD < ? ORDER BY FIELD DESC
третий запрос выбирает запись, которую нужно поместить на место текущей (выборка запомненного первым запросом значения). Кстати, этот запрос у меня почему-то выполнился аж три раза (BDE 5.1.1). Раньше он обычно выполнялся всего один раз.
четвертый запрос выбирает данные от текущей записи и ниже - см. добавку WHERE FIELD > ? ORDER BY FIELD ASC

Вот это и есть "живой" кэш. Т.е. при любых операциях перемещения по набору данных, отличных от перемещения на одну запись (или PageUp/PageDown) вверх или вниз, TTable уничтожает текущий кэш, перечитывает данные столь экзотическим образом, и создает новую копию кэша. По количеству вызовов isc_dsql_fetch вы можете понять, что как "вверх" так и "вниз" от текущей записи второй и четвертый запросы выбрали ровно столько записей, сколько помещается в Grid. Если вы продолжите движение курсором по одной строке вверх или вниз, то увидите каким способом (зачастую неэффективным) BDE довыбирает необходимые записи (особенно неэффективность проявляется при движении вверх).

Если же курсор находится вверху или внизу грида, то вместо трех запросов отображения данных будет два - выборка текущей записи и выборка записей только вверх или вниз от текущей записи.

Существенный момент - выборка "вверх" всегда использует сортировку по убыванию. Если по полю сортировки нет индекса по убыванию, то Interbase (или другой сервер) будет сортировать результат в памяти или на диске, что существенно медленнее сортировки с использованием индекса. Поэтому "резкие" перемещения, например в конец таблицы при помощи Ctrl-End будут приводить к значительной паузе, пока сервер отсортирует данные и выдаст резульат. Повысить скорость в этом случае можно только использованием ClientDataSet, который сортирует кэш вместо выдачи серверу SQL-запросов.

Locate в живом кэше в отличие от мертвого кэша выполняется намного быстрее, т.к. TTable применяет ту же самую технику очистки кэша для поиска нужной записи.

BDE32 приложения в ptp-сети

Вторник, 06 Октября 2009 г. 22:30 + в цитатник
Использование BDE32-приложений в Peer-To-Peer сети

Сетью Peer-To-Peer (сеть, где каждая машина действует как клиент и как сервер) может быть одна из следующих сетей, включая другие сетевые платформы, совместимые с ними:
Windows 95
Windows NT
Lantastic
Netware Lite
BDE автоматически обнаруживает таблицы на сетевом диске, но он не может их определить на dedicated сервере или server/client. Dedicated-сервера уведомляют приложение клиента о том, что файл был изменен или заблокирован. Данная функциональность отсутствует в Peer-To-Peer (не-dedicated) сетях. Для ее включения в сетях Peer-To-Peer, установите "LOCAL SHARE" в TRUE в BDE Configuration Utility на странице System. Это должно быть сделано на всех клиентах BDE, которые имеют доступ к таблицам в сетях, указанных выше. В случае файловых серверов Novell данное требование не является необходимым.

Если используемые таблицы - таблицы Paradox, они также должны использовать каталог с сетевым контролем. Данный каталог должен находиться в сети для всех используемых клиентских приложений. Хорошим стилем считается использование отдельного каталога для приложения, сети и таблиц. Поясним примером:
<Каталог общего доступа>
|
|--- <Каталог таблиц>
|--- <Каталог Exe-файлов>
|--- <Сетевой каталог>
Существуют две различных среды BDE, которые необходимо принимать во внимание:
Использование только 32-битных приложений BDE.
Использование только 32-битных приложений BDE совместно с 16-битными.
Установка только для 32-битных приложений

32-битное BDE полностью поддерживает соглашение об путях UNC вместе с длинными именами файлов. Рекомендуется использование соглашения UNC для всех сетевых соединений BDE. UNC позволяет обойтись без подключения (mapped) сетевых дисков. Это позволяет иметь доступ к таблицам и сетевым каталогам без необходимости заставлять пользователя подключать сетевые диски. UNC имеет следующий синтаксис:
\<Имя сервера>\<Имя каталога общего доступа>\<Путь к каталогу>+<Имя файла>
Вот простой пример стандартного псевдонима (alias) BDE с использованием UNC:
Псевдоним: MyUNCAlias
Тип: STANDARD
Путь: \\FooServer\FooShare\Sharedir\Tables
Драйвер по умолчанию: Paradox
Сетевой каталог может быть установлен и таким способом:
Драйвер: Paradox
Сетевой каталог: \\FooServer\FooShare\Sharedir\NetDir
Сетевой каталог может быть установлен во время выполнения приложения с помощью session.netfiledir (Delphi) или DbiSetProp (C++ / Delphi)

Если по какой-либо причине UNC не может использоваться в 32-битных приложениях, следуйте за следующими инструкциями для среды с 32-битными и 16-битными приложениями BDE.

Установка для 16-битных и 32-битных приложений BDE

Поскольку 16-битное Windows API не поддерживает UNC, ее не поддерживает и 16-битное BDE. Для того, чтобы позволить приложениям иметь общий доступ к таблицам, все клиенты должны подключить один и тот же каталог на сервере. Если сервер также используется и в качестве клиента, то все другие клиенты должны подключить его корневой каталог диска. Логический диск при этом у клиентов может быть разным. Вот несколько примеров с работающими и неработающими настройками:
Клиент1:
Путь: X:\Каталог общего доступа\Таблицы
Клиент2:
Путь: X:\Каталог общего доступа\Таблицы
Работоспособно

Клиент1: (Также машина с таблицами):
Путь: X:\Каталог общего доступа\Таблицы
Клиент2:
Путь: X:\Каталог общего доступа\Таблицы
Работоспособно

Клиент1: (Также машина с таблицами):
Путь: C:\Каталог общего доступа\Таблицы
Клиент2:
Путь: X:\Каталог общего доступа\Таблицы
Клиент3:
Путь: R:\Каталог общего доступа\Таблицы
Работоспособно

Клиент1:
Путь: X:\Каталог общего доступа\Таблицы
Клиент2:
Путь: X:\Таблицы (Где "X:\Таблицы" реально -
"X:\Каталог общего доступа\Таблицы", но имеющий
общий доступ в "Каталог общего доступа")
Неработоспособно. BDE должен иметь возможность
иметь доступ к файлу Network Control (управление
сетью).
Итог (установки для сетей Peer-To-Peer):

16- и/или 32-битные приложения:
В BDE Configuration Utility установите "LOCAL SHARE" в TRUE.
Не используйте UNC-имена.
Не используйте таблицы с длинными именами файлов.
Убедитесь в том, что все клиенты подключены к одному и тому же каталогу на сервере.
Только 32-битные приложения:
В BDE Configuration Utility установите "LOCAL SHARE" в TRUE.
Для получения доступа к сетевому каталогу и каталогу с таблицами используйте UNC-имена.
При невыполнении описанных выше шагов пользователи могут блокировать таблицы с получением следующей ошибки:
"Directory is controlled by other .NET file."
(Каталог управляется другим .NET-файлом)
"File: PDOXUSRS.LCK" ("Файл: PDOXUSRS.LCK")
"Directory: " (Каталог: )
ИЛИ

"Multiple .NET files in use."
(Используются несколько .NET-файлов.)
"File: PDOXUSRS.LCK"
(Файл: PDOXUSRS.LCK)

Как программно создать Alias 2

Вторник, 06 Октября 2009 г. 22:29 + в цитатник
Есть три способа сделать это... No1 годится только для постоянных псевдонимов BDE. No2 работает с BDE и локальными псевдонимами, и No3 работает с BDE и локальными псевдонимами, используя "тяжелый" путь, через вызовы DBI.
function GetDBPath1(AliasName: string): TFileName;
var
ParamList: TStringList;
begin
ParamList := TStringList.Create;
with Session do
try
GetAliasParams(AliasName, ParamList);
Result := UpperCase(ParamList.Values['PATH']) + '';
finally
Paramlist.Free;
end;
end;

function GetDBPath2(AliasName: string): TFileName;
var
ParamList: TStringList;
i: integer;
begin
ParamList := TStringList.Create;
with Session do
try
try
GetAliasParams(AliasName, ParamList);
except
for i := 0 to pred(DatabaseCount) do
if (Databases[i].DatabaseName = AliasName) then
ParamList.Assign(Databases[i].Params);
end;
Result := UpperCase(ParamList.Values['PATH']) + '';
finally
Paramlist.Free;
end;
end;

function GetDBPath3(ATable: TTable): TFileName;
var
TblProps: CURProps;
pTblName, pFullName: DBITblName;
begin
with ATable do
begin
AnsiToNative(Locale, TableName, pTblName, 255);
Check(DBIGetCursorProps(Handle, TblProps));
Check(DBIFormFullName(DBHandle,
pTblName,
TblProps.szTableType,
pFullName));
Result := ExtractFilePath(StrPas(pFullName));
end;
end;

Задание псевдонима программным путем

Вторник, 06 Октября 2009 г. 22:24 + в цитатник
Эта информация поможет вам разобраться в вопросе создания и использования ПСЕВДОНИМОВ баз данных в ваших приложениях.

Вне Delphi создание и конфигурирование псевдонимов осуществляется утилитой BDECFG.EXE. Тем не менее, применяя компонент TDatabase, вы можете в вашем приложении создать и использовать псевдоним, не определенный в IDAPI.CFG.

Важно понять, что создав псевдоним, использовать его можно только в текущем сеансе вашего приложения. Псевдонимы определяеют расположение таблиц базы данных и параметры связи с сервером баз данных. В конце концов, вы получаете преимущества использования псевдонимов в пределах вашего приложения без необходимости беспокоиться о наличии их в конфигурационном файле IDAPI.CFG в момент инициализации приложения.

Некоторые варианты решения задачи:

Пример #1:

Пример #1 создает и конфигурирует псевдоним для базы данных STANDARD (.DB, .DBF). Псевдоним затем используется компонентом TTable.

Пример #2:

Пример #2 создает и конфигурирует псевдоним для базы данных INTERBASE (.gdb). Псевдоним затем используется компонентом TQuery для подключения к двум таблицам базы данных.

Пример #3:

Пример #3 создает и конфигурирует псевдоним для базы данных STANDARD (.DB, .DBF). Демонстрация ввода псевдонима пользователем и его конфигурация во время выполнения программы.

Пример #1: Используем базу данных .DB или .DBF (STANDARD)
Создаем новый проект.
Располагаем на форме следующие компоненты: - TDatabase, TTable, TDataSource, TDBGrid, and TButton.
Дважды щелкаем на компоненте TDatabase или через контекстное меню (правая кнопка мыши) вызываем редактор базы данных.
Присваиваем базе данных имя 'MyNewAlias'. Это имя будет выполнять роль псевдонима в свойстве DatabaseName для компонентов типа TTable, TQuery, TStoredProc.
Выбираем в поле Driver Name (имя драйвера) пункт STANDARD.
Щелкаем на кнопке Defaults. Это автоматически добавляет путь (PATH=) в секцию перекрытых параметров (окно Parameter Overrides).
Устанавливаем PATH= to C:\DELPHI\DEMOS\DATA (PATH=C:\DELPHI\DEMOS\DATA).
Нажимаем кнопку OK и закрываем окно редактора.
В компоненте TTable свойству DatabaseName присваиваем 'MyNewAlias'.
В компоненте TDataSource свойству DataSet присваиваем 'Table1'.
В компоненте DBGrid свойству DataSource присваиваем 'DataSource1'.
Создаем в компоненте TButton обработчик события OnClick.
procedure TForm1.Button1Click(Sender: TObject);
begin
Table1.Tablename:= 'CUSTOMER';
Table1.Active:= True;
end;


Запускаем приложение.

*** В качестве альтернативы шагам 3 - 11, вы можете включить все эти действия в сам обработчик:
procedure TForm1.Button1Click(Sender: TObject);
begin
Database1.DatabaseName:= 'MyNewAlias';
Database1.DriverName:= 'STANDARD';
Database1.Params.Clear;
Database1.Params.Add('PATH=C:\DELPHI\DEMOS\DATA');
Table1.DatabaseName:= 'MyNewAlias';
Table1.TableName:= 'CUSTOMER';
Table1.Active:= True;
DataSource1.DataSet:= Table1;
DBGrid1.DataSource:= DataSource1;
end;



Пример #2: Используем базу данных INTERBASE
Создаем новый проект.
Располагаем на форме следующие компоненты: - TDatabase, TQuery, TDataSource, TDBGrid, and TButton.
Дважды щелкаем на компоненте TDatabase или через контекстное меню (правая кнопка мыши) вызываем редактор базы данных.
Присваиваем базе данных имя 'MyNewAlias'. Это имя будет выполнять роль псевдонима в свойстве DatabaseName для компонентов типа TTable, TQuery, TStoredProc.
Выбираем в поле Driver Name (имя драйвера) пункт INTRBASE.
Щелкаем на кнопке Defaults. Это автоматически добавляет путь (PATH=) в секцию перекрытых параметров (окно Parameter Overrides).
SERVER NAME=IB_SERVEER:/PATH/DATABASE.GDB
USER NAME=MYNAME
OPEN MODE=READ/WRITE
SCHEMA CACHE SIZE=8
LANGDRIVER=
SQLQRYMODE=
SQLPASSTHRU MODE=NOT SHARED
SCHEMA CACHE TIME=-1
PASSWORD=
Устанавливаем следующие параметры
SERVER NAME=C:\IBLOCAL\EXAMPLES\EMPLOYEE.GDB
USER NAME=SYSDBA
OPEN MODE=READ/WRITE
SCHEMA CACHE SIZE=8
LANGDRIVER=
SQLQRYMODE=
SQLPASSTHRU MODE=NOT SHARED
SCHEMA CACHE TIME=-1
PASSWORD=masterkey
В компоненте TDatabase свойство LoginPrompt устанавливаем в 'False'. Если в секции перекрытых параметров (Parameter Overrides) задан пароль (ключ PASSWORD) и свойство LoginPrompt установлено в 'False', при соединении с базой данный пароль запрашиваться не будет. Предупреждение: при неправильно указанном пароле в секции Parameter Overrides и неактивном свойстве LoginPrompt вы не сможете получить доступ к базе данных, поскольку нет возможности ввести правильный пароль - диалоговое окно "Ввод пароля" отключено свойством LoginPrompt.
Нажимаем кнопку OK и закрываем окно редактора.
В компоненте TQuery свойству DatabaseName присваиваем 'MyNewAlias'.
В компоненте TDataSource свойству DataSet присваиваем 'Query1'.
В компоненте DBGrid свойству DataSource присваиваем 'DataSource1'.
Создаем в компоненте TButton обработчик события OnClick.
procedure TForm1.Button1Click(Sender: TObject);
begin
Query1.SQL.Clear;
Query1.SQL.ADD(
'SELECT DISTINCT * FROM CUSTOMER C, SALES S
WHERE (S.CUST_NO = C.CUST_NO)
ORDER BY C.CUST_NO, C.CUSTOMER');
Query1.Active:= True;
end;


Запускаем приложение.

Пример #3: Ввод псевдонима пользователем

Этот пример выводит диалоговое окно и создает псевдоним на основе информации, введенной пользователем.

Директория, имя сервера, путь, имя базы данных и другая необходимая информация для получения псевдонима может быть получена приложением из диалогово окна или конфигурационного .INI файла.

Выполняем шаги 1-11 из примера #1.
Пишем следующий обработчик события OnClick компонента TButton:
procedure TForm1.Button1Click(Sender: TObject);
var
NewString: string;
ClickedOK: Boolean;
begin
NewString := 'C:';
ClickedOK := InputQuery('Database Path',
'Path: --> C:\DELPHI\DEMOS\DATA', NewString);
if ClickedOK then
begin
Database1.DatabaseName := 'MyNewAlias';
Database1.DriverName := 'STANDARD';
Database1.Params.Clear;
Database1.Params.Add('Path=' + NewString);
Table1.DatabaseName := 'MyNewAlias';
Table1.TableName := 'CUSTOMER';
Table1.Active := True;
DataSource1.DataSet := Table1;
DBGrid1.DataSource := DataSource1;
end;
end;

Как открыть базу данных Microsoft Access .MDB в Delphi

Вторник, 06 Октября 2009 г. 22:21 + в цитатник
ADO

Если у Вас Delphi 5 Enterprise или Delphi 5 Professional с ADO Express, то Вы можете использовать компонент ADOTable и в его свойстве ConnectionString настроить (build) подключение как базе данных MS Access. Например:

Provider=Microsoft.Jet.OLEDB.4.0;
User ID=Admin;
Password=Password;
Data Source=D:\Path\dbname.mdb;
Mode=ReadWrite;
Extended Properties=" " ;
Persist Security Info=False;
Jet OLEDB:System database=" " ;
Jet OLEDB:Registry Path=" " ;
Jet OLEDB:Database Password=" " ;
Jet OLEDB:Engine Type=5;
Jet OLEDB:Database Locking Mode=1;
Jet OLEDB:Global Partial Bulk Ops=2;
Jet OLEDB:Global Bulk Transactions=1;
Jet OLEDB:New Database Password=" " ;
Jet OLEDB:Create System Database=False;
Jet OLEDB:Encrypt Database=False;
Jet OLEDB:Don't Copy Locale on Compact=False;
Jet OLEDB:Compact Without Replica Repair=True;
Jet OLEDB:SFP=False

При этом будет открыта база данных D:\Path\dbname.mdb, будет использован драйвер ADO для базы данных Access (Microsoft.Jet.OLEDB.4.0). Имя пользователя будет Admin без пароля (эти значения присваиваются поумолчанию при создании базы Access). Если Вы всё-таки захотите использовать пароль, то его надо будет задать в ствойстве Jet OLEDB:Database Password. Если у Вас установлен режим безопасности, то необходимо указать файл .MDW или .MDA в свойстве Jet OLEDB:System database.

BDE

Так же для открытия базы данных Access можно воспользоваться BDE которая содержит родной драйвер (MSACCESS). В компоненте Database установите следующие свойства:

DatabaseName = any_name (или alias_name)
DriverName = MSACCESS
LoginPrompt = False
Params = PATH=d:\path
DATABASE NAME=d:\path\filename.mdb
TRACE MODE=0
LANGDRIVER=Access General
USER NAME=Admin
PASSWORD=your_password
OPEN/MODE=READ/WRITE
SQLPASSTHRU MODE=NOT SHARED

Значения свойства DatabaseName объекта Database, это то, которое Вы будете использовать в свойстве DatabaseName компонентов Table и Query, которые представляют таблицы и запросы для этой базы данных (тем самым связывая их с объектом Database).

BDE+ODBC

В случае с базой данных Access, BDE предоставляет драйвер, однако существует множество других баз, для которых в BDE драйвера нет, но для которых есть драйвер ODBC. ODBC обычно используется для небольших баз данных или в приложениях, в которых присутствуют только операции импорта/экспорта...

Ниже приведён пример использования драйвера ODBC с BDE для открытия базы данных Access:

Создайте DSN (Data Source Name) для Вашей базы данных (используя апплет ODBC Data Sources в панели управления).
Кликните на закладку " System DSN" или " User DSN"
Кликните по кнопке " Add..."
Выберите " Microsoft Access Driver (*.mdb)" и нажмите ENTER. Появится диалоговое окошко " ODBC Microsoft Access Setup" .
Задайте имя в текстовом окошке Data Source Name (без пробелов и без специальных символов).
Кликните по кнопке " Select..." чтобы выбрать нужный файл .MDB.
Если у Вас установлена схема безопасноти, то выберите радио кнопку " Database" в " System Database" , а затем кликните кнопку " System database..." , чтобы указать файл рабочей группы .MDW или .MDA.
Если Вы хотите указать имя пользователя и пароль, то нажмите кнопку " Advanced..." . Данный способ защиты является низкоуровневым, так как любой, кто имеет доступ к Вашей машине может спокойно посмотреть свойства DSN. Если Вам необходим более высокий уровень защиты, то задавать имя пользователя и пароль необходимо на стадии открытия базы данных (см. ниже).
В заключении нажмите " OK" , после чего Ваш DSN будет сохранён.
В Delphi установите свойства компонента TDatabase:
В DatabaseName задайте имя, которое указали в DSN.
Если Вы хотите, чтобы пользователя спрашивали имя и пароль, то установите LoginPrompt в True.
Если Вы не хотите использовать стандартный диалог имени и пароля (или если имя и пароль будут задаваться программно), то установите LoginPrompt в False и задайте свойство Params (или задайте эти свойства по ходу выполнения программы):
USER NAME=your_username
PASSWORD=your_password
Свяжите компоненты TTable или TQuery с компонентом TDatabase, как рассказывалось Выше, просто указав тоже имя (которое было задано в DSN) в их соответствующих свойствах DatabaseName.


Поиск сообщений в xpackpackax
Страницы: 24 ... 22 21 [20] 19 18 ..
.. 1 Календарь