• Калькуляция
  • Код определения возраста
  • Как пересчитать все вычисляемые поля (Calculated fields) без переоткрытия TDataSet?
  • Как создать вычисляемые поля во время исполнения программы (Calculated fields at RunTime)?
  • Доступ 
  • Хитрости многопользовательского доступа к БД
  • Выполнение запросов к базе данных в фоне
  • Получение физического пути к таблице
  • Cancel в связанных таблицах
  • Отображение формы ввода в БД CUSTOMER из рабочей формы ORDER
  • Отображение определенных полей БД
  • Из базы данных в переменные
  • Получение информации о таблице
  • Обмен данными между TMemoField и TMemo
  • Если в транзакции изменена какая-то таблица, то для другого пользователя блокируется вся таблица, до окончания транзакции. Как лечить?
  • Форма Мастер-Деталь
  • Подскажите как правильно показать на экpане и сохранить в базе картинку формата JPEG?
  • Как исключить показ поля P_RECNO?
  • Информация из одной таблицы и набора данных на двух формах
  • Как при вводе информации в БД автоматически вставлять SEQUENCE?
  • Помещение переменной в Memo-поле
  • Индикатор прогресса выполнения запроса
  • Обновление данных БД из модальной формы
  • Как записать в BLOB-поле большой текст (>255 байт) из Delphi?
  • Блокировка таблицы
  • Каким драйвером пользуется TDATABASE?
  • Как создать новый запрос и скопировать туда точно такие же описания полей?
  • Запись потока в BLOB-поле
  • Как я могу выбрать на клиента только часть данных с определенной позиции из набора данных на сервере?
  • Отследить изменение данных?
  • Как достучаться до методов сервера приложений из TClientDataSet?
  • Я включил dbclient.dll в секцию `additional files` опций распространения по web, но этот файл никогда не загружается на клиента. Как это исправить?
  • Как можно использовать TClientDataSet в локальном приложении с таблицами Paradox, без использования компонент TProvider и TRemoteServer?
  • Hе получается открыть таблицу, созданную в InterBase с DEFAULT CHARACTER SET WIN1251. Оно говорит, что `WIN1251 undefined`
  • Создание 
  • Функции редактора полей во время выполнения программы
  • Производная TIntegerField
  • Создание новой таблицы на основе структуры другой таблицы
  • Создание уникального ID для новой записи
  • Динамическое создание таблицы и полей во время выполнения программы
  • Проблема с AddIndex
  • Как создать БД в кодировке CP1251?
  • Таблицы в памяти
  • Поиск 
  • FindKey для нескольких полей
  • Поиск существующей записи перед тем, как она будет вставлена
  • Поиск фраз и записей переменной длины
  • dBase 
  • Текущий номер записи набора данных
  • Как открыть индексированную таблицу dBase, если отсутствует файл индекса?
  • Определение удаления записей в .DBF
  • Определение номера записи в таблице dBASE
  • Пакование таблиц dBASE II
  • Пакование таблиц dBASE III
  • Пакование таблиц dBASE IV
  • Показ удаленных записей в таблице dBASE
  • Пароль на dBASE-файлы
  • Показ меток 'удаленных' записей в dBASE-файлах
  • DB2 
  • Как заставить работать DB2 через протокол IPX?
  • Почему DB2 ругается на Create Trigger?
  • Модули данных 
  • Модуль данных для каждого MDIChild
  • Как передать UserName и Password в удаленный модуль данных (remote datamodule)?
  • Paradox 
  • Byte-поля Paradox
  • Доступ к таблицам Paradox на CD или c флагом только для чтения
  • Нечувствительный к регистру поиск в первичном индексе Paradox
  • Создание таблицы Paradox
  • DBEdit и реальные значения
  • Почему при создании таблицы Paradox с первичным нечувствительным к регистру индексом вываливается ошибка?
  • Как сменить пароль (master password) для таблицы Paradox?
  • Что нужно сделать для нормальной работы в одноранговой сети с базами Paradox?
  • Переиндексирование файлов Paradox в пределах моей программы
  • Разное 
  • Помещение Memo-файла с ASCII-разделителем в Memo-поле таблицы
  • Почему не всегда верно обновляются IndexDefs по Update?
  • БД-дерево взаимоотношений
  • DBGrid и Memo-поля
  • Убывающий индекс
  • Как работать из Delphi напрямую с MS ADO (Microsoft Active Data Objects)?
  • Как засунуть в качестве паpаметpа хpанимой пpоцедуpы стpоку длиной более 255 символов? И вообще, как использовать паpаметpы SP, если они BLOB?
  • Дублирование набора записей
  • Как программно изменить LangDriver для таблиц dBase и Paradox?
  • Существует ли средство для вывода определения структуры таблицы?
  • У меня есть текстовые файлы, которые я хочу использовать в запросах к DB2, но не хочу создавать из них постоянные таблицы в базе. Что делать?
  • Список структуры полей таблицы
  • Создание индексного файла из Delphi
  • Контекстное меню на основе базы данных
  • Корректное закрытие базы данных приложением Delphi
  • Изменение свойств базы данных во время выполнения приложения
  • Как мне задать выражение по умолчанию для объекта TField?
  • После того, как я использовал правый щелчок мыши для создания функции-провайдера, как мне снова выполнить команду контекстного меню `Export from Table`?
  • Как работать с новыми, своими интерфейсами в RemoteDataModule?
  • Database Desktop показывает содержимое таблиц шрифтом без русских букв
  • Базы данных 

    Калькуляция

    Код определения возраста

    Delphi 1

    Вызовите диалог редактирования полей (Fields Editor), дважды щелкнув на компоненте TTable или TQuery, расположенном на вашей форме (или выбрав в контекстном меню пункт Fields Editor). Добавьте все поля, с которыми вы хотите работать в форме (даже если вы хотите, чтобы они были невидимы, но вам необходим к ним доступ – для таких полей установите свойство visible в false). Затем щелкните на «Define…» (определить) для добавления вычисляемого поля. Введите имя вычисляемого поля, отличающееся от имен других полей таблицы, выберите тип (вероятно, StringField) и задайте длину (20 будет в самый раз). Убедитесь в том, что напротив поля 'calculated' стоит галочка. Затем создайте для вашего объекта TTable или TQuery обработчик события 'OnCalcFields'. В этом обработчике вы берете значения реальных полей таблицы, делаете вычисления, и помещаете результаты в объект вычисляемого поля, который вы только что создали. После этого значение выводится в TDBGrid, или в элементе управления TDBText, если вы решили использовать форму вместо табличной сетки.

    Наша функция должна достичь цели, обрабатывая значения лет и месяцев. Поскольку не все месяцы имеют одно и то же количество дней, я просто брал среднее число, поэтому результат может быть не очень точен, но большинство людей это удовлетворяет:

    function AgeStr(aDate: TDateTime): string;

    var

     DaysOld: Double;

     Years, Months: Integer;

    begin

     DaysOld:= Date – aDate;

     Years:= Trunc(DaysOld / 365.25);

     DaysOld:= DaysOld – (365.25 * Years);

     Months:= Trunc(DaysOld / 30.41);

     Result:= Format('%d лет, %d месяцев',[Years, Months]);

    end;

    В моем случае метод OnCalcFields выглядит так:

    procedure TEntryForm.TableNameOrderCalcFields(DataSet: TDataset);

    begin

     TableNameOrderAge.AsString := AgeStr(TableNameOrderDateOfBirth.AsDateTime);

    end;
     

    Как пересчитать все вычисляемые поля (Calculated fields) без переоткрытия TDataSet?

    Одной строкой

    Nomadic отвечает:

    Resync([rmExact, rmCenter]);

    Как создать вычисляемые поля во время исполнения программы (Calculated fields at RunTime)?


    Nomadic отвечает:

    Смотрите книгу "Developing Custom Delphi Components" от Рэя Конопки.

    Здесь немного исправленный пример из этой книги


    function TMyClass.CreateCalcField(const AFieldName: string; AFieldClass: TFieldClass; ASize: Word): TField;

    begin

     Result := FDataSet.FindField( AFieldName ); // Field may already exists!

     if Result<>nil then Exit;

     if AFieldClass = nil then

     begin

      DBErrorFmt( SUnknownFieldType, [AFieldName] );

     end;

     Result := FieldClass.Create( Owner );

     with Result do

     try

      FieldName := AFieldName;

      if (Result is TStringField) or (Result is TBCDField) or (Result is TBlobField) or (Result is TBytesField) or (Result is TVarBytesField) then

      begin

       Size := ASize;

      end;

      Calculated := True;

      DataSet := FDataset;

      Name := FDataSet.Name + AFieldName;

     except

      Free; // We must release allocated memory on error!

      raise;

     end;

    end;
     

    Доступ 

    Хитрости многопользовательского доступа к БД

    Вот некоторые хитрости, могущие помочь в разработке баз многопользовательского доступа:

    В модуле DBIPROCS Delphi 1.0 и в BDE.INT 2.0 существует классная функция с именем DBISETLOCKRETRY(n).

    Синтаксис – DBISetLockRetry(n), где n – количество секунд ожидания перед повторной попыткой вставки, редактирования или другой операцией с таблицей. DBISetLockRetry(-1) будет бесконечно пытаться получить доступ к вашей таблице.

    Хорошее место для вызова функции – обработчик события TableAfterOpen. В этом случае все, что вам нужно сделать, это:

    DBISetLockRetry(x);

    Если вы используете Delphi 1.0, не забудьте включить в вашу программу DBIProcs. В Delphi 2.0 включите BDE.

    Мой заказчик и я разработали многопользовательскую базу данных по вашему рецепту, до этого наши пользователи получали сообщения «File is Locked» (файл заблокирован), «Table is Busy» (таблица занята), «Record Locked» (запись заблокирована) и др. Мы также пробовали Session.Netdir, но он не помог нам. Поскольку мы добавили в код эту строку, никаких блокировок не было. Частота обращений пользователей к базе достаточно велика (80 kpm). Мы разработали «измеритель скорости доступа» с 2 открытыми сессиями на двух компьютерах в сети Novell 4.1. Две сессии занимались вставкой, две другие редактированием, а мы сами занимались посылкой данных с частотой около 65 записей в минуту в течение операций редактирования и 85 в течение вставки. Сеть чуть не захлебнулась от такой работы. Утилизация файлового сервера была до нас около 60%. Не плохо для всего! Я думаю Borland необходимо задокументировать такой подход, чтобы другие не становились хакерами типа нас! :)

    Эти требования обязательны при разработке многопользовательских приложений Delphi с использованием файлов Dbase или Paradox.

    – Ted Bulmanski

    Выполнение запросов к базе данных в фоне

    Delphi 2

    Тема: Выполнение запросов к базе данных в фоновом потоке

    Данный документ объясняет как выполнить запрос в фоновом режиме, используя класс TThread. Для получения общей информации о классе TThread, пожалуйста обратитесь к документации Borland и электронной справке. Для понимания данного документа вам необходимо иметь представление о том, как работать с компонентами для работы с базами данных, поставляемых в комплекте с Delphi 2.0.

    Для осуществления потокового запроса необходимо выполнение двух требований. Во-первых, потоковый запрос должен находиться в своей собственной сессии с использованием отдельного компонента TSession. Следовательно, на вашей форме должен находиться компонент TSession, имя которого должно быть назначено свойству SessonName компонента TQuery, используемого для выполнения потокового запроса. Для каждого используемого в потоке компонента TQuery вы должны использовать отдельный компонент TSession. При использовании компонента TDataBase, для отдельного потокового запроса должен также использоваться отдельный TDataBase. Второе требование заключается в том, что компонент TQuery, используемый в потоке, не должен подключаться в контексте это потока к TDataSource. Это должно быть сделано в контексте первичного потока.

    Приведенный ниже пример кода иллюстрирует описываемый процесс. Данный модуль демонстрирует форму, которая содержит по два экземпляра следующих компонентов: TSession, TDatabase, TQuery, TDataSource и TDBGrid. Данные компоненты имеют следующие значения свойств:

    Session1

     Active True;

     SessionName "Ses1"


    DataBase1

     AliasName "IBLOCAL"

     DatabaseName "DB1"

     SessionName "Ses1"


    Query1

     DataBaseName "DB1"

     SessionName "Ses1"

     SQL.Strings "Select * from employee"


    DataSource1

     DataSet ""


    DBGrid1

     DataSource DataSource1


    Session2

     Active True;

     SessionName "Ses2"


    DataBase2

     AliasName "IBLOCAL"

     DatabaseName "DB2"

     SessionName "Ses2"


    Query2

     DataBaseName "DB2"

     SessionName "Ses2"

     SQL.Strings "Select * from customer"


    DataSource2

     DataSet ""


    DBGrid1

     DataSource DataSource2

    Обратите внимание на то, что свойство DataSet обоих компонентов TDataSource первоначально никуда не ссылается. Оно устанавливается во время выполнения приложения, и это проиллюстрировано в коде.

    unit Unit1;


    interface


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


    type

     TForm1 = class(TForm)

      Session1: TSession;

      Session2: TSession;

      Database1: TDatabase;

      Database2: TDatabase;

      Query1: TQuery;

      Query2: TQuery;

      DataSource1: TDataSource;

      DataSource2: TDataSource;

      DBGrid1: TDBGrid;

      DBGrid2: TDBGrid;

      GoBtn1: TButton;

      procedure GoBtn1Click(Sender: TObject);

     end;


     TQueryThread = class(TThread)

     private

    FSession: TSession;

      FDatabase: TDataBase;

      FQuery: TQuery;

      FDatasource: TDatasource;

      FQueryException: Exception;

      procedure ConnectDataSource;

      procedure ShowQryError;

     protected

      procedure Execute; override;

     public

      constructor Create(Session: TSession; DataBase: TDatabase; Query: TQuery; DataSource: TDataSource); virtual;

     end;


    var Form1: TForm1;


    implementation


    constructor TQueryThread.Create(Session: TSession; DataBase: TDatabase; Query: TQuery; Datasource: TDataSource);

    begin

     inherited Create(True); // Создаем поток c состоянием suspendend

     FSession := Session;     // подключаем все privat-поля

     FDatabase := DataBase;

     FQuery := Query;

     FDataSource := Datasource;

     FreeOnTerminate := True; // Устанавливаем флаг освобождения потока после его завершения

     Resume;                  // Продолжение выполнения потока

    end;


    procedure TQueryThread.Execute;

    begin

     try

    { Выполняем запрос и подключаем источник данных к компоненту TQuery, вызывая ConnectDataSource из основного потока(для этой цели используем Synchronize)}

      FQuery.Open;

      Synchronize(ConnectDataSource);

     except

    { Ловим исключение (если оно происходит) и его дескриптор в контексте основного потока (для этой цели используемSynchronize). }

      FQueryException := ExceptObject as Exception;

      Synchronize(ShowQryError);

     end;

    end;


    procedure TQueryThread.ConnectDataSource;

    begin

    FDataSource.DataSet := FQuery;  // Подключаем DataSource к TQuery

    end;


    procedure TQueryThread.ShowQryError;

    begin

    Application.ShowException(FQueryException); // Обрабатываем исключение

    end;


    procedure RunBackgroundQuery(Session: TSession; DataBase: TDataBase; Query: TQuery; DataSource: TDataSource);

    begin

    { Создаем экземпляр TThread с различными параметрами. }

     TQueryThread.Create(Session, Database, Query, DataSource);

    end;


    {$R *.DFM}


    procedure TForm1.GoBtn1Click(Sender: TObject);

    begin

    { Запускаем два отдельных запроса, каждый в своем потоке }

     RunBackgroundQuery(Session1, DataBase1, Query1, Datasource1);

     RunBackgroundQuery(Session2, DataBase2, Query2, Datasource2);

    end;


    end.

    Метод TForm1.GoBtn1Click является обработчиком события нажатия кнопки. Данный обработчик события дважды вызывает процедуру RunBackgroundQuery, это случается при каждой передаче новых параметров компонентам для работы с базой данных. RunBackgroundQuery создает отдельный экземпляр класса TQueryThread, передает различные компоненты для работы с базой данных в его конструктор, который, в свою очередь, назначает их закрытым полям TQueryThread.

    TQueryThread содержит две определенные пользователем процедуры: ConnectDataSource и ShowQryError. ConnectDataSource связывает FDataSource.DataSet с FQuery. Тем не менее, это делается в первичном потоке с помощью метода TThread.Synchronize. ShowQryError обрабатывает исключение в контексте первиного потока, также используя метод Synchronize. Конструктор Create и метод Execute снабжены подробными комментариями.

    Получение физического пути к таблице

    Delphi 2

    Тема: Получение физического пути к таблице

    Отправлено: Август 13, 1996

    Автор: Xavier Pacheco

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

    pDBDesc = ^DBDesc;

    DBDesc = packed record 2{ Описание данной базы данных }

     szName    : DBINAME; { Логическое имя (или псевдоним) }

     szText    : DBINAME; { Описательный текст }

     szPhyName : DBIPATH; { Физическое имя/путь }

     szDbType  : DBINAME; { Тип базы данных }

    end;

    Физическое имя/путь будет содержаться в поле szPhyName структуры DBDesc.

    Возможные значения, возвращаемые функцией DBIGetDatbaseDesc:

    DBIERR_NONE Описание базы данных для pszName было успешно извлечено. DBIERR_OBJNOTFOUND База данных, указанная в pszName, не была обнаружена.

    Приведенный ниже пример кода показывает как можно получить физический путь для компонента TTable, использующего псевдоним DBDemos:

    var

     vDBDesc: DBDesc;

     DirTable: String;

    begin

     Check(DbiGetDatabaseDesc(PChar(Table1.DatabaseName), @vDBDesc));

     DirTable := Format('%s\%s', [vDBDesc.szPhyName, Table1.TableName]);

     ShowMessage(DirTable);

    end;
     

    Cancel в связанных таблицах

    Delphi 1

    В книге 'Delphi unleashed' на странице 520 автор написал:

    '…, вы можете делать откат все время до тех пор, пока прямо или косвенно не сделаете постинг данных.'

    Моя проблема дважды возникала в случае ExTable.Edit в различных процедурах. Код был примерно таким:

    Procedure1 …

    begin

     ExTable.Edit;

     ExTable.FieldByName('').asstring := ;

     …

    end;


    procedure2

    begin

     ExTable.Edit;

     …

    end;

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

    Отображение формы ввода в БД CUSTOMER из рабочей формы ORDER

    Delphi 1

    В моем проекте имеется подобная функция, определяющая количество элементов:

    В обработчике события OnClick я создаю форму ввода данных и вывожу ее командой .ShowModal. Затем я проверяю результат .ModalResult – и, если он равен mrOk, я передаю запись, в противном случае делаю отмену.

    Я поместил имя модуля с формой ввода данных в список используемых модулей главной формы. Вот базовая схема моего кода: 

    procedure TFrmItemNav.BtnChangeLocClick(Sender: TObject);

    {var DlgItemLoc: TDlgItemLoc;}

    begin

     DlgItemLoc := TDlgItemLoc.Create(FrmItemNav);

     DlgItemLoc.ShowModal;

     if DlgItemLoc.ModalResult = mrOk then

      {делаем все, что необходимо для постинга данных}

     else

      {очищаем и делаем Cancel};

     DlgItemLoc.Free;

    end;
     

    Отображение определенных полей БД

    Delphi 1

    Вот что можно сделать во время выполнения программы: 

    Table1.FieldByName(RemovedFieldName).Visible := False;

    или 

    Table1.Field[removedFieldNumber-1].Visible := false;
     

    Из базы данных в переменные

    Delphi 1

    Примерно так вы можете программным путем извлечь содержимое поля: 

    aValue := TMyTable.FieldByName('SomeField').AsText;

    или 

    aValue := TMyTable.FieldByName('SomeField').AsInteger;

    или 

    aValue := TMyTable.Fields[1].AsFloat;

    В действительности здесь вы получаете объект TField от объекта TTable (или TQuery), и затем вызываете соответствующий метод объекта TField для получения самих данных. Вы можете также изменить значение самого поля, но только в случае, если объект TTable находится в режиме вставки (Insert) или редактирования (Edit). Члены AsFloat, AsInteger, AsDateTime и AsString в действительности являются свойствами, и как таковые также могут принимать значения. С помощью Редактора Полей (Fields Editor, для вызова которого достаточно дважды щелкнуть на объекте TTable или TQuery) также возможно создание объектов-полей. Эти объекты могут быть использованы вместо получения их каждый раз от объекта TTable или TQuery.

    Получение информации о таблице

    Вам нужно воспользоваться свойством FieldDefs. В следующем примере список полей и их соответствующий размер передается компоненту TMemo (расположенному на форме) с именем Memo1:

    procedure TForm1.ShowFields;

    var

     i : Word;

    begin

     Memo1.Lines.Clear;Table1.FieldDefs.Update; { должно быть вызвано, если Table1 не активна }

     for i:= 0 to Table1.FieldDefs.Count - 1 do With Table1.FieldDefs.Items[i] do Memo1.Lines.Add(Name + ' - ' + IntToStr(Size));

     Memo1.Lines.Add(Name + ' – ' + IntToStr(Size));

    end;

    Если вам просто нужны имена полей (FieldNames), то используйте метод TTable GetFieldNames:

    GetIndexNames для получения имен индексов:

    var FldNames, IdxNames : TStringList

    begin

     FldNames := TStringList.Create;

     IdxNames := TStringList.Create;

     If Table1.State = dsInactive then Table1.Open;

     Table1.GetFieldNames(FldNames);

     Table1.GetIndexNames(IdxNames);

     {…… используем полученную информацию ……}

     FldNames.Free; {освобождаем stringlist}

     IdxNames.Free;

    end;

    Для получения информации об определенном поле вы должны использовать FieldDef. 

    Обмен данными между TMemoField и TMemo

    Delphi 1

    Procedure TMemoToTMemoField;

    begin

     TMemoField.Assign(TMemo.Lines);

    end;


    Procedure TMemoFieldToTMemo;

    VAR aBlobStream : TBlobStream;

    begin

     aBlobStream := TBlobStream.Create(TMemoField, bmRead);

     TMemo.Lines.LoadFromStream(aBlobStream);

     aBlobStream.Free;

    end;
     

    Если в транзакции изменена какая-то таблица, то для другого пользователя блокируется вся таблица, до окончания транзакции. Как лечить?

    Nomadic отвечает:

    По умолчанию, оператор UPDATE в MS SQL Server пытается поставить эксклюзивную табличную блокировку. Вы можете обойти это, используя ключевое слово FROM в сочетании с опцией PAGLOCK для использования MS SQL Server страничных блокировок вместо эксклюзивной табличной блокировки:

    UPDATE orders SET customer_id=NULL FROM orders(PAGLOCK) WHERE customer_id=32;

    Блокиpовка на всю таблицу пpи UPDATE ставится только в том случае, если по предикату нет индекса. Так, можно просто проиндексировать таблицу orders по полю customer_id, и не забывать делать UPDATE STATISTIC, хотя будет работать и с PAGLOCK. Просто не факт, что UPDATE всегда делает табличную блокировку. 

    Форма Мастер-Деталь

    Delphi 1

    …это нормально в двух случаях:

    1. Эксперт баз данных по умолчанию создает запрос, где RequestLive установлен в False; если вы хотите что-либо изменить, установите RequestLive в True.

    2. При отношениях «один к многим», из-за правил сохранения целостности, вам дозволяется делать изменения только на форме «многих», а не на форме «один».

    BTW: правильно, что вы об этом задумались. Предположим, что вы имеете отношение «один к многим», где «один» — ваши клиенты, а «многие» — их счета-фактуры: естественно, счетов, относящихся к клиенту, может быть больше, чем один. Если ваша система позволяет редактировать информацию о клиентах, например, удалять записи, то вскоре вы можете обнаружить, что некоторые счета не будут иметь отношения к кому бы то ни было. 

    Подскажите как правильно показать на экpане и сохранить в базе картинку формата JPEG?

    Nomadic отвечает:

    Я делал так (это кусок компонента):

    if Picture.Graphic is TJPegImage then

    begin

     bs:=TBlobStream.Create(TBlobField(Field),bmWrite);

     Picture.Graphic.SaveToStream(bs);

     bs.Free;

    end

    else if Picture.Graphic is TBitmap then

    begin

     Jpg:=TJPegImage.Create;

     Jpg.CompressionQuality:=…;

     Jpg.PixelFormat:=;

     Jpg.Assign(Picture.Graphic);

     Jpg.JPEGNeeded;

     bs:=TBlobStream.Create(TBlobField(Field),bmWrite);

     Jpg.SaveToStream(bs);

     bs.Free;

     Jpg.Free;

    end else Field.Clear;
     

    Как исключить показ поля P_RECNO?

    Delphi 1 

    Вы можете сделать:

    1. отредактируйте TTable для исключения P_RECNO

    или

    2. установите

    TableX.FieldbyName('P_RECNO').Visible := False;

    Это можно сделать также и с помощью редактора полей (Fields Editor), который связан не с DBGrid, а напрямую с компонентом Table. Для вызова редактора щелкните правой кнопкой мыши на компоненте Table и выберите самый верхний пункт контекстного меню. Добавьте все поля в список полей и выделите то поле, которое вы не хотите показывать в DBgrid. Найдите в Инспекторе Объектов свойство Visible, и установите его в False.

    //

    Если у вас имеется компонент TTable, дважды щелкните на иконке компонента (расположенной на форме), и вы получите в диалоге список полей, имеющих отношение к соответствующей таблице щелкните на одном из полей и проверьте в Инспекторе Объектов свойство Visible, оно должно быть установлено в False.

    //

    Информация из одной таблицы и набора данных на двух формах

    1. Добавьте на вторую форму (form2) компонент TTable

    2. В режиме разработки присвойте этой таблице такие же значения, как и у таблицы, расположенной на form1

    3. В секции IMPLEMENTATION у form2 создайте следующий фрагмент кода:


    unit form2;

    interface

    {…}

    implementation

    uses form1;

    {…}

    4. Подключите процедуру к OnCreate-событию в form2 (через Инспектор Объектов)

    5. Добавьте к этой процедуре следующую строку:

    table1 := form1.table1;

    В режиме разработки свяжите все компоненты с table1, расположенным на form1.

    Остается только решить проблему синхронизации. Попробуйте следующее:

    - На Form1

     разместите Table1

     разместите DataSource1

      установите DataSource1.DataSet := Table1

     разместите DataGrid

      установите DataSource := DataSource1

    Ну это все просто и стандартно. Поехали дальше:

    - На Form2

     разместите DataSource1 (#1 для этой формы)

     разместите любые другие необходимые вам БД-компоненты;

      укажите у них в качестве источника данных DataSource1

     В обработчике события OnCreate для этой формы (например, FormCreate), поместите следующий код:

    With Form1 do

    begin

     Form2.DataSource1.DataSet := Table1;

    end;

     Данный код подключает Table1 на Form1 к DataSource от Form2.

     После таких действий данные будут отображены на Form2 и будут «синхронизированы» с данными, отображаемыми на Form1 (поскольку в действительности используется одна таблица).

    Единственное здесь предостережение – если вы используете TDatabase, так как это может быть не то, что вы хотите. Компонент TDatabase не обязателен для получения доступа к базам данных, но, тем не менее, он обеспечивает вас дополнительным контролем в приложениях класса клиент/сервер.

    Таким образом, если приложение не работает в среде клиент/сервер, нет необходимости использовать TDatabase. Все, что вам нужно – TDataSource, TTable и компоненты для работы с базами данных. 

    Как при вводе информации в БД автоматически вставлять SEQUENCE?

    Nomadic отвечает:

    Если добавление через оператор INSERT ( в TQuery), то прямо там пишешь, как в плюсе («… Values (My_seq.nextval, …»).

    Если добавление идет через TQuery c RequestLive=true, то в BeforeInsert сделай запрос через TQuery (select myseq.nextval from dual) и заноси значение в свое поле. 

    Помещение переменной в Memo-поле

    Delphi 1 

    Если я правильно понял ваш вопрос, вам нужно сделать приблизительно так (для ПОЛУЧЕНИЯ данных): 

    Memos := TStringList.Create;

    Memos.Assign(Table1Memo);

    yourvariable_0 := Memos[0];

    yourvariable_1 := Memos[1];

    ……………………

    yourvariable_n := Memos[n];

    Memos.Free;

    или так (для УСТАНОВКИ данных): 

    Memos := TStringList.Create;

    Memos.Add(yourvariable_0);

    Memos.Add(yourvariable_1);

    ……………………

    Memos.Add(yourvariable_n);

    Table1Memo.Assign(Memos);

    Memos.Free;
     

    Индикатор прогресса выполнения запроса

    Delphi 1 

    Невозможно.

    Идея заключалась в том, чтобы с помощью объекта TQuery выполнять запросы, SQL сервер их в фоне обрабатывал бы, а мы смотрели бы на это дело на локальной машине с помощью линейки прогресса. Но из приложения никоим образом нельзя узнать, что делает TQuery, так что линейка прогресса, по идее, должна была бы получать текущую позицию непосредственно с SQL сервера. Но, поскольку большинство SQL серверов не публикуют такой информации, эту идею можно торжественно схоронить…

    Если вы используете Paradox или DBase, то, я думаю, для этой цели вы можете воспользоваться функцией DBIRegisterCallback:

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

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

    (Из руководства пользователя DBE)

    Лично я никогда этим не пользовался, поэтому на смогу поделиться деталями. 

    Обновление данных БД из модальной формы

    Delphi 1 

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

    unit myModalF;

    interface

    {…}

    implementation

    {…}

    uses

     MainForm; {Имя файла родительской формы для вашей модальной формы}


    MyModalForm.OnCreate(Sender: TObject);

    begin

     DBGrid1.DataSource := MyMainForm.DataSource1;

    end;
     

    Как записать в BLOB-поле большой текст (>255 байт) из Delphi?

    Nomadic отвечает:

    Можно так –

    var

     S: TBlobStream;

     B: pointer;

     c: integer;

    Table1.Edit;

    S := TBlobStream.Create(Table1BlobField as TBlobField, bmWrite); {кажется, так}

    C := S.Write(B, C);

    Table1.Post;

    S.Destroy;

    или так –

    var

     S: TMemoryStream;

     B: pointer;

     C: integer;

     …

      S := TMemoryStream.Create;

    Table1.Edit;

    S.Clear;

    S.SetSize(C);

    C := S.Write(B,C);

    (Table1BlobField as TBlobField).LoadFromStream(S);

    S.Clear;

    Table1.Post;

    S.Destroy;
     

    Блокировка таблицы

    …когда вы получаете эту, или аналогичную ошибку, вы можете прервать процесс следующим образом (в предположении, что вы пытаетесь запостить запись):

    try

     Table1.Post;

    except

     MessageDlg ('Ошибка постинга записи', прочее…

     Table1.Cancel;

    end;

    В противном случае вы не получите ошибку в случае, если текущую запись «рассматривает» другой пользователь (если вы пользуетесь базой данных Paradox, поставляемой с Delphi), если, конечно, вы правильно это установили. Paradox сам создает в сетевом каталоге файл с именем pdxusers.lck, видимый всеми пользователями, так что каждый BDE на каждой локальной машине может запирать запись, таким образом запрещая другим пользователям постить запись до снятия блокировки. Я не знаю, каким образом вы получаете эту ошибку, поэтому существует вероятность того, что я ошибаюсь в своих предположениях. 

    Каким драйвером пользуется TDATABASE?

    Delphi 1 

    Вы можете использовать вызов IDAPI dbiGetDatabaseDesc. Вот быстрая справка (не забудьте добавить DB в список используемых модулей): 

    var

     pDatabase: DBDrsc:

    begin

     { pAlias – PChar, содержащий имя псевдонима}

     dbiGetDatabaseDesc(pAlias, @pDatabase);

    Для получения дополнительной информации обратитесь к описанию свойства pDatabase.szDbType. 

    Как создать новый запрос и скопировать туда точно такие же описания полей?

    Nomadic отвечает:

    Копируешь FieldDefs.

    Проходишь циклом по FieldDefs.Items[i].CreateField(Owner); 

    Запись потока в BLOB-поле

    Delphi 1 

    Вся хитрость заключается в использовании StrPcopy (помещения вашей строки в PChar) и записи буфера в поток. Вы не сможете передать это в PChar непосредственно, поскольку ему нужен буфер, поэтому для получения необходимого размера буфера используйте <BufferName>[0] и StrLen().

    Вот пример использования TMemoryStream и записи его в Blob-поле: 

    var

     cString: String;

     oMemory: TMemoryStream;

     Buffer: PChar;

    begin

     cString := 'Ну, допустим, хочу эту строку!';

     { СОздаем новый поток памяти }

     oMemory := TMemoryStream.Create;

     {!! Копируем строку в PChar }

     StrPCopy(Buffer, cString);

     { Пишем =буфер= и его размер в поток }

     oMemory.Write(Buffer[0], StrLen(Buffer));

     {Записываем это в поле}

     <Blob/Memo/GraphicFieldName>.LoadFromStream(oMemory);

     { Необходимо освободить ресурсы}

     oMemory.Free;

    end;
     

    Как я могу выбрать на клиента только часть данных с определенной позиции из набора данных на сервере?

    Nomadic отвечает:

    Наиболее приемлемым является использование TQuery и Provider.SetParams.

    Но также Вы можете сделать это иначе:

    Сперва на клиенте Вам нужно считать с сервера только метаданные для набора данных. Это можно сделать, установив PacketRecords в 0, и затем вызвав Open. Затем Вы должны вызвать метод сервера (Вы должны определить этот метод на сервере), который спозиционирует курсор на первую нужную запись. И, наконец, установите PacketRecords в нужное значение, большее нуля, и вызовите GetNextPacket. 

    Отследить изменение данных?

    Предположим, что пользователь изменил строковое поле в Null. Как тогда я в обработчике OnUpdateData смогу определить, изменилось ли это поле на строку Null, или поле просто не было изменено?

    Nomadic отвечает:

    Используйте свойство NewValue класса TField при чтении второй записи (той, которая содержит изменения). Если возвращаемое значение (variant) пусто или не назначено, тогда поле не было модифицировано. Здесь немного иллюстрирующего кода: 

    var NewVal: Variant;

    begin

     NewVal := DataSet.FieldByName('MyStrField').NewValue;

    if VarIsEmpty(NewVal) then ShowMessage('Field was not edited')

    else if VarIsNull(NewVal) then ShowMessage('Field was blanked out')

    else ShowMessage('New Field Value: ' + String(NewVal));

    end;

    Если Вы взглянете на исходники формы RecError (в репозитории), то Вы увидите, как она использует эту информацию для вывода строки ' ' при показе ошибок синхронизации данных. На сервере Вы добавляете ограничения уровня записи, используя свойство Constraints Вашего TQuery/TTable или ограничения уровня поля, используя постоянные обьекты TField (с помощью FieldsEditor либо на CustomConstraint, либо ImportedConstraint). Если Вы используете ограничения уровня поля, они вступают в силу, когда данныеотправляются в поле (например, когда Вы уходите из органа управления, связанного с этим полем (типа TDBEdit)). 

    Как достучаться до методов сервера приложений из TClientDataSet?

    Nomadic отвечает:

    Вот так: 

    RemoteServer.AppServer.MyMethod

    AppServer – свойство только для чтения, возвращающее интерфейс удаленного сервера, возвращаемый провайдером сервера приложений. Клиентские приложения могут общаться напрямую с сервером приложений через этот интерфейс. 

    Я включил dbclient.dll в секцию `additional files` опций распространения по web, но этот файл никогда не загружается на клиента. Как это исправить?

    Nomadic отвечает:

    Ваш INF-файл должен включать в себя строки наподобие:

    [Add.Code]

    dbclient.dll=dbclient.dll

    [dbclient.dll]

    file=http://yoursite.com/dbclient.cab

    clsid={9E8D2F81-591C-11D0-BF52-0020AF32BD64}

    RegisterServer=yes

    FileVersion=4,0,0,36

    Замените «yoursite» Вашим HTTP-адресом, где находится cab-файл. FileVersion – это версия файла в Вашем cab-файле (проверьте информацию о версии DBCLIENT, чтобы быть уверенным в соответствии). Убедитесь, что FileVersion относится к версии Вашего DBCLIENT.DLL. Вы можете положить dbclient.dll в cab-файл, используя утилиту CABARC, которую Вы найдете в папке delphi\bin. Примерная команда вызова CABARC может выглядеть примерно так:

    CABARC N DBCLIENT.CAB DBCLIENT.DLL
     

    Как можно использовать TClientDataSet в локальном приложении с таблицами Paradox, без использования компонент TProvider и TRemoteServer?

    Nomadic отвечает:

    Вы не сможете отделаться от Провайдера (хотя бросать его на форму/модуль данных не придется), но Вы сможете использовать TClientDataSet в одно-точечном (single-tier) приложении. Для того, чтобы открыть client dataset, Вы должны назначить Провайдера Данных вручную.

    { CDS = TClientDataSet }

    { Table1 = TTable }

    CDS.Provider := Table1.Provider;

    CDS.Open;

    Также Вы должны включить модуль BDEProv в предложение uses. 

    Hе получается открыть таблицу, созданную в InterBase с DEFAULT CHARACTER SET WIN1251. Оно говорит, что `WIN1251 undefined`

    Nomadic отвечает:

    A: (AA): Ставьте Interbase в каталог с путем, соответствующим DOS-овским соглашениям об именах (8+3).

    Создание 

    Функции редактора полей во время выполнения программы

    Возможен ли вызов функций редактора полей (Fields Editor) во время выполнения программы?

    Да. Если вы определили поля во время разработки приложения, то во время выполнения можно менять их свойства (например, Size).

    Например, следующий код изменяет каждый размер поля TField.Size так, чтобы соответствовать фактическому размеру поля открываемого набора данных:

    procedure SetupFieldsAndOpenDataset(DataSet: TDataSet);

    var FieldNum, DefNum: Integer;

    begin

     with DataSet do

     begin

      if Active then Close;

      FieldDefs.Update;

      {набор данных должен быть закрыт}

      {ищем каждое предопределенное TField в DataSet.FieldDefs:}

      for FieldNum := FieldCount - 1 downto 0 do with Fields[FieldNum] do

      begin

    DefNum := FieldDefs.IndexOf(FieldName);

       if DefNum < 0 then raise Exception.CreateFmt('Поле "%s" не найдено в наборе данных "%s"',[FieldName, Dataset.Name]);

       {устанавливаем свойство size:}

       Size := FieldDefs[DefNum].Size;

      end;

      Open;

     end;

    end;

    – Lindsay Reichmann

    Производная TIntegerField


    Я думал о производной, новом варианте компонента TIntegerfield, но я не могу понять как мне его получить во время разработки, ведь он не устанавливается в палитру компонентов.

    Это то, что вы хотите. Создайте следующий молуль:

    MICRON.PAS:

    unit micron;

    interface

    uses DB, DBTables, Classes;


    type

     TMicronField = class(TIntegerField)

     public

      function IsValidChar(Ch: Char): Boolean; override;

     end;


    procedure Register;


    implementation



    function TMicronField.IsValidChar(Ch: Char): Boolean;

    begin

     Result := Ch in ['+', '-', '0'..'9','.'];

    end;


    procedure Register;

    begin

     RegisterFields([TMicronField]);

    end;


    end.

    Поместите данный модуль в ваш каталог lib и добавьте это поле, используя диалог установки компонент. Затем, используя «DataSet designer», свяжите TMicronField с нужными вам полями, после чего вы увидите, что список типов полей включает теперь «Micron». (для отображения полей на новый тип поля, сначала вам необходимо удалить все TIntegerFields).

    Другое решение, более простое (но так-же работающее), заключается в изменении исходного кода DBTables и простой замене существующей функции IsValidChar на TIntegerField.

    – Mark Edington

    Создание новой таблицы на основе структуры другой таблицы

    Delphi 1

    На ум сразу приходит операция присваивания значения свойству (стоящему с левой стороны от ':='), при которой Delphi в своих недрах вызывает метод 'write' и передает ему в виде единственного параметра все то, что находится в правой части выражения. Если свойство не имеет метода write, оно предназначено только для чтения. Вот определение свойства FieldDefs объекта TDataSet в файле DB.PAS:

    property FieldDefs: TFieldDefs read FFieldDefs write SetFieldDefs

    Как вы можете видеть, у него есть метод write. Следовательно, код:

    Destination.FieldDefs := Source.FieldDefs;

    в действительности делает такую операцию:

    Destination.SetFieldDefs(Source.FieldDefs);

    (за исключением того, что вы не можете использовать эту строку, поскольку SetFieldDefs определен в секции Private.)

    Вот определение свойства IndexDefs объекта TTable в файле DBTABLES.PAS file:

    property IndexDefs: TIndexDefs read FIndexDefs;

    В этом случае метод write отсутствует, поэтому свойство имеет атрибут только для чтения. Тем не менее, для самого объекта TIndexDefs существует метод Assign. Следовательно, следующий код должен работать:

    Source.IndexDefs.Update;

    Destination.IndexDefs.Assign(Source.IndexDefs);

    Перед вызовом Assign для Source.IndexDefs вызывайте метод Update, чтобы быть уверенным в том, что вы получите то, что хотите.

    Метод SetFieldDefs является процедурой с одной строкой кода, в которой вызывается метод FieldDefs Assign.

    Также можно проверить, определен ли реально индекс, и, если нет, то при вызове IndexDefs.Assign вы можете получить исключение типа «List Index Out Of Bounds» (или что-то типа этого). Например, так:

    if Source.IndexDefs.Count > 0 then…

    Вам нужно будет это сделать, поскольку метод TIndexDefs.Assign не проверяет это перед копированием индекс-информации. Также вам нет необходимости вызывать Clear до работы с IndexDefs, поскольку метод Assign сделает это и без вашего участия.

    Создание уникального ID для новой записи

    Delphi 1

    Существует несколько способов задавать в таблице уникальный ID.

    1. Вы можете использовать поле с автоприращением

    Этот метод не очень надежен. Если ваша таблица каким-то образом испортится, и вам понадобиться ее пересобрать, автоинкрементальные поля будут перенумерованы. Хотя это легкий способ для ситуации, когда вы не ссылаетесь на id таблицы в других таблицах, но это не очень мудрое решение в других случаях.


    2. Вы можете использовать ID-таблицу

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

    Table Name A (первичный ключ) Last Id NВ методе BeforePost таблицы, которой необходим уникальный ID, делайте примерно так:

    TableBeforePost(Sender: TObject)

    var Id: Integer;

    begin


     with TTable(Sender) do

     begin

    {проверяем, существует ли ID для этой записи}

      if Field[0].AsInteger=0 then

      begin

       {ищем имя таблицы в ID-Таблице}

       IDTable.FindKey[Name]

       {извлекаем последний Id – подразумеваем блокировку записи}

       Id := IDTable.FieldByName['Last Id'].AsInteger;

       Inc(Id);

       {записываем новый Id в ID-таблицу – подразумеваем разблокировку таблицы}

       IDTable.FieldByName['Last Id'].AsInteger := Id;

       IDTable.Post;

       {записываем извлеченный ID в вашу таблицу}

       Field[0].AsInteger := Id;

      end;

     end;

    end;


    end;

    Если вы поместите этот код в обработчик события таблицы BeforePost, вы убедитесь в том, что все ID будут последовательными (без «дырок»). Недостаток: если пользовать во время попытки добавления новой записи вдруг передумает, вы будете иметь запись с заполненным только полем ID.

    В случае, если вы решили воспользоваться данным способом (последовательные ID), поместите приведенный выше код в обработчик события таблицы OnNewRecord.

    3. Вы можете использовать ID-файл

    Используйте те же принципы, что и в предыдущем способе, но вместо ID-таблицы используется ID-Файл. Это дает преимущество за счет более высокой скорости работы, но в многопользовательской среде вы должны сами заботиться о блокировке записей.

    Динамическое создание таблицы и полей во время выполнения программы


    Delphi в режиме разработки позволяет быстро добавлять и настраивать в вашем проекте компоненты для работы с базами данных, но есть ситуации, когда вам нужно создавать и конфигурировать объекты во время выполнения программы. Например, во время выполнения программы вам может понадобиться добавить колонку с вычисляемым полем (используя алгоритмы пользователя). Поэтому вопрос: как, не используя среды разработки, Инспектора Объектов и редактора TFields, создавать и сконфигурировать TField и другие компоненты для связки данных?

    В следующем примере показано динамическое создание TTable, таблицы базы данных в связке с TTable, TFieldDefs, TFields, вычисляемых полей и подключение обработчика для события OnCalc.

    Для начала выберите пункт New Application меню File. Будет создан новый проект с пустой формой, на которой мы и будет создавать на лету наши компоненты.

    В секцию interface вашего модуля формы добавьте, как показано ниже, объявление обработчика события OnCalcFields и поля TaxAmount. Позже мы создадим TTable и назначим этот обработчик событию TTable OnCalcFields, который позволит при чтении каждой записи вызывать событие OnCalcFields, которое, в свою очередь, выполнит нашу процедуру TaxAmountCalc.

    type TForm1 = class(TForm)

     procedure TaxAmountCalc(DataSet: TDataset);

    private

     TaxAmount: TFloatField;

    end;

    В секции implementation создайте обработчик события OnCalc как показано ниже:

    procedure TForm1.TaxAmountCalc(DataSet: TDataset);

    begin

     Dataset['TaxAmount'] := Dataset['ItemsTotal'] * (Dataset['TaxRate'] / 100);

    end;

    Создайте обработчик формы OnCreate как показано ниже (для получения дополнительной информации о создании обработчиков событий обратитесь к Delphi Users Guide, Chapter 4 «Working With Code»).

    procedure TForm1.FormCreate(Sender: TObject);

    var

     MyTable: TTable;

     MyDataSource: TDataSource;

     MyGrid: TDBGrid;

    begin


     { Создаем компонент TTable -- связанная таблица базы данных будет создана ниже. }

     MyTable := TTable.Create(Self);

     with MyTable do

     begin

      { Определяем основную базу данных и таблицу. Примечание: Test.DB пока не существует. }

      DatabaseName := 'DBDemos';

      TableName := 'Test.DB';

      { Назначаем TaxAmountCalc обработчиком события, чтобы использовать его при наступлении события OnCalcFields в MyTable. }

      OnCalcFields := TaxAmountCalc;

      { Создаем и добавляем определения полей к массиву TTableFieldDefs, затем создаем TField с использованием информации из определения поля. }

      with FieldDefs do

      begin

    Add('ItemsTotal', ftCurrency, 0, false);

       FieldDefs[0].CreateField(MyTable);

       Add('TaxRate', ftFloat, 0, false);

       FieldDefs[1].CreateField(MyTable);

       TFloatField(Fields[1]).DisplayFormat := '##.0%';

       { Создаем вычисляемое TField, назначаем свойства, и добавляем поле к массиву определений MyTable. }

       TaxAmount := TFloatField.Create(MyTable);

       with TaxAmount do

       begin

    FieldName := 'TaxAmount';

        Calculated := True;

        Currency := True;

        DataSet := MyTable;

        Name := MyTable.Name + FieldName;

        MyTable.FieldDefs.Add(Name, ftFloat, 0, false);

       end;

      end;

      { Создаем в базе данных новую таблицу, используя в качестве основы MyTable. }

      MyTable.CreateTable;

     end;

     { Создаем компонент TDataSourceи назначаем его MyTable. }

     MyDataSource := TDataSource.Create(Self);

     MyDataSource.DataSet := MyTable;

     { Создаем табличную сетку, отображаемна форме, и назначаем MyDataSource дляполучения доступа к данным из MyTable. }

     MyGrid := TDBGrid.Create(Self);

     with MyGrid do

     begin

      Parent := Self;

      Align := alClient;

      DataSource := MyDataSource;

     end;

     { Запускаем нашу конструкцию! }

     MyTable.Active := True;

     Caption := 'Новая таблица ' + MyTable.TableName;

    end;

    Ниже приведен полный исходный код проекта:

    unit gridcalc;


    interface


    uses

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


    type

     TForm1 = class(TForm)

      procedure FormCreate(Sender: TObject);

      procedure TaxAmountCalc(DataSet: TDataset);

     private

    TaxAmount: TFloatField;

     end;


    var

     Form1: TForm1;


    implementation


    {$R *.DFM}


    procedure TForm1.TaxAmountCalc(DataSet: TDataset);

    begin

     Dataset['TaxAmount'] := Dataset['ItemsTotal'] *(Dataset['TaxRate'] / 100);

    end;


    procedure TForm1.FormCreate(Sender: TObject);

    var

     MyTable: TTable;

     MyDataSource: TDataSource;

     MyGrid: TDBGrid;

    begin

     MyTable := TTable.Create(Self);

     with MyTable do

     begin

      DatabaseName := 'DBDemos';

      TableName := 'Test.DB';

      OnCalcFields := TaxAmountCalc;

      with FieldDefs do

      begin

       Add('ItemsTotal', ftCurrency, 0, false);

       FieldDefs[0].CreateField(MyTable);

       Add('TaxRate', ftFloat, 0, false);

       FieldDefs[1].CreateField(MyTable);

       TFloatField(Fields[1]).DisplayFormat := '##.0%';

       TaxAmount := TFloatField.Create(MyTable);

       with TaxAmount do

       begin

        FieldName := 'TaxAmount';

        Calculated := True;

        Currency := True;

        DataSet := MyTable;

        Name := MyTable.Name + FieldName;

        MyTable.FieldDefs.Add(Name, ftFloat, 0, false);

       end;

      end;

      MyTable.CreateTable;

     end;

     MyDataSource := TDataSource.Create(Self);

     MyDataSource.DataSet := MyTable;

     MyGrid := TDBGrid.Create(Self);

     with MyGrid do

     begin

    Parent := Self;

      Align := alClient;

      DataSource := MyDataSource;

     end;

     MyTable.Active := True;

     Caption := 'Новая таблица ' + MyTable.TableName;

    end;


    end.
     

    Проблема с AddIndex

    Delphi 1 

    Я использую таблицу paradox на своей локальной машине.

    Я использую следующие команды: 

    Table.DatabaseName := 'ABC';

    Table.TableName := 'TEST';

    Table.CreateTable;

    Table.AddIndex('Primary','ID',[ixPrimary]); (работает как часы)

    Table.AddIndex('Number_IDX','NUMBER',[ixUnique]); (здесь я получаю ошибку времени выполнения)

    ID – LongInt поле

    NUMBER – поле типа char[15] 

    Как создать БД в кодировке CP1251?

    Nomadic отвечает:

    Вот такая конструкция проходит на DB2 2.1.2/NT и UDB5/NT…

    CREATE DATABASE Efes2

    USING CODESET 1251 TERRITORY RU

    COLLATE USING IDENTITY;
     

    Таблицы в памяти

    Delphi 1 

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

    ВНИМАНИЕ! ДАННЫЙ КОД НЕ ПРЕДУСМАТРИВАЕТ НИКАКИХ ГАРАНТИЙ!

    ИСПОЛЬЗУЙТЕ ЕГО НА СВОЙ СТРАХ И РИСК - ВЫ ЕДИНСТВЕННЫЙ ЧЕЛОВЕК, ОТВЕТСТВЕННЫЙ ЗА ЛЮБОЙ УЩЕРБ, КОТОРЫЙ МОЖЕТ ПОВЛЕЧЬ ЗА СОБОЙ ИСПОЛЬЗОВАНИЕ ДАННОГО КОДА — Я ВАС ПРЕДУПРЕДИЛ!

    Благодарю Steve Garland <72700.2407@compuserve.com> за предоставленную помощь. Он создал свой собственный "in-memory" табличный компонент, который послужил мне толчком для написания сего кода.

    InMemory-таблицы являются характеристикой Borland Database Engine (BDE). InMemory-таблицы создаются в RAM и удаляются при их закрытии. Работают они значительно быстрее и очень полезны в случае, если вам нужны быстрые операции в небольших таблицах. Данный пример использует вызов функции BDE DbiCreateInMemoryTable. Данный объект должен работать наподобии простой регулярной таблицы, за исключением того, что InMemory-таблицы не поддерживают некоторые характеристики (типа проверка целостности, вторичные индексы и BLOB-поля), и в настоящее время данный код не содержит механизма обработки ошибок. Вероятно, вы получите ошибку при попытке создания memo-поля. Если у вас есть любые замечания, шлите их по адресу grisha@mira.com.

    unit Inmem;


    interface


    uses DBTables, WinTypes, WinProcs, DBITypes, DBIProcs, DB, SysUtils;


    type TInMemoryTable = class(TTable)

    private

    hCursor: hDBICur;

     procedure EncodeFieldDesc(var FieldDesc: FLDDesc; const Name: string; DataType: TFieldType; Size: Word);

     function CreateHandle: HDBICur; override;

    public

     procedure CreateTable;

    end;


    implementation


    { Эта функция виртуальная, так что я смог перекрыть ее. В оригинальном VCL-коде для TTable эта функция реально открывает таблицу, но, поскольку мы уже имеем дескриптор таблицы, то мы просто возвращаем его }


    function TInMemoryTable.CreateHandle;

    begin

     Result := hCursor;

    end;


    { Эта функция получена ее простым копированием из исходного кода VCL. Я должен был это сделать, поскольку это было объявлено в секции private компонента TTable, поэтому отсюда у меня не было к этому досупа. }

    procedure TInMemoryTable.EncodeFieldDesc(var FieldDesc: FLDDesc; const Name: string; DataType: TFieldType; Size: Word);

    const

     TypeMap: array[TFieldType] of Byte = (fldUNKNOWN, fldZSTRING, fldINT16, fldINT32, fldUINT16, fldBOOL,fldFLOAT, fldFLOAT, fldBCD, fldDATE, fldTIME, fldTIMESTAMP, fldBYTES,fldVARBYTES, fldBLOB, fldBLOB, fldBLOB);

    begin

     with FieldDesc do

     begin

    AnsiToNative(Locale, Name, szName, SizeOf(szName) - 1);

      iFldType := TypeMap[DataType];

      case DataType of

    ftString, ftBytes, ftVarBytes, ftBlob, ftMemo, ftGraphic:

       iUnits1 := Size;

      ftBCD:

       begin

    iUnits1 := 32;

        iUnits2 := Size;

       end;

      end;

      case DataType of

    ftCurrency: iSubType := fldstMONEY;

      ftBlob: iSubType := fldstBINARY;

      ftMemo: iSubType := fldstMEMO;

      ftGraphic: iSubType := fldstGRAPHIC;

      end;

     end;

    end;


    { Вот кухня, где все это происходит. Я скопировал эту функцию из исходников VCL и затем изменил ее для использования DbiCreateInMemoryTable вместо DbiCreateTable. Поскольку InMemory-таблицы не поддерживают индексы, я удалил весь соответствующий код. }

    procedure TInMemoryTable.CreateTable;

    var

     I: Integer;

     pFieldDesc: pFLDDesc;

     szTblName: DBITBLNAME;

     iFields: Word;

     Dogs: pfldDesc;

    begin

     CheckInactive;

     if FieldDefs.Count = 0 then for I := 0 to FieldCount - 1 do with Fields[I] do if not Calculated then FieldDefs.Add(FieldName, DataType, Size, Required);

     pFieldDesc := nil;

     SetDBFlag(dbfTable, True);

     try

      AnsiToNative(Locale, TableName, szTblName, SizeOf(szTblName) - 1);

      iFields := FieldDefs.Count;

      pFieldDesc := AllocMem(iFields * SizeOf(FLDDesc));

      for I := 0 to FieldDefs.Count - 1 do with FieldDefs[I] do

      begin

       EncodeFieldDesc(PFieldDescList(pFieldDesc)^[I], Name,DataType, Size);

      end;

      { тип драйвера nil, т.к. поля логические }

      Check(DbiTranslateRecordStructure(nil, iFields, pFieldDesc, nil, nil, pFieldDesc));

      { здесь hCursor получает свое значение }

      Check(DbiCreateInMemTable(DBHandle, szTblName, iFields, pFieldDesc, hCursor));

     finally

      if pFieldDesc <> nil then FreeMem(pFieldDesc, iFields * SizeOf(FLDDesc));

      SetDBFlag(dbfTable, False);

     end;

    end;


    end.


    {Данный код взят из файлов помощи Ллойда!}

    Поиск 

    FindKey для нескольких полей

    Delphi 1 

    with Table1 do

    begin

     SetKey;

     FieldByName('State').AsString := 'CA';

     FieldByName('City').AsString := 'Scotts Valley';

     GotoKey;

    end;

    Вы не можете использовать Findkey с файлами DBase более чем для одного поля. 

    oEmetb.indexName:='PrimaryKey';

    if oEmeTb.findkey([prCLient,prDiv,prEme]) then

    где findkey передаются параметры для Primary Keyfields.

    Я обращаю ваше внимание на то, что имя индекса (Index) чувствительно к регистру, так что будьте внимательны.

    Вы можете также воспользоваться oEmeTb.indexfieldnames, но убедитесь в том, что ваш список ключевых полей в точности соответствуют ключевым полям, которые вы ищете. 

    oEmetb.indexfieldNames:='EmeClient;EmeDiv;EmeNo';

    if oEmeTb.findkey([123,'a',96]) then
     

    Поиск существующей записи перед тем, как она будет вставлена

    Если вы находитесь в режиме редактирования (Edit) или вставки (Insert), то при изменении режима вы автоматически делаете постинг записи. И, естественно, при наличие дубликата (неуникальности) записи, вы получите ошибку. Способ обойти это – использовать другой компонент TTable, связанный с той же таблицей, и осуществляющий по ней поиск. Этот путь самый простой и эффективный.

    Воспользуйтесь двумя компонентами TTable (оба должны указывать на одну и ту же таблицу). Используйте один для поиска, а второй для редактирования.

    Ваша «ключевая» таблица BDE будет автоматически генерировать исключения, если пользователь будет пытаться послать созданный им дублирующий ключ. Для установки таблицы используйте Database Desktop.

    Создайте на основе поля первичный индекс (Primary Index). Затем создайте какой-то обработчик DB-исключения для нашего «нарушения уникальности».

    Моя технология заключается в следующем: в отдельной форме я предлагаю пользователям ввести часть записи, которая должна быть уникальна (обычно одно поле). Затем для проверки существования я делал FindKey. Если он находился, через MessageDlg я информировал пользователя, и возвращал его на форму редактирования, не создавая новой записи. Помните, что если FindKey ничего не находит, dbCursor никуда не перемещается, и закладка не нужна. Если запись найдена, она немедленно будет отображена на форме редактирования для того, чтобы пользователь смог увидеть ее содержимое. В противном случае происходит следующее: 

    Table.Append;

    Table.FieldByName('KeyField').AsString := UserEntry;

    { … позволяем пользователю редактировать все остальные поля записи … }

    { в это время кнопка Cancel должна быть активной для того, чтобы дать возможность пользователю отменить ввод новой записи. }

    В моей форме редактирования поле с уникальном ключем выключается (disabled) и показывается с другим цветом. Целостность соблюдена :-). 

    Поиск фраз и записей переменной длины

    Delphi 1 

    Для текста переменной длины вы можете использовать DBmemo. Большинство людей это делают сканированием «на лету» (когда оператор постит запрос), но для реального ускорения процесса можно попробовать способ пре-сканирования, который делают «большие мальчики» (операторы больших баз данных):

    1. при внесении в базу данных новой записи она сканируется на предмет определения ключевых слов (это может быть как предопределенный список ключевых слов, так и всех слов, не встречающиеся в стоп-листе [пример: «the», «of», «and"])

    2. ключевые слова вносятся в список ключевых слов со ссылкой на номер записи, например, «hang»,46 или «PC»,22.

    3. когда пользователь делает запрос, мы извлекаем все записи, где встречается каждое из ключевых слов, например, «hang» может возвратить номера записей 11, 46 и 22, тогда как «PC» — записи с номерами 91, 22 и 15.

    4. затем мы объединяем числа из всех списков c помощью какого-либо логического оператора, например, результатом приведенного выше примера может быть запись под номером 22 (в случае логического оператора AND), или записи 11, 15, 22, 46 и 91 (в случае оператора OR). Затем извлекайте и выводите эти записи.

    5. для синонимов определите таблицу синонимов (например, «hang»,"kaput»), и также производите поиск синонимов, добавляя их к тому же списку как и оригинальное слово.

    6. слова, имеющие общие окончания (например, «hang» и «hanged»), можно также сделать синонимами, или, как это делает большинство систем, производить анализ окончаний слов, вычисляя корень по их перекрытию (например, слову «hang» соответствует любое слово, чьи первые 4 буквы равны «hang»).

    Конечно, есть множестно технических деталей, которые необходимо учесть, например, организация списков, их эффективное управление и объединение. Оптимизация этой характеристики может вам дать очень быстрое время поиска (примером удачный реализаций могут служить двигатели поиска Nexus, Lycos или WebCrawler, обрабатывающие сотни тысяч записей в течение секунды).

    dBase 

    Текущий номер записи набора данных

    Delphi 1

    {Извлекает физический номер записи xBase. Требует наличие модулей DBITYPES, DBIPROCS, и DBIERRS в списке используемых модулей. Функция требует на входе один аргументтипа TTable (например, Table1).}

    function Form1.Recno(oTable: TTable): Longint;

    var

     rError: DBIResult;

     rRecProp: RECprops;

     szErrMsg: DBIMSG;

    begin

     Result := 0;

     try

      oTable.UpdateCursorPos;

      rError := DbiGetRecord(oTable.Handle, dbiNOLOCK, nil, @rRecProp);

      if rError = DBIERR_NONE then Result := rRecProp.iPhyRecNum

      else case rError of

      DBIERR_BOF: Result := 1;

      DBIERR_EOF: Result := oTable.RecordCount + 1;

      else

       begin

        DbiGetErrorString(rError, szErrMsg);

        ShowMessage(StrPas(szErrMsg));

       end;

      end;

     excepton

      E: EDBEngineError do ShowMessage(E.Message);

     end;

    end;

    Как открыть индексированную таблицу dBase, если отсутствует файл индекса?

    Nomadic советует:

    Для dBase-таблицы встроенными средствами ты не перестроишь индекс, если его нет. Для этой цели мне пришлось написать процедуру для физического удаления признака индексации в самом dbf-файле и после её применения добавлять индексы заново.

    Для этого в заголовок файла dbf по смещению 28(dec) записываешь 0.

    По другому никак не выходит(я долго бился) — вот для Paradox таблиц все Ok.

    С помощью BDE Callbacks. Пример для Delphi 2.0, на первом не проверял:

    === Callback.pas ===

    unit Callback;


    interface


    uses BDE, Classes, Forms, DB, DBTables;


    type

     TForm1 = class(TForm)

      Table1: TTable;

      procedure FormCreate(Sender: TObject);

      procedure FormDestroy(Sender: TObject);

     private

      CBack: TBDECallback; // опpеделение BDE CallBack

      CBBuf: CBInputDesc; // пpосто буфеp

      function CBFunc(CBInfo: Pointer): CBRType; // Callback-функция

     public

     end;


    var

     Form1: TForm1;


    implementation


    {$R *.DFM}


    procedure TForm1.FormCreate(Sender: TObject);

    begin

     Session.Open; // В это вpемя сессия ещё не откpыта

     CBack := TBDECallback.Create(Session {Hапpимеp}, nil, cbINPUTREQ, @CBRegBuf, SizeOf(CBBuf), CBFunc, False); // Опpеделили Callback

     Table1.Open;

     //^^^^^^^^^^^ - здесь возможна ошибка с индексом, etc.

    end;


    procedure TForm1.FormDestroy(Sender: TObject);

    begin

     CBack.Free; // Освобождаем CallBack

    end;


    function TForm1.CBFunc(CBInfo: Pointer): CBRType;

    begin

     with PCBInputDesc(CBInfo)^ do case eCbInputId of

     cbiMDXMissing {, cbiDBTMissing - можно ещё и очищать BLOB-поля}:

      begin

       iSelection := 3; // Hомеp ваpианта ответа (1-й - откpыть только

                        // для чтения, 2-й - не откpывать, 3-й - отсоединить индекс).

                        // Возможный источник непpиятностей: а вдpуг в последующих веpсиях

                        // BDE номеpа будут дpугими?

       Result := cbrCHKINPUT; // Обpабатывать введённый ответ

      end;

     end;

    end;


    end.

    === Callback.pas ===

    PS: конечно, это лишь пример, делающий минимум необходимого. В рамках данного письма невозможно дать какое-то описание BDE Callbacks. Информацию я взял из BDE32.HLP, BDE.INT и DB.PAS. В VCL.HLP совсем ничего нет по этому поводу.

    Вообще, руки бы оторвал тем, кто писал справку по Дельфям: я неделю мучался с сабжем, пока случайно не набрёл на Callbacks.

    Определение удаления записей в .DBF

    Delphi 1

    Взято из "Dtopics Database 1.10 from 3K computer Consultancy":

    Dbase в BDE имеет большее количество ситуаций 'особого случая', чем таблицы SQL и Paradox, поскольку данный формат поддерживает выражения в индексах и прочие характеристики, например:

    1. Создание и пересоздание индекса

    – DbiRegenIndexes( Table1.Handle ); { Регенерация всех индексов }

     – создание индекса (зависит от того, существует ли выражение или нет)

    if ((Pos('(',cTagExp) + Pos('+',cTagExp)) > 0) then Table1.AddIndex(cTagName, cTagExp, [ixExpression])  ( <– ixexpression – _литерал_)

    else Table1.AddIndex(cTagName, cTagExp, []);

    2. Связки Master/Detail в выражениях дочерних индексов

    – вызов процедуры BDE DbiLinkDetailToExp() вместо обычной DbiLinkDetail()

    3. Пакование таблиц

    with Table1 do StrPCopy(TName, TableName);

    Result := DBIPackTable(DbHandle, Handle, TName, szDBASE, TRUE);

    4. Задание видимости удаленных записей – вкл/выкл (например, dBase SET DELETED ON/OFF)

    DbiSetProp( hDBIObj(Table1.Handle), curSOFTDELETEON, LongInt(bValue));

    5. Задание частичного/полного соответствия символов – вкл/выкл (например, dBase SET EXACT ON/OFF)

    DbiSetProp( hDBIObj(Table1.Handle), curINEXACTON,   LongInt(bValue));

    <– Конец –>

    Ну и теперь сами вопросы:

    <– Начало –>

    «Как мне увидеть записи dBASE, помеченные для удаления?»

    В обработчике события AfterOpen вызовите приведенную ниже функцию. Включите DBITYPES, DBIERRS, DBIPROCS в список используемых модулей. Для вызова функции передайте ей в качестве аргумента имя TTable и TRUE/FALSE в зависимости от необходимости показа/скрытия удаленных записей. Пример:

    procedure TForm1.Table1AfterOpen(DataSet: TDataset);

    begin

    SetDelete(Table1, TRUE);

    end;


    procedure SetDelete(oTable:TTable; Value: Boolean);

    var

     rslt: DBIResult;

     szErrMsg: DBIMSG;

    begin

     try

      Table.DisableControls;

      try

       rslt := DbiSetProp(hDBIObj(oTable.Handle), curSOFTDELETEON,LongInt(Value));

       if rslt <> DBIERR_NONE then

       begin

        DbiGetErrorString(rslt, szErrMsg);

        raise Exception.Create(StrPas(szErrMsg));

       end;

       excepton E: EDBEngineError do ShowMessage(E.Message);

       on E: Exception do ShowMessage(E.Message);

      end;

     finally

      Table.Refresh;

      Table.EnableControls;

     end;

    end;

    «Могу ли я создать в табличной сетке колонку, в которой будут показываться записи, помеченные для удаления из таблицы dBASE?»

    Создайте вычисляемое поле, затем в обработчике события таблицы OnCalcField замените его таким образом:

    procedure TForm1.Table1CalcFields(DataSet: TDataset);

    var

     RCProps : RecProps;

     Result : DBIResult;

    begin

     Result := DbiGetRecord(Table1.Handle, dbiNo

    Рукописи не горят…

    Определение номера записи в таблице dBASE

    Таблицы dBASE применяют довольно статическую систему нумерации записей. Номер записи для данной записи (извините за тавтологию) отражает физическую позицию в табличном файле. Эти номера записей не изменяются вследствие фильтрации, упорядочивания данных или сортировки. К примеру, первая запись, хранящаяся в .DBF файле, будет иметь номер записи 1. Возможно, после некоторого упорядочивания индекса, запись будет последней из 100 записей. В этом случае запись должна оставаться с тем же номером, а не номером 100, отражающим новую позицию в сортированном наборе данных. Это противоречит таблицам Paradox, где соблюдается последовательная нумерация. Последовательная нумерация Paradox похожа на нумерацию записей dBASE, за исключением большей гибкости и отражению в номере записи ее текущей позиции в наборе данных. То есть, запись может не всегда иметь номер, установленный для нее фильтром набора данных, уменьшившим общее число записей, или при активном индексе, из-за чего может измениться отображаемый порядок записи.

    В приложениях для работы с базами данных, созданных с помощью Delphi и Borland Database Engine (BDE), DB-компонентами не предусмотрено извлечение и определение записи таблицы dBASE. Такая операция, тем не менее, возможна с помощью вызова из вашего приложения функций BDE.

    Существует несколько функций BDE, возвращающих информацию о текущей записи dBASE, например, ее номер. На самом деле, любая функция, заполняющая структуру BDE pRECProps, вполне достаточна. Например, функции BDE DbiGetRecord, DbiGetNextRecord и DbiGetPriorRecord. Естественно, только первая из них реально позволяет получить информацию о текущей записи. Две других перемещают при вводе указатель на запись, подобно методам Next и Prior компонентов TTable и TQuery.

    Структура pRECProps состоит из следующих полей:

    iSeqNum: тип LongInt; определяет текущий номер записи (относительно набора данных, включая фильтрацию и сортировку индекса); используется, если тип таблицы поддерживает последовательную нумерацию (только Paradox).

    iPhyRecNum: тип LongInt; определяет номер записи; используется, если тип таблицы поддерживает физические номера записи (только dBASE).

    bRecChanged: тип Boolean; в настоящее время не используется.

    bSeqNumChanged: тип Boolean; в настоящее время не используется.

    bDeleteFlag: тип Boolean; указывает на удаленную запись; используется, если тип таблицы поддерживает "мягкое" удаление (только dBASE).

    Одна из этих BDE-функций может быть вызвана из вашего приложения для заполнения данной структуры, из которой затем может быть извлечен физический номер записи. Ниже - пример использования для этой цели функции DbiGetRecord.

    function RecNo(ATable: TTable): LongInt;

    var

     R: RECProps;

     rslt: DbiResult;

     Error: array [0..255] of Char;

    begin

     ATable.UpdateCursorPos;

     rslt := DbiGetRecord(ATable.Handle, dbiNoLock, nil, @R);

     if rslt = DBIERR_NONE then Result := R.iPhyRecNum

     else begin

      DbiGetErrorString(rslt, Error);

      ShowMessage(StrPas(Error));

      Result := -1;

     end;

    end;

    Для вызова любой BDE-функции из приложения Delphi, модули-обертки BDE DbiTypes, DbiErrs и DbiProcs должны быть включены в секцию Uses модуля, из которого они будут вызываться (секция Uses здесь не показана). Для того, чтобы сделать функции более транспортабельными, они не имеют прямой ссылки на компонент TTable, но указатель на TTable передается как параметр. Если эта функция используется в модуле, который не ссылается на модули Delphi DB и DBTables, они должны быть добавлены, иначе ссылки на компонент TTable будут недействительными.

    Метод TTable UpdateCursorPos вызывается для гарантии синхронизации номера текущей записи в компоненте TTable и связанной с ним таблицы.

    В случае ошибок BDE функций, исключительная ситуация ими не генерируется. Вместо этого они возвращают значение BDE-типа DbiResult, указывающее на успешное завершение или ошибку операции. Возвращаемое значение должно быть получено и обработано внешним приложением, с выполнением соответствующих действий. Любой результат, кроме DBIERR_NONE, указывает на неудачное выполнение функции. В этом случае может быть осуществлено дополнительное действие (как в примере выше), где с помощью BDE функции DbiGetErrorString код ошибки переводится в удобночитаемое сообщение. В этом примере возвращаемое в DbiGetRecord значение сохраняется в переменной rslt, а затем для определения успешности вызова функции сравнивается с DBIERR_NONE.

    Если вызов DbiGetRecord был успешным, физический номер записи из поля iPhyRecNum структуры pRECProps сохраняется в переменной Result, которая является возвращаемой функцией величиной. Чтобы указать на то, что функция потерпела неудачу (т.е., вызов фунции DbiGetRecord окончился неудачно), вместо номера записи возвращается отрицательная величина. Значение ее может быть произвольным (отрицательная величина совместимого типа) и отдается на усмотрение программисту.

    Пакование таблиц dBASE II

    Упаковка таблиц dBASE требует вызова BDE функции DbiPackTable. Пример ее использования показан ниже, включая проверку на ошибки. Чтобы воспользоваться функцией DbiPackTable, вызывающий модуль должен в своей секции uses иметь модули-обертки BDE DbiTypes, DbiErrs и DbiProcs.

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

    Вот сам пример:

    procedure TForm1.Button1Click(Sender: TObject);

    var

     Error: DbiResult;

     ErrorMsg: String;

     Special: DBIMSG;

    begin

     table1.Active := False;

     try

      Table1.Exclusive := True;

      Table1.Active := True;

      Error := DbiPackTable(Table1.DBHandle, Table1.Handle, nil, szdBASE, True);

      Table1.Active := False;

      Table1.Exclusive := False;

     finally

      Table1.Active := True;

     end;

     case Error of

     DBIERR_NONE: ErrorMsg := 'Успешно';

     DBIERR_INVALIDPARAM: ErrorMsg := 'Указанное имя таблицы или указатель на имя таблицы ' +'равен NULL';

     DBIERR_INVALIDHNDL: ErrorMsg := 'Указанный дескриптор базы данных или курсора ' +'неверен или равен NULL';

     DBIERR_NOSUCHTABLE: ErrorMsg := 'Таблица с таким именем не существует';

     DBIERR_UNKNOWNTBLTYPE: ErrorMsg := 'Неизвестный тип таблицы';

     DBIERR_NEEDEXCLACCESS: ErrorMsg := 'Таблица открыта не в эксклюзивном режиме';

     else

      DbiGetErrorString(Error, Special);

      ErrorMsg := '[' + IntToStr(Error) + ']: ' + Special;

     end;

     MessageDlg(ErrorMsg, mtWarning, [mbOk], 0);

    end;
     

    Пакование таблиц dBASE III

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

    1. Добавьте в секцию uses следующие модули:

    { Для Delphi 1.0: } DBITYPES, DBIPROCS и DBIERRS;

    { Для Delphi 2.0: } BDE;

    2. Затем вызовите BDE функцию DbiPackTable следующим образом:

    Check(DbiPackTable(Table1.DbHandle, Table1.Handle, Nil, szDBASE, TRUE));

    Примечания:

    • Таблица должна быть открыта в эксклюзивном режиме.

    • При вызове функций API BDE используйте процедуру Check. Check в случае ошибки при вызове BDE генерирует исключительную ситуацию.

    Пакование таблиц dBASE IV

    Nomadic советует:

    Для dBase:

    uses DbiProcs;

    with table do

     begin

     OldState := Active;

     Close;

     Exclusive := True;

     Open;

     DbiPackTable(DBHandle, Handle, nil, nil, True);

     {^ здесь можно добавить check()}

     Close;

     Exclusive := False;

     Active := OldState;

     { при желании можно сохранить закладку }

    end;

    Pavel Kulchenko

    (2:465/66)

    Пример для Paradox:

    Uses BDE; // for d3, для d2 не помню (что-то типа dbiprocs и еще что-то)

    // для пpимеpа

    tLog : TTable; // таблица, юзающая d:\db\log.db

    var

     TblDesc: CRTblDesc;

     rslt: DBIResult;

     Dir: String; //имеется в виду huge string т.е. {$h+}

     hDb: hDbiDb;

    begin

     tLog.Active := False; //деактивиpуем TTable

     SetLength(Dir, dbiMaxNameLen + 1);

     DbiGetDirectory(tLog.DBHandle, False, PChar(Dir));

     SetLength(Dir, StrLen(PChar(Dir)));

     DbiOpenDatabase(nil, nil, dbiReadWrite, dbiOpenExcl, nil, 0, nil, nil, hDb);

     DbiSetDirectory(hDb, PChar(Dir));

     FillChar(TblDesc, sizeof(CRTblDesc), 0);

     StrPCopy(TblDesc.szTblName, 'd:\db\log.db');

     // здесь должно быть полное имя файла

     //котоpое можно: а) ввести pуками;

     //б) вытащить из пpопеpтей таблицы;

     //в) вытащить из алиаса;

     //г) см. FAQ

     StrCopy(TblDesc.szTblType, szParadox);

     //BTW тут может и szDBase стоять

     TblDesc.bPack := TRUE;

     DbiDoRestructure(hDb, 1, @TblDesc, nil, nil, nil, false);

     DbiCloseDatabase(hDb);

    end;

    // можно еще чеки ввести, но облом :-)

    Показ удаленных записей в таблице dBASE

    В таблицах dBASE записи не удаляются до тех пор, пока таблица не будет упакована. Пока же это не произойдет, удаленные записи остаются в таблице, только имеют при этом флажок "к удалению". Для того, чтобы показать эти существующие, но не отображаемые записи, существует функция ShowDeleted(), которая использует функцию BDE API DbiSetProp(), показывающая записи, помеченные к удалению. При использовании этой функции нет необходимости закрывать и вновь открывать таблицу. ShowDeleted() в качестве параметров передается TTable и логическое значение. Логический параметр указывает на необходимость показа удаленных записей.

    Демонстрационный проект:

    unit Unit1;


    interface


    uses

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


    type

     TForm1 = class(TForm)

      Table1: TTable;

      DataSource1: TDataSource;

      DBGrid1: TDBGrid;

      DBNavigator1: TDBNavigator;

      CheckBox1: TCheckBox;

      procedure CheckBox1Click(Sender: TObject);

     public

      procedure ShowDeleted(Table: TTable; ShowDeleted: Boolean);

     end;


    var

     Form1: TForm1;


    implementation


    uses DBITYPES, DBIERRS, DBIPROCS;


    {$R *.DFM}


    procedure TForm1.ShowDeleted(Table: TTable; ShowDeleted: Boolean);

    var

     rslt: DBIResult;

     szErrMsg: DBIMSG;

    begin

     Table.DisableControls;

     try

      Check(DbiSetProp(hDBIObj(Table.Handle), curSOFTDELETEON, LongInt(ShowDeleted)));

     finally

      Table.EnableControls;

     end;

     Table.Refresh;

    end;


    procedure TForm1.CheckBox1Click(Sender: TObject);

    begin

     ShowDeleted(Table1, CheckBox1.Checked);

    end;


    end.
     

    Пароль на dBASE-файлы

    Delphi 1 

    dBase-файлы не поддерживают пароли. Естественно, вы можете создать свои собственные методы поддержки паролей. Но это будет работать только с вашими приложениями. Боюсь, что при наличии тысяч читателей/конверторов dBase, этот способ не годится. 

    Показ меток 'удаленных' записей в dBASE-файлах

    Delphi 1 

    Для начала вы должны включить SoftDeletes, после чего вы сможете просматривать записи, помеченные к удалению. В противном случае, вы их не увидите. По умолчанию, для файлов DBF, SoftDeletes установлен в False. Вот логика работы:

    procedure TForm1.Button1Click(Sender: TObject);

    var

     B: BOOL;

     W: Word;

    begin

     Check(DbiSetProp(hDBIObj(Table1.Handle), curSOFTDELETEON, longint(True)));

     { Проверяем, что это работает }

     Check(DbiGetProp(hDBIObj(Table1.Handle), curSOFTDELETEON, @B, sizeof(B), W));

     if B = False then Label2.Caption := 'Не помечена'

     else Label2.Caption := 'Помечена';

    end;

    Когда указатель на запись указывает на запись, которую вы хотите удалить, используйте следующую логику:

    Table1.UpdateCursorPos;

    Check(DbiUndeleteRecord(Table1.Handle));

    Метод UpdateCursorPos устанавливает основной курсор BDE на позицию курсора текущей записи, который существуют только для того, чтобы все работало правильно. Вам нужно только вызвать этот метод прямым вызовом одной из BDE API функций (такой как, например, DbiUndeleteRecord).

    Ну и, наконец, чтобы все работало, поместите модули DBIPROCS и DBITYPES с список USES. 

    DB2 

    Как заставить работать DB2 через протокол IPX?

    Nomadic отвечает:

    Связь Win-клиента c DB2 в сети Netware

    Hастройка доступа к DB2

    1. Связь с использованием протокола IPX/SPX.

    Возможны два варианта доступа:

    • через сервер NETWARE;

    • прямая адресация.

    1.1. Конфигурация для доступа через сервер.

    Замечание: Проверялся доступ через сервера NW 3.11 и 3.12. Для 4.х нужно еще разобраться.

    1.1.1. DB2 Сервер

    • должна быть установлена OS/2 Warp или OS/2 Warp Connect;

    • включена поддержка NETWARE;

    • в CONFIG.SYS в переменную среды DB2COMM добавить (через запятую) IPXSPX и перезагрузить систему;

    • создать командный файл DBIPXSET.CMD следующего вида:

    db2 update dbm cfg using fileserver objectname dbserver

    где – <NWSERVER> – имя сервера;

    • выполнить командный файл DBIPXSET.CMD;

    • перестартовать сервер базы данных;

    • создать командный файл DBIPXREG.CMD следующего вида:

    db2 register nwbindery user

    где – <USERNAME> – имя пользователя, обладающего правами администратора на сервере <NWSERVER>;

    • выполнить командный файл DBIPXREG.CMD;

    • ответить на запрос пароля.

    1.1.2. WINDOWS-клиент

    • установить WINDOWS 3.1 или WfWG 3.11;

    • установить клиента NETWARE от версии 4.х;

    • при установке влючить поддержку WINDOWS;

    • установить клиента DB2 для WINDOWS;

    • используя программу Client Setup описать новый узел – сервер базы данных:

    Name – <любое имя>

    Protocol – IPX/SPX

    File server – <NWSERVER>

    Object name – dbserver

    • описать базу данных и разрешить доступ к ней через ODBC.

    1.2. Конфигурация для доступа через прямую адресацию

    1.2.1. DB2 Сервер

    • см. п 1.1.1;

    • найти в директории x:\sqllib\misc программу DB2IPXAD.EXE и выполнить ее;

    • записать полученный адрес;

    1.2.2. WINDOWS-клиент

    • см. п. 1.1.2. (первые три шага);

    • используя программу Client Setup описать новый узел – сервер базы данных:

    Name – <любое имя>

    Protocol – IPX/SPX

    File server – *

    Object name – <адрес полученный от DB2IPXAD.EXE>

    • описать базу данных и разрешить доступ к ней через ODBC. 

    Почему DB2 ругается на Create Trigger?

    Nomadic отвечает:

    Я тут писал по поводу того, что у меня не работали триггеры. Все дело оказалось в правиле написания команды «create trigger». Если все остальные команды корректно воспpинимаются на любом регистре, то эта – только набранная одними большими буквами.

    Модули данных 

    Модуль данных для каждого MDIChild

    Delphi 2 

    Когда во время разработки вы устанавливаете "DataSource"-свойство в БД-компонентах для указания на модуль данных, VCL во время выполнения приложения будет пытаться создать связь с существующим TDataModule, основываясь на его свойтсве Name. Так, если вы добавите модуль данных к вашему проекту и переместите его в свойстве проекта из колонки автоматически создаваемых форм в колонку доступных, вы сможете разработать форму, содержащую элементы управления для работы с базами данных, после чего несколькими строчками кода можете создать экземпляр формы, имеющий экземпляр собственного модуля данных.

    С помощью Репозитория создайте "standard MDI application" (стандартное MDI-приложение), в котором модуль TMDICHild будет похож на приведенный ниже. Добавленные строки имеют комментарий {!}. Хитрости спрятаны в конструкторе create и задании другого порядка следования операторов.

    unit Childwin;


    interface


    uses Windows, Classes, Graphics, Forms, Controls,ExtCtrls, DBCtrls, StdCtrls, Mask, Grids, DBGrids,DataM; {!} // Модуль TDataModule1


    type

     TMDIChild = class(TForm)

      DBGrid1: TDBGrid;

      DBGrid2: TDBGrid;

      DBEdit1: TDBEdit;

      DBEdit2: TDBEdit;

      DBNavigator1: TDBNavigator;

      procedure FormClose(Sender: TObject; var Action: TCloseAction);

     private { Private declarations }

     public { Public declarations }

      {!} DM:TDataModule1;

      {!} constructor Create(AOwner:TComponent); override;

     end;


    implementation


    {$IFDEF XOXOXOX}  // DataM должен находиться в секции interface. Необходимо для среды

    uses DataM;       // времени проектирования. Определение "XOXOXOX" подразумевает,{$ENDIF}

                      // что это никогда не будет определено, но чтобы компилятор видел это.


    {$R *.DFM}


    {!} constructor TMDIChild.Create;

    {!} begin

    {!}  DM := TDataModule1.Create(Application);

    {!}  inherited Create(AOwner);

    {!}  DM.Name := '';

    {!} end;


    procedure TMDIChild.FormClose(Sender: TObject; var Action: TCloseAction);

    begin

     Action := caFree;

    end;


    end.

    – Pat Ritchey 

    Как передать UserName и Password в удаленный модуль данных (remote datamodule)?

    Nomadic отвечает:

    В Удаленный Модуль Данных бросьте компонент TDatabase, затем добавьте процедуру автоматизации (пункт главного меню Edit | Add To Interface) для Login.

    Убедитесь, что свойство HandleShared компонента TDatabase установлено в True.

    procedure Login(UserName, Password: WideString);

    begin

     { DB = TDatabase }

     { Something unique between clients }

     DB.DatabaseName := UserName + 'DB';

     DB.Params.Values['USER NAME'] := UserName;

     DB.Params.Values['PASSWORD'] := Password;

     DB.Open;

    end;

    После того, как Вы создали этот метод автоматизации, Вы можете вызывать его с помощью:

    RemoteServer1.AppServer.Login('USERNAME','PASSWORD');

    Paradox 

    Byte-поля Paradox

    Delphi 2 

    Что за магия при записи в поле Paradox Byte? По этому поводу в документации ничего не сказано.

    Есть 2 пути получить доступ к данным в TBytesField.

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

    procedure SetCheckBoxStates;

    var CBStates: array[1..13] of Byte;

    begin

     CBStateField.GetData(CBStates);

     { Здесь обрабатываем данные… }

    end;

    Для записи значений вы должны использовать SetData.

    Используйте свойство Value, возвращающее вариантный массив байт (variant array of bytes):

    procedure SetCheckBoxStates;

    var CBStates: Variant;

    begin

     CBStates := CBStateField.Value;

     { Здесь обрабатываем данные… }

    end;

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

    – Mark Edington

    Доступ к таблицам Paradox на CD или c флагом только для чтения

    Тема: Доступ к таблицам Paradox на CD или на дисках c флагом только для чтения

    Данный совет поможет вам разобраться в таком вопросе, как доступ к таблицам Paradox, расположенным на CD-ROM или диске, имеющем флаг "только для чтения".

    Механиз блокирования файлов Paradox требует наличие файла PDOXUSRS.LCK, осуществляющий логику работы блокировки. Данный файл обычно создается во время выполнения приложения и располагается в том же каталоге, где и таблицы. Тем не менее, в случае с CD-ROM, во время выполнения программы нет никакой возможности создать на нем описанный выше файл. Решение простое: мы создаем этот файл и помещаем его на CD-ROM во время его (CD) создания. Следующая простейшая программка позволит создать вам файл PDOXUSRS.LCK и поместить его в образ компакта для его последующего копирования на CD-ROM:

    1. Стартуйте пустой проект и добавьте на форму следующие компоненты: TEdit, TButton и TDatabase.

    2. В обработчике кнопки OnClick используйте следующий код:

    procedure TForm1.Button1Click(Sender: TObject);

    begin

     if ChkPath then Check(DbiAcqPersistTableLock(Database1.Handle, 'PARADOX.DRO','PARADOX'));

    end;

    3. Функция ChkPath является методом, определенным пользователем для формы. Она просто проверяет путь, введенный пользователем в поле редактирования и убеждается, что он существует. Вот функция:

    function TForm1.ChkPath : Boolean;

    var s: array[0..100] of char;

    begin

     If DirectoryExists(Edit1.Text) then begin

    DataBase1.DatabaseName:= 'TempDB';

      DataBase1.DriverName:= 'Standard';

      DataBase1.LoginPrompt:= false;

      DataBase1.Connected := False;

      DataBase1.Params.Add('Path=' + Edit1.Text);

      DataBase1.Connected := TRUE;Result := TRUE;

     end else begin

    StrPCopy(s,'Каталог : ' + Edit1.text + ' не найден');

      Application.MessageBox(s, 'Ошибка!', MB_ICONSTOP);

      Result := FALSE;

     end;

    end;

    { Примечание: Не забудьте добавить объявление функции в секцию public формы.}

    4. Перед компиляцией необходимо вспомнить еще об одной вещи: в список Uses нужно добавить следующие модули:

    Delphi 1.0: FileCtrl, DbiProcs, DbiTypes, DbiErrs.

    Delphi 2.0: FileCtrl, BDE

    После компиляции и выполнения, программа создаст два файла в определенном вами каталоге. Создаваемые два файла: PDOXUSRS.LCK и PARADOX.LCK.

    Примечание: Файл PARADOX.LCK необходим только для доступа к таблицам Paradox for DOS, так что вы можете его удалить.

    5. Вам осталась сделать только одну последнюю вещь: скопировать оставшийся файл (PDOXUSRS.LCK) в образ CD-ROM. Естественно, ваши таблицы будут только для чтения.

    Примечание: Если вы собираетесь довольно часто пользоваться данной утилитой, то для удобства вы можете изменить свойство Text компонента Edit на ваш «любимый» каталог, а свойство Caption кнопки поменять на что-нибудь более «интеллектуальное».

    Вот окончательная версия кода:

    unit Unit1;


    interface


    uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, DB, StdCtrls, FileCtrl,

    {$IFDEF WIN32}

     BDE;

    {$ELSE}

     DbiProcs, DbiTypes, DbiErrs;

    {$ENDIF }


    type TForm1 = class(TForm)

     Edit1: TEdit;

     Button1: TButton;

     Database1: TDatabase;

     procedure Button1Click(Sender: TObject);

    private { Private declarations }

    public { Public declarations }

     function ChkPath : Boolean;

    end;


    var Form1: TForm1;


    implementation


    {$R *.DFM}


    function TForm1.ChkPath : Boolean;

    var s: array[0..100] of char;

    begin

     If DirectoryExists(Edit1.Text) then begin

      DataBase1.DatabaseName:= 'TempDB';

      DataBase1.DriverName:= 'Standard';

      DataBase1.LoginPrompt:= false;

      DataBase1.Connected := False;

      DataBase1.Params.Add('Path=' + Edit1.Text);

      DataBase1.Connected := TRUE;

      Result := TRUE;

     end else begin

      StrPCopy(s,'Каталог : ' + Edit1.text + ' не найден');

      Application.MessageBox(s, 'Ошибка!', MB_ICONSTOP);

      Result := FALSE;

     end;

    end;


    procedure TForm1.Button1Click(Sender: TObject);

    begin

     if ChkPath then Check(DbiAcqPersistTableLock(Database1.Handle, 'PARADOX.DRO','PARADOX'));

    end;


    end.
     

    Нечувствительный к регистру поиск в первичном индексе Paradox

    Delphi 1 

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

    Создание таблицы Paradox

    Delphi 1 

    Вот маленький кусочек кода для создания таблицы Paradox:

    with TTable.create(self) do begin

     DatabaseName := 'C:\temp';

     TableName := 'FOO';

     TableType := ttParadox;

     with FieldDefs do Begin

      Add('Age', ftInteger, 0, True);

      Add('Name', ftString, 25, False);

      Add('Weight', ftFloat, 0, False);

     End;

     IndexDefs.Add('MainIndex','IntField', [ixPrimary, ixUnique]);

     CreateTable;

    End;
     

    DBEdit и реальные значения

    При работе с реальными числами, хранимые в таблице Paradox, вы уверены в том, что вы используете тип Real как тип ваших данных? Если так, то попробуйте использовать тип Double. Double – 8-байтовое (64-битное) реальное число, которое нормально работает с BDE, тогда как Real – 6-байтовая версия и подходит только для Delphi и BP. Или попробуйте использовать Extended, занимающий 10 байтов.  

    Почему при создании таблицы Paradox с первичным нечувствительным к регистру индексом вываливается ошибка?

    Пара строк

    Nomadic отвечает:

    В Парадоксе первичный индекс всегда CaseSensitive. 

    Как сменить пароль (master password) для таблицы Paradox?

    Nomadic отвечает:

    Пожалуйста: 

    var

     db : TDatabase;

     Desc : CRTblDesc;

    begin

     db := PriceTable.OpenDatabase;

     FillChar(Desc, SizeOf(Desc), #0 );

     StrCopy(Desc.szTblName, PChar(PriceTable.TableName));

     StrCopy(Desc.szTblType, szParadox);

     StrCopy(Desc.szPassword, 'password');

     Desc.bProtected := TRUE;

     Check(DbiDoRestructure(db.Handle, 1, @Desc, nil, nil, nil, FALSE));

    end;
     

    Что нужно сделать для нормальной работы в одноранговой сети с базами Paradox?

    Nomadic отвечает:

    BDE Config/Admin – нa вкладке System устaнови LOCAL SHARE в TRUE!

    Здесь комментарий –

    В Help параметр LOCAL SHARE описан как:

    AA> === Cut ===

    AA> The ability to share access to local data between an active BDE

    AA> application and an active non-BDE application. Set to TRUE if you need to

    AA> work with the same files through both a BDE and a non-BDE application at

    AA> the same time. (It is not necessary to set LOCAL SHARE to TRUE if you do

    AA> not need to have both applications open at the same time.) Default: FALSE.

    AA> === Cut ===

    Дак читал я вышеизложенное, и расцениваю его кaк туманопускательство. А подозреваю, что просто у BDE для скорости есть свой внутренний кэш (или, может, мехaнизм блокировок в пaмяти), и для двух приложений на одном компьютере оно всё делает хорошо, a вот если приложение находится на другом компьютере (и лезет в БД через другую копию BDE), то у него есть доступ только к файлам нa диске (как и у non-BDE application).

    Скорее всего, борланд отключает эти хитрости у сетевых дисков. Hо для локального дискa, который рaсшарен по сети, он этого, похоже, не сделaл :(

    И BDE нa файл-сервере не заботится о правильных индексaх и блокировках нa диске (т.е. не ожидает, что кто-то мог исправить индекс, пока оно ворон считaло).

    А этa установка заставляет его работать по старым парадоховым соглашениям.

    Что и требовалось.

    PS. Иначе говоря, следует считать, что network is non-BDE application, и тогда это не есть бага :) 

    Переиндексирование файлов Paradox в пределах моей программы

    Delphi 1 

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

    …сейчая я пишу небольшое приложение, которое может оказаться полезным для восстановления «разбитых» таблиц. Аллен, я пошлю тебе полный список шагов, как только смогу перевести их (это писал итальянец, я же только перевожу это), но уже сейчас я могу сказать как это проблему я решил для себя. Один из наших клиентов всегда разбивал таблицы paradox, поскольку они у него всегда были очень большими (в Blob-полях хранились WAV-файлы – оцифрованный голос). Решение заключалось в создании маленьких таблиц, включенных в отношение справочной целостности, и загрузки больших blob-полей в эти отдельные таблицы. Ненужно никаких BDE функций, единственное условие – вы не должны вручную удалять индексные файлы (все .x00, .y01 и т.д., они все имеют маску .x?? и .y??, не трогайте других файлов!), в противном случае вы НЕ СМОЖЕТЕ ОТКРЫТЬ ТАБЛИЦУ, даже с помощью DBD!

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

    Разное 

    Помещение Memo-файла с ASCII-разделителем в Memo-поле таблицы

    Вам нужно использовать процедуру getTextBuf. Вот пример из электронной справки:

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

    procedure TForm1.Button1Click(Sender: TObject);

    var

     Buffer: PChar;

     Size: Byte;

    begin

     Size := Edit1.GetTextLen;      {Получаем длину строки в Edit1}

     Inc(Size);                     {Добавляем место для терминирующего нуля}

     GetMem(Buffer, Size);          {Создаем динамическую переменную Buffer}

     Edit1.GetTextBuf(Buffer,Size); {Помещаем Edit1.Text в Buffer}

     Edit2.Text := StrPas(Buffer);  {Преобразуем Buffer в строку паскалевского типа}

     FreeMem(Buffer, Size);         {Освобождаем память, распределенную для Buffer}

    end;
     

    Почему не всегда верно обновляются IndexDefs по Update?

    Пара строк 

    Nomadic отвечает:

    Ошибка в VCL.

    А помогает добавление fUpdated:=false; в теле процедуры TIndexDefs.Update.

    Или убиением владельца через Free, и пересозданием.

    БД-дерево взаимоотношений

    Delphi 1

    Все это я делал раньше. Я не могу вам все это показать на развернутом примере, но я дам вам идею как сделать это. Вы должны иметь таблицу, осуществляющую взаимоотношение между людьми. Если на Peter работают Jane и Simon, вы должны иметь таблицу (RELATION) с этими двумя записями.

    Master

    Slave ------- имена полей

    Peter Jane

    Peter Simon

    Если George и Elisa работают на Jane, то таблица становится такой:

    Master Slave ------- имена полей

    Peter Jane

    Peter Simon

    Jane George

    Jane Elisa

    и так далее.

    Если в таблице RELATION необходимо создать дерево, начинающееся на Peter, то нужно добавить к дереву главный узел (запись), где Master = Peter. Затем каждая дочерняя запись располагается ниже записи Master = Peter. После добавления дочерней записи вы сразу увидите, если ребенок имеет собственного ребенка. Ребенок становится теперь, вероятно, отцом, поэтому вы должны позиционировать таблицу RELATION к первой записи, где Master = child, и так далее, рекурсивно. Такой способ гарантирует построение правильного дерева.

    Пример:

    AddFather('Peter')

    AddChild('Peter',1)


    Procedure AddFather(Name: String)

    Begin

     Tree.Add(Name);

    End;


    Procedure AddChildr(Name: String, Index:Integer)

    Begin

     Relation.FindKey([Name])while RelationMaster.AsString = Name do

     Begin

      Tree.AddChild(Index,RelationSlave.AsString);

      AddChild(RelationSlave.AsString,Tree.ItemsCount);

      Relation.Next;

     End;

    End;

    По-моему, ошибок нет.

    DBGrid и Memo-поля

    Delphi 1

    В обработчик события GetText TMemoField поместите следующую строку:

    Text := GrabMemoAsString(TMemoField(Sender));

    и поместите следующую функцию так, чтобы к ней можно было свободно обратиться:

    function GrabMemoAsString(TheField : TMemoField): String;

    begin

     if TheField.IsNull then Result := '' else with TBlobStream.Create(TheField, bmRead) do begin

      if Size >= 255 then begin

       Read(Result[1], 255);

       Result[0] := #255;

      end else begin

       Read(Result[1], Size);

       Result[0] := Chr(Size);

      end;

      Free;

      while Pos(#10, Result) > 0 do Result[Pos(#10, Result)] := ' ';

      while Pos(#13, Result) > 0 do Result[Pos(#13, Result] := ' ';

    end;

    end;
     

    Убывающий индекс

    Delphi 1 

    Я нашел простой способ получения убывающего индекса. В Delphi это получается очень легко и красиво: 

    Table1.AddIndex('NewIndex', 'CustNo;CustName', [ixDescending]);
     

    Как работать из Delphi напрямую с MS ADO (Microsoft Active Data Objects)?

    Nomadic отвечает:

    Итак, хочу поделиться некоторыми достижениями… так на всякий случай. Если у вас вдруг потребуется сделать в своей программке доступ к базе данных, а BDE использовать будет неохота (или невозможно) – то есть довольно приятный вариант: использовать ActiveX Data Objects. Однако с их использованием есть некоторые проблемы, и одна из них это как передавать Optional параметры, которые вроде как можно не указывать. Однако, если вы работаете с ADO по-человечески, а не через тормозной IDispatch.Invoke то это превращается в головную боль. Вот как от нее избавляться: 

    var

     OptionalParam: OleVariant;

     VarData: PVarData;

    begin

     OptionalParam := DISP_E_PARAMNOTFOUND;

     VarData := @OptionalParam;

     VarData^.VType := varError;

    после этого переменную OptionalParam можно передавать вместо неиспользуемого аргумента.

    Далее, самый приятный способ получения Result sets:

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

    var

     MyConn: _Connection;

     MyComm: _Command;

     MyRecSet: _Recordset;

     prm1: _Parameter;

    begin

     MyConn := CoConnection.Create;

     MyConn.ConnectionString := 'DSN=pubs;uid=sa;pwd=;';

     MyConn.Open('', '', '', –1);

     MyCommand := CoCommand.Create;

     MyCommand.ActiveConnection := MyConn;

     MyCommand.CommandText := 'SELECT * FROM blahblah WHERE BlahID=?'

     Prm1 := MyCommand.CreateParameter('Id', adInteger.adParamInput, –1, <value>);

     MyCommand.AppendParameter(Prm1);

     MyRecSet := CoRecordSet.Create;

     MyRecSet.Open(MyCommand, OptionalParam, adOpenDynamic, adLockReadOnly, adCmdText);

    …теперь можно фетчить записи. Работает шустро и классно. Меня радует. Особенно радуют серверные курсоры.

    Проверялось на Delphi 3.02 + ADO 1.5 + MS SQL 6.5 sp4. Пашет как зверь.

    Из вкусностей ADO – их легко можно использовать во всяких многопоточных приложениях, где BDE порой сбоит, если, конечно, ODBC драйвер грамотно сделан…

    Ну и еще можно использовать для доступа к данным всяких там «нестандартных» баз типа MS Index Server или MS Active Directory Services.

    В Delphi (как минимум в 4 версии) существует «константа» EmptyParam, которую можно подставлять в качестве пустого параметра. 

    Как засунуть в качестве паpаметpа хpанимой пpоцедуpы стpоку длиной более 255 символов? И вообще, как использовать паpаметpы SP, если они BLOB?

    Nomadic отвечает:

    «Засунуть» длинную строку можно было и раньше, если написать редактируемый запрос, и воспользоваться операциями Insert/Edit.

    Однако это не относится к хранимым процедурам.

    В Delphi 3.0 появился новый тип параметра (TBlobField вроде) и соответственно его поддержка в BDE.

    Если просто взять BDE 4.01 и выше, то работать все-равно не будет – нужна соотв. версия VCL (из Delphi 3.0 или выше). 

    Дублирование набора записей

    Delphi 1 

    Вы можете воспользоваться вторым объектом TTable, подключенным к той же таблице, или можете вызвать метод объект TTable DisableControls, сделать изменения, и вызвать EnableControls. Для сохранения той же позиции вы можете попробовать воспользоваться закладкой. Например, так:

    procedure TMyForm.MakeChanges;

    var

     aBookmark: TBookmark;

    begin

     Table1.DisableControls;

     aBookmark := Table.GetBookmark;

     try

      {ваш код}

     finally

      Table1.GotoBookmark(aBookmark);

      Table1.FreeBookmark(aBookmark);

      Table1.EnableControls;

     end;

    end;

    Как программно изменить LangDriver для таблиц dBase и Paradox?


    Nomadic отвечает:

    Откpываешь help и смотpишь:

    ……

    var list:tstrings;

    ……

    BEGIN

    …….

    List.Add ( 'LANGDRIVER=db866ru0 ');

    ……

    Session.ModifyDriver( 'DBASE', List );

    ……

    END;

    Это действие я пpовожy пеpед откpытием таблицы

    Ivan Sboev

    (2:5049/36.15)

    Это о «русификации» таблицы. В таблицах dBase и Paradox имеется байт, который определяет CodePage содержимого таблицы. Раньше он не использовался и был зарезервирован. Тебе нужно его правильно установить. Это делается через DBD Restructure table. Если хочешь программно, можешь воспользоваться следующей процедурой:

    uses DbiTypes, DbiProcs, DbiErrs, DB, WinProcs, SysUtils;


    procedure ChangeLangDriver( DatabaseName, TableName, LDName: string );

    var

     TblExt: string;

     Database: TDatabase;

     TblDesc: CRTblDesc;

     OptDesc: FLDDesc;

     OptData: array [0..250] of Char;

     Cur: hDBICur;

     Rec: CFGDesc;

    begin

     if (TableName='') or (LDName='') then raise Exception.Create('Unknown TableName or LDName');

     Database:=Session.OpenDatabase(DatabaseName);

     try

      if Database.IsSQLBased then raise Exception.Create('Function ChangeLangDriver working only with dBase or Paradox tables');

      FillChar(OptDesc, SizeOf(OptDesc), #0);

      FillChar(TblDesc, SizeOf(TblDesc), #0);

      StrCopy(OptDesc.szName, 'LANGDRIVER');

      OptDesc.iLen := Length(LDName) + 1;

      with TblDesc do

      begin

       StrPCopy(szTblName, TableName);

       TblExt := UpperCase(ExtractFileExt(TableName));

       if TblExt = 'DBF' then StrCopy(szTblType, szDbase)

       else if TblExt = '.DB' then StrCopy(szTblType, szParadox)

       else

       begin

        AnsiToOEM(StrPCopy(OptData, DatabaseName), OptData);

       if DbiOpenCfgInfoList(nil, dbiREADONLY, cfgPersistent, StrPCopy(OptData, '\DATABASES\' + StrPas(OptData) + '\DB INFO\')Cur) <> DBIERR_NONE then raise Exception.Create('Unknown table type');

        try

         while DbiGetNextRecord(Cur, dbiNOLOCK, @Rec, nil) <> DBIERR_EOF do if StrComp(Rec.szNodeName, 'DEFAULT DRIVER') = 0 then

         begin

          StrCopy(szTblType, Rec.szValue);

          Break;

         end;

        finally

         Check(DbiCloseCursor(Cur));

        end;

       end;

       iOptParams := 1;

       pfldOptParams := @OptDesc;

       pOptData := @OptData;

      end;

      StrPCopy(OptData, LDName);

      Check(DbiDoRestructure(Database.Handle, 1, @TblDesc, nil, nil, nil, False));

     finally

      Session.CloseDatabase(Database);

     end;

    end;

    Примеры использования:

    ChangeLangDriver('DBDEMOS', 'EMPLOYEE', 'ancyrr');

    ChangeLangDriver('DBDEMOS', 'EMPLOYEE.DB', 'ancyrr');

    ChangeLangDriver('C:\DELPHI\DEMOS\DATA', 'CLIENTS.DBF', 'db866ru0');

    LDName:

    для D1 – имя .LD файла в каталоге IDAPI\LANGDRV;

    для D2 и CB – из BDECFG32.HLP поле Short name в табличке по указателю language drivers, dBASE или поле Internal в табличке по указателю language drivers, Paradox;

    для D3 и выше – не знаю так как у меня её нет, но думаю, что также, как и в D2. 

    Существует ли средство для вывода определения структуры таблицы?

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

    Nomadic отвечает:

    Для этого существует утилита DB2LOOK. Она находится в SQLLIB\MISC.

    Пример использования:

    CONNECT TO SAMPLE USER xxx USING yyy

    DB2LOOK –d SAMPLE –u xxx –e –t employee

    Вывод может быть перенаправлен в файл. Полный синтаксис выдаётся по команде:

    DB2LOOK ?
     

    У меня есть текстовые файлы, которые я хочу использовать в запросах к DB2, но не хочу создавать из них постоянные таблицы в базе. Что делать?

    Nomadic отвечает:

    Можно воспользоваться табличными функциями (Table Functions). Они позволяют использовать файлы как таблицы. Примеры приведены в руководстве «Embedded SQL Programming Guide».

    Список структуры полей таблицы

    В данном проекте создается список структуры полей соответствующей таблицы, с использованием массивов Fields и IndexDefs, который затем отображается в компоненте ListBox. Демонстрационный проект (dbbrowsr.dpr) решает эту задачу несколько иначе. Вы можете сравнить две версии этого кода.

    Примечание: Данный код работает только в 16-битной среде.

    procedure TForm1.Button1Click(Sender: TObject);

    const MyFielddefs: array[ftUnknown..ftGraphic] of string [8] = ('Unknown', 'String', 'Smallint', 'Integer', 'Word','Boolean', 'Float', 'Currency', 'BCD', 'Date','Time', 'DateTime', 'Bytes', 'VarBytes', 'Blob','Memo', 'Graphic');

    var

     i, Indx: integer;

     Definition: string;

    begin


     for  i := 0 to Table1.FieldCount - 1 do begin

      Definition := Table1.Fields[i].DisplayLabel;

      Definition := Definition + ' ' +MyFieldDefs[Table1.Fields[i].DataType];

      Table1.IndexDefs.Update;

      if Table1.Fields[i].IsIndexField then begin

       Indx := Table1.IndexDefs.Indexof(Table1.Fields[i].Name);

       if Indx > -1 then if ixPrimary in Table1.IndexDefs[Indx].Options then Definition := Definition + ' (Первичный)';

      end;

      Listbox1.Items.Add(Definition);

     end;

    end;

    Приведенная выше версия не работает в 32-битной среде, поскольку в ней присутствуют дополнительные типы полей. Вот версия, которая работает в 32-битной среде:

    procedure TForm1.Button1Click(Sender: TObject);

    const

     MyFielddefs: array[ftUnknown..ftTypedBinary] of string [11] =('Unknown', 'String', 'Smallint', 'Integer','Word', 'Boolean', 'Float', 'Currency', 'BCD','Date', 'Time', 'DateTime', 'Bytes', 'VarBytes','AutoInc', 'Blob', 'Memo', 'Graphic', 'FmtMemo','ParadoxOle', 'DBaseOle', 'TypedBinary');


    var

     i, Indx: integer;

     Definition: string;

    begin

     for  i := 0 to Table1.FieldCount - 1 do begin

      Definition := Table1.Fields[i].DisplayLabel;

      Definition := Definition + ' ' +MyFieldDefs[Table1.Fields[i].DataType];

      Table1.IndexDefs.Update;

      if Table1.Fields[i].IsIndexField then begin

       Indx := Table1.IndexDefs.Indexof(Table1.Fields[i].Name);

       if Indx > -1 thenif ixPrimary in Table1.IndexDefs[Indx].Options then Definition := Definition + ' (Первичный)';

      end;

      Listbox1.Items.Add(Definition);

     end;

    end;
     

    Создание индексного файла из Delphi

    Delphi 1 

    Если вы используете таблицы dBASE или Paradox, то для создания нового индекса воспользуйтесь методом AddIndex. Для примера: 

    Table1.AddIndex('Articles','Title', []);

    создаст индексный файл с именем ARTICLES с использованием поля TITLE в качестве индексного ключа. При создании вы можете воспользоваться различными индексными опциями (например, уникальность, необслуживаемый и пр.) – для получения дополнительной информации обратитесь к электронной справке по Delphi. ПРИМЕЧАНИЕ: Ваша таблица должна быть открыта исключительно для того, чтобы только воспользоваться методом AddIndex.

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

    Контекстное меню на основе базы данных

    var

     m:TMenuItem;

     navidummy:TComponent;

    …………………………………………………

    procedure  TMyForm.CreatePopUpMM(Sender: TObject);

    begin

     Navidummy.free;

     Navidummy:=TComponent.create(self);

     While not NaviT.EOF do


     begin

      m := TMenuItem.create(navidummy);

      II:=II+1;

      with m do begin

    name :='MM'+IntToStr(II);

       caption := NaviT.Fieldbyname('MyWHAT').AsString;

       tag := NaviT.Fieldbyname('MyTAG').AsInteger;

       visible:=True;

       OnClick:= NaviExec;

      end;

      MyMenuItem.add(m);

      NaviT.Next;

     end;

     NaviT.Close;

    end;


    procedure TMyForm.NaviExec(Sender:TObject);

    begin

     What.text := (Sender as TMenuItem).Caption;

     { Здесь я получаю то, что хочу ! }

     Key:= (Sender as TMenuItem).Tag;

    end;
     

    Корректное закрытие базы данных приложением Delphi

    Delphi 1 

    Очень интересный и полезный вопрос!! Я сам так с ним до конца и не разобрался! Но я попробую систематизировать события, происходящие при запросе на завершение работы Windows:

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

    Если при этом хотя бы одно из приложений ответит отрицательно, Windows прерывает процесс завершения работы.

    Delphi перехватывает это сообщение, и, в свою очередь, вызывает метод TForm.CloseQuery, (в главной форме, естественно), который генерирует событие OnCloseQuery, в обработчике которого можно указать на неготовность завершения приложения и отмены завершения работы Windows.

    Если я правильно понимаю, если ваше приложение «не мешает» Windows завершить свою работу, Windows нормально НЕ завершает работу приложения, поскольку для этого нет необходимости, не нужно освобождать память, ресурсы и пр. Так, если это утверждение верно (это легко можно проверить, но я слишком ленив сейчас), то событие OnCloseQuery – ваш единственный шанс сохранения данных на диске. Я не думаю что эта логика слишком плоха, просто это одна из тех причуд Windows, которую нужно знать и пользоваться ею. Что может произойти в описанном выше сценарии: редактируемая в настоящий момент запись не будет отправлена (Post) в базу данных, но та же самая вещь может случиться и при нормальном завершении приложения.

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

    Изменение свойств базы данных во время выполнения приложения

    Delphi 1 

    Свойство DatabaseName тесно связано с:

    • каталогом, где расположены ваши табличные файлы.

    • BDE-псевдоним вашей базы данных.

    • DatabaseName вашего компонента TDatabase, если вы имеете его.

    Выводы? 

    Как мне задать выражение по умолчанию для объекта TField?

    Delphi 3 

    Это будет работать, если вы уже установили атрибуты поля и ассоциировали его с полем вашей таблицы. Если вы установили значение в Инспекторе Объектов, т.е. задали строку, не думайте, что это сработает во время выполнения приложения. Если вы попытаетесь во время прогона установить свойство TField.DefaultExpression примерно так: 

    MyField.DefaultExpression := 'MyValue';

    то это скомпилируется, но при создании в таблице новой записи, скажем, при щелчке на кнопке + в DBNavigator, значения по умолчанию вы не получите. Чтобы во время работы приложения все работало, код должен быть таким: 

    MyField.DefaultExpression := '''MyValue''';

    В Инспекторе Объектов вам нужно просто поместить значение 'MyValue' (используя одинарные кавычки). 

    После того, как я использовал правый щелчок мыши для создания функции-провайдера, как мне снова выполнить команду контекстного меню `Export from Table`?

    Nomadic отвечает:

    Как только Вы экспортировали интерфейс провайдера, эта команда контекстного меню перестает быть видимой. Чтобы снова включить ее, Вы должны удалить ассоциированное свойство в Редакторе Библиотеки Типов, и затем нажать кнопку обновления информации в Редакторе Библиотеки Типов (Type Library Editor's Refresh button). Вы могли бы также удалить точку вхождения «Get_XXX» в исходном тексте RemoteDataModule. 

    Как работать с новыми, своими интерфейсами в RemoteDataModule?

    Nomadic отвечает:

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

    (IDispatch(RemoteServer.AppServer) as IAnother)

    Необходимо заметить, что это будет работать только, если Вы используете DCOM как транспорт. 

    Database Desktop показывает содержимое таблиц шрифтом без русских букв

    Nomadic отвечает:

    A: Для DBD 5.0 в файл c:\windows\pdoxwin.ini вставить в секцию

    [Properties]

    SystemFont=Arial Cyr

    Для DBD 7.0 нужно исправить реестр: ключ

    HKCU\Software\Borland\DBD\7.0\Preferences\Properties\

    SystemFont="Fixedsys"

    Если такой ключ не существует, его следует создать. Впрочем, для просмотра таблиц все равно можно порекомендовать rx Database Explorer – у него это получается очень хорошо.

    Ребят, я давно делаю под HТ (под 95 не знаю, не пробовал) такую вещь:

    [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Nls\CodePage]

    "1252"="c_1251.nls"
     








    Главная | В избранное | Наш E-MAIL | Прислать материал | Нашёл ошибку | Наверх