Советы по Delphi

         

ASCII драйвер для CSV-файлов


Использование драйвера ASCII для файлов с разделительной запятой

Delphi (и BDE) имеют способность использовать ASCII файлы для хранения таблиц. Драйвер ASCII имеет возможность транслировать значения данных ASCII-поля фиксированной длины или файла с разделительной запятой в поля и величины, которые могут отображаться компонентом TTable. Трансляция ASCII файла целиком зависит от сопровождающего файла схемы (Schema File). Файл схемы для файла ASCII данных определяет различные атрибуты, необходимые для преобразования данных ASCII файла в значения отдельных полей. Определения полей для файла с ASCII полями фиксированной длины достаточно простая задача, необходимо знать позиции всех полей, для всех строк они одинаковы. Для файлов с разделительной запятой данный процесс чуть более усложнен из-за того, что не все данные в таком файле во всех строках имеют одинаковую длину. Данный совет как раз и концентрируется на описании этой трудной темы, связанной с чтением данных из файлов с разделительной запятой, имеющих варьируемую длину поля.

Файл схемы

Файл схемы для файла данных ASCII содержит информацию, которая определяет оба типа файла (версии с разделительной запятой и полем с фиксированной длиной), а также определяет поля, которые представлены значениями данных в каждой строке файла данных ASCII. (Все поля файла схемы нечуствительны к регистру, поэтому написание "ascii" равнозначно написанию "ASCII".) Для того, чтобы файл схемы был признан в качестве такового, он должен иметь то же имя, что и файл данных ASCII, для которого он содержит схему, но иметь расширение .SCH (SCHema - схема). Атрибуты описания файла: File name: Располагаемый в квадратных скобках, данный атрибут определяет имя файла ASCII данных (с расширением имени файла, которое должно быть .TXT). Filetype: Определяет, имеет ли файл ASCII данных структуру файла с полями фиксированной длины (используется атрибут FIXED) или файлом с разделительной запятой (со значениями данных, которые потенциально могут изменять длину (используется атрибут VARYING). Delimiter: Определяет символ, которым "окантуривают" значения данных типа String (обычно двойные кавычки, десятичный ASCII код 34). Separator: Определяет символ, который используется для разделения отдельных значений данных (обычно запятая). Данный символ должен быть видимым символом, т.е. не может быть пробелом (десятичный ASCII код 32). CharSet: Определяет драйвер языка (используется атрибут ASCII). Расположенные ниже атрибуты файла являются определениями поля, задающими правила для каждой строки файла данных ASCII. Данные определения служат источником информации для Delphi и BDE, первоначально необходимой для создания виртуального поля в памяти, в свою очередь служащее для хранения значений данных; тип данных виртуального поля определяется после чтения и трансляции данных из ASCII файла, определения размера и применения атрибутов. Различные атрибуты, определяющие поле файла данных ASCII:

Field: Имя виртуального поля (всегда будет "Field"), сопровождаемое целым числом, определяющим порядковый номер поля относительно других полей в файле данных ASCII. Например, первое поле - Field1, второе Field2, и т.д.. Field name: Определяет выводимое имя поля, отображаемое в виде заголовка колонки в TDBGrid. Соглашения имен для таблиц ASCII такие же, как и для таблиц Paradox. Field type: Определяет, какой тип данных BDE должен использоваться при трансляции значений данных каждого поля и сообщает Delphi тип виртуального поля, которое необходимо создать. Используйте определение Для значений типа ----------------------- ---------------------------- CHAR Символ FLOAT 64-битное число с плавающей точкой NUMBER 16-битное целое BOOL Boolean (T или F) LONGINT 32-битное длинное целое DATE Поле Date. TIME Поле Time. TIMESTAMP Поле Date + Time. (Фактически формат для значений данных даты и времени будет определяться текущими настройками конфигурации BDE, страница с закладкой Date.) Data value length: Максимальная длина значения данных соответствующего поля. Данный атрибут определяет длину виртуального поля, создаваемое Delphi для получения считываемых значений из ASCII-файла. Number of decimals: Приложение к полю типа FLOAT; определяет количество цифр справа от десятичной точки; необходимо для включения в определение виртуального поля. Offset: Отступ от начала строки, позиция начала данных описываемого поля; задается для всех строк файла. Например, приведенное ниже определение поля относится к первому полю таблицы ASCII. Данная строка определяет значения данных типа String с именем "Text", максимальная длина значения данных составляет три символа (и в Delphi компонентах для работы с базами данных, типа TDBGrid, поле будет отображаться только тремя символами), десятичный порядок (значение данных типа String никогда не сможет иметь десятичные значения, тем более после запятой), и смещение относительно нулевой позиции (поскольку описываемая область первая, то она сама начинается с нулевой позиции, перед ней не находится ни одно поле).



Field1=Text,Char,3,00,00 Вот пример файла схемы с тремя полями, первое поле имеет тип String, второе и третье тип Date. Данный файл схемы должен содержаться в файле с именем DATES.SCH и обеспечивать определения полей для файла данных ASCII с именем DATES.TXT. [DATES] Filetype=VARYING Delimiter=" Separator=, CharSet=ascii Field1=Text,Char,3,00,00 Field2=First Contact,Date,10,00,03 Field3=Second,Date,10,00,13 Данная схема определяет поле с разделительной запятой, где все данные могут быть отнесены к типу String, значения полей "окантурены" двойными кавычками и отдельные значения полей разделены запятой (за исключением любых запятых, которые могут находится между разделительными запятыми, внутри отдельных значений полей типа String). Первое поле типа character имеет длину три символа, без определения десятичного порядка и с нулевым отступом от начала строки. Второе поле данных имеет длину 10, без определения десятичного порядка и отступ, равный трем. Третье поле данных имеет длину 10, без определения десятичного порядка и отступ, равный 13.

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

Вот файл данных с именем DATES.TXT, который соответствует описанному выше файлу схемы: "A",08/01/1995,08/11/19955 "BB",08/02/1995,08/12/1995 "CCC",08/03/1995,08/13/1995 Максимальная длина фактических значений данных в первом поле составляет три символа ("CCC"). Поскольку это первое поле и предшествующих полей не существует, отступ для данного поля равен нулю. Длина первого поля (3) используется в качестве отступа для второго поля. Длина второго поля, значение date, равно 10 и отражает максимальную длину значения данных этого поля. Совокупная длина первого и второго полей используется в качестве значения отступа для третьего поля (3 + 10 = 13).

Только когда соответствующая длина значения данных ASCII файла или длина каждого поля добавляется к длине предыдущих полей, вычисляется значение отступа и получается позиция очередного поля, только тогда данный процесс правильно считает данные. Если из-за неправильных установочных параметров в файле схемы данные транслируются неверно, то в большинстве типов полей могут возникнуть неблагоприятные эффекты типа обрезания строк, или интерпретирование цифр как нулей. Обычно в таком случае данные выводятся, но ошибки не возникает. Тем не менее, значения определенного формата в процессе трансляции в подходящий тип данных могут вызвать ошибку, если считываемые символы не соответствуют символам, например, в типе date. В контексте вышесказанного, ошибка с типом datе может возникнуть и-за того, что при неправильном определении в значения данных могут попасть данные другого, соседнего поля. При таком стечение обстоятельств трансляция данных прерывается, и в файле схемы требуется установка правильной длины поля и его отступа.

[000641]



ASCII-файл с использованием полей


В том случае, когда вы собираетесь использовать содержимое текстового файла таким образом, как будто он имеет поля, вам необходим файл схемы, содержащий описание формата текстового файла и который необходим для осуществления вызовов при работе с полями (Fields / FieldByName / Post / и др.). Ниже приводится код, который вы можете использовать при создании своей программы:

    { Подразумеваем, что Table1 - файл, который мы хотим скопировать в ASCII-файл. Используем TBatchMove, поскольку быстро работает. Также это автоматически создаст файл схемы }

procedure TForm1.Button1Click(Sender: TObject);
var
oDest: TTable ; oBMove: TBatchMove ; begin
try
oDest  := nil ; oBMove := nil ; Table1.Close ;
oDest := TTable.Create(nil) ; with oDest do begin DatabaseName := 'c:\delphi\files' ; TableName := 'Test.Txt' ; TableType := ttASCII ; end ; {Обратите внимание на то, что нет необходимости вызывать CreateTable}
oBMove := TBatchMove.Create(nil) ; with oBMove do begin Source := Table1 ; Destination := oDest ; Mode := batCopy ; Execute ; end ; finally if Assigned(oDest) then oDest.Free ; if Assigned(oBMove) then oBMove.Free ; end ; end;

{ Теперь, допустим, файл схемы существует; сам текстовый файл может как быть, так его может и не быть. С помощью файла схемы мы уже можем работать с полями }
procedure TForm1.Button2Click(Sender: TObject);
var
oTxt: TTable ; i: Integer ; f: System.Text ; begin
try
oTxt := nil ;
if not FileExists('c:\delphi\files\Test.Txt') then begin AssignFile(f, 'c:\delphi\files\Test.Txt' ) ; Rewrite(f) ; CloseFile(f) ; end ;
oTxt := TTable.Create(nil) ; with oTxt do begin DatabaseName := 'c:\delphi\files' ; TableName := 'Test.Txt' ; TableType := ttASCII ; Open ; end ;
with Table1 do begin DisableControls ; if not Active then Open ; First ; while not EOF do begin oTxt.Insert ; { В данном случае файл схемы описывает формат текстового файла; в этом примере фактически один к одному воспроизводятся поля таблицы в логическое определение полей в .sch-файле } for i := 0 to FieldCount - 1 do oTxt.Fields[i].AsString := Fields[i].AsString ; oTxt.Post ; Next ; end ; end ; finally Table1.EnableControls ; if Assigned(oTxt) then oTxt.Free ; end ;
end;

OAmiry/Borland [000551]



Blob-поля, потоки, компрессия...


Приветствую Вас, Валентин!

*** skip (личная переписка) ***.

В последней версии "Советов" я нашел рекомендацию по сохранению текста из TStrings в поле BLOB таблицы базы данных (однако на мой взгляд приведенный код делает обратное - т.е. считывает содержимое поля BLOB в TRichEdit). Я сам занимался этой задачей и мое предыдущее письмо к Вам явилось результатом экспериментов над TRichEdit. Поэтому я хочу предложить Вам пример проэкта, в котором я связываю поле BLOB таблицы Paradox с компонентом TRichEdit через потоки. Кроме того я использую библиотеку ZLib из стандартного приложения к Delphi 3 CSS. Это позволяет по ходу перекачивания данных в таблицу сжимать текст, а при чтении - распаковывать его чем достигается уменьшение размера .MB-файла, что полезно при большом количестве записей с BLOB-полем.

В заключение хочу сказать несколько слов о библиотеке ZLib.dcu (размер 48496 байт, дата создания 24.03.97г.) которая включена в поставку Delphi 3. При использовании конструктора TDecompressStream почему-то генерировался Default Beep и это очень задерживало выполнение декомпрессии. По счастью в поставку входит и исходный текст ZLib.pas. Я перекомпилировал модуль с помощью тестового примера, также входящего в поставку, при этом указав в настройках проэкта не включать отладочную информацию. В результате размер ZLib.dcu стал равным 45681 байт, а сигнал генерироваться перестал.

Теперь о проэкте. Он имеет одну форму frmMain. Содержимое файлов проэкта привожу ниже. Для работы также необходима таблица Table.db, имеющая структуру:

Имя поля Тип Размер ID + BLOBData B 64 и Alias с именем CBDB указывающий на каталог с этой таблицей.

Для упрощения размещения компонентов в форме проделайте следующее: Создайте новый проэкт; Скопируйте выделенную красным цветом часть файла Main.dfm в буфер обмена; Сделайте активной вновь созданную форму и вставте в нее содержимое буфера; Измените свойства самой формы в соответствии с нижеприведенным описанием. Файл Main.dfm:

    object frmMain: TfrmMain
Left = 476 Top = 347 BorderStyle = bsSingle Caption = 'Compressed BLOB' ClientHeight = 235 ClientWidth = 246 Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText Font.Height = -11 Font.Name = 'MS Sans Serif' Font.Style = [] Position = poScreenCenter OnShow = FormShow PixelsPerInch = 96 TextHeight = 13 object SB1: TSpeedButton Left = 1 Top = 209 Width = 25 Height = 25 Hint = 'Добавить' Glyph.Data = { 76010000424D7601000000000000760000002800000020000000100000000100 04000000000000010000130B0000130B00001000000000000000000000000000 800000800000008080008000000080008000808000007F7F7F00BFBFBF000000 FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333333333333 33333333333FFFFFFFFF333333000000000033333377777777773333330FFFFF FFF03333337F333333373333330FFFFFFFF03333337F3FF3FFF73333330F00F0 00F03333F37F773777373330330FFFFFFFF03337FF7F3F3FF3F73339030F0800 F0F033377F7F737737373339900FFFFFFFF03FF7777F3FF3FFF70999990F00F0 00007777777F7737777709999990FFF0FF0377777777FF37F3730999999908F0 F033777777777337F73309999990FFF0033377777777FFF77333099999000000 3333777777777777333333399033333333333337773333333333333903333333 3333333773333333333333303333333333333337333333333333} NumGlyphs = 2 ParentShowHint = False ShowHint = True OnClick = SB1Click end object SB2: TSpeedButton Left = 25 Top = 209 Width = 25 Height = 25 Hint = 'Удалить' Glyph.Data = { 76010000424D7601000000000000760000002800000020000000100000000100 0400000000000001000000000000000000001000000000000000000000000000 8000008000000080800080000000800080008080000080808000C0C0C0000000 FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333333333333 33333333333FFFFFFFFF333333000000000033333377777777773333330FFFFF FFF03333337F333333373333330FFFFFFFF03333337F3FF3FFF73333330F00F0 00F033333F7F773777373333300FFFFFFFF03333F73FFF3FF3F733330C0F0800 F0F0333F773F337737373330CC0FFFFFFFF033F777FFFFF3FFF7330CCCCC00F0 00003F777777F737777730CCCCCC0FF0FF03F7777777FF37F3730CCCCCCC08F0 F03377777777F337F73330CCCCCC0FF0033337777777FFF77333330CCCCC0000 333333777777777733333330CC3333333333333777333333333333330C333333 3333333377333333333333333033333333333333373333333333} NumGlyphs = 2 ParentShowHint = False ShowHint = True OnClick = SB2Click end object SB3: TSpeedButton Left = 49 Top = 209 Width = 25 Height = 25 Hint = 'Редактировать' Glyph.Data = { 76010000424D7601000000000000760000002800000020000000100000000100 04000000000000010000120B0000120B00001000000000000000000000000000 800000800000008080008000000080008000808000007F7F7F00BFBFBF000000 FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333333000000 000033333377777777773333330FFFFFFFF03FF3FF7FF33F3FF700300000FF0F 00F077F777773F737737E00BFBFB0FFFFFF07773333F7F3333F7E0BFBF000FFF F0F077F3337773F3F737E0FBFBFBF0F00FF077F3333FF7F77F37E0BFBF00000B 0FF077F3337777737337E0FBFBFBFBF0FFF077F33FFFFFF73337E0BF0000000F FFF077FF777777733FF7000BFB00B0FF00F07773FF77373377373330000B0FFF FFF03337777373333FF7333330B0FFFF00003333373733FF777733330B0FF00F 0FF03333737F37737F373330B00FFFFF0F033337F77F33337F733309030FFFFF 00333377737FFFFF773333303300000003333337337777777333} NumGlyphs = 2 ParentShowHint = False ShowHint = True OnClick = SB3Click end object SB4: TSpeedButton Left = 73 Top = 209 Width = 25 Height = 25 Hint = 'Отменить редактирование' Glyph.Data = { DE010000424DDE01000000000000760000002800000024000000120000000100 0400000000006801000000000000000000001000000000000000000000000000 80000080000000808000800000008000800080800000C0C0C000808080000000 FF0000FF000000FFFF00FF000000FF00FF00FFFF0000FFFFFF00333333333333 333333333333333333333333000033338833333333333333333F333333333333 0000333911833333983333333388F333333F3333000033391118333911833333 38F38F333F88F33300003339111183911118333338F338F3F8338F3300003333 911118111118333338F3338F833338F3000033333911111111833333338F3338 3333F8330000333333911111183333333338F333333F83330000333333311111 8333333333338F3333383333000033333339111183333333333338F333833333 00003333339111118333333333333833338F3333000033333911181118333333 33338333338F333300003333911183911183333333383338F338F33300003333 9118333911183333338F33838F338F33000033333913333391113333338FF833 38F338F300003333333333333919333333388333338FFF830000333333333333 3333333333333333333888330000333333333333333333333333333333333333 0000} NumGlyphs = 2 ParentShowHint = False ShowHint = True OnClick = SB4Click end object P1: TPanel Left = 0 Top = 0 Width = 246 Height = 206 BevelInner = bvRaised BevelOuter = bvLowered BevelWidth = 2 TabOrder = 0 object RE: TRichEdit Left = 5 Top = 5 Width = 236 Height = 196 ScrollBars = ssVertical TabOrder = 0 end end object DBN: TDBNavigator Left = 149 Top = 209 Width = 96 Height = 25 DataSource = DS VisibleButtons = [nbFirst, nbPrior, nbNext, nbLast] TabOrder = 1 end object T1: TTable Active = True DatabaseName = 'CBDB' TableName = 'table.db' Left = 5 Top = 5 object T1ID: TAutoIncField FieldName = 'ID' Visible = False end object T1BLOBData: TBlobField FieldName = 'BLOBData' Visible = False BlobType = ftBlob Size = 64 end end object OD: TOpenDialog DefaultExt = 'rtf' Filter = 'RTF-файлы|*.rtf|Все файлы|*.*' Title = 'Выберите файл' Left = 5 Top = 35 end object DS: TDataSource DataSet = T1 OnDataChange = DSDataChange Left = 35 Top = 5 end end

Файл Main.pas:

    unit Main;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Db, DBTables, StdCtrls, ComCtrls, ExtCtrls, DBCtrls, Buttons, swDBPanl, swRecPos; type
TfrmMain = class(TForm) T1: TTable; T1ID: TAutoIncField; T1BLOBData: TBlobField; OD: TOpenDialog; P1: TPanel; SB1: TSpeedButton; SB2: TSpeedButton; SB3: TSpeedButton; SB4: TSpeedButton; DS: TDataSource; DBN: TDBNavigator; procedure SB1Click(Sender: TObject); procedure SB2Click(Sender: TObject); procedure SB3Click(Sender: TObject); procedure SB4Click(Sender: TObject); procedure DSDataChange(Sender: TObject; Field: TField); procedure FormShow(Sender: TObject); private EF:boolean; procedure SetButtons; procedure UpdateEditor; procedure StoreFromFile; procedure StoreFromEditor; public { Public declarations } end;
var frmMain: TfrmMain;

implementation
uses
ZLib;

{$R *.DFM}

const LID:longint=0;
procedure TfrmMain.SetButtons;
var c1:boolean;
begin c1:=T1.RecordCount>0;
SB2.Enabled:=not EF and c1; SB3.Enabled:=not EF and c1; SB4.Enabled:=EF; end;

procedure TfrmMain.UpdateEditor;
var Buf:TStream;
ZStream:TCustomZLibStream; id:longint; begin
id:=T1ID.AsInteger;if (id=LID) and not EF then exit else LID:=id; Buf:=TMemoryStream.Create;T1BLOBData.SaveToStream(Buf); if Buf.Size>0 then begin ZStream:=TDecompressionStream.Create(Buf); RE.Lines.LoadFromStream(ZStream);ZStream.Free; end else RE.Lines.Clear; Buf.Free; end;

procedure TfrmMain.StoreFromFile;
var InFile,Buf:TStream;
ZStream:TCustomZLibStream; begin
if not
OD.Execute then exit; T1.AppendRecord([NULL]);InFile:=TFileStream.Create(OD.FileName,fmOpenRead); Buf:=TMemoryStream.Create;ZStream:=TCompressionStream.Create(clMax,Buf); ZStream.CopyFrom(InFile,0);ZStream.Free; T1.Edit;T1BLOBData.LoadFromStream(Buf);T1.Post; Buf.Free;InFile.Free; LID:=0;UpdateEditor; end;

procedure TfrmMain.StoreFromEditor;
var InStream,Buf:TStream;
ZStream:TCustomZLibStream; begin
InStream:=TMemoryStream.Create;Buf:=TMemoryStream.Create; RE.Lines.SaveToStream(InStream); ZStream:=TCompressionStream.Create(clMax,Buf);ZStream.CopyFrom(InStream,0);ZStream.Free; T1.Edit;T1BLOBData.LoadFromStream(Buf);T1.Post; UpdateEditor; end;

procedure TfrmMain.SB1Click(Sender: TObject);
begin
if
EF then begin StoreFromEditor;RE.ReadOnly:=true; DBN.Enabled:=true;EF:=false;SB1.Hint:='Добавить'; end else StoreFromFile; SetButtons; end;

procedure TfrmMain.SB2Click(Sender: TObject);
begin
if
MessageDlg('Удалять запись?',mtConfirmation,[mbYes,mbNo],0)=mrYes then begin T1.Delete;SetButtons;end; end;

procedure TfrmMain.SB3Click(Sender: TObject);
begin
DBN.Enabled:=false;EF:=true;SB1.Hint:='Внести изменения';RE.ReadOnly:=false;SetButtons; end;

procedure TfrmMain.SB4Click(Sender: TObject);
begin
UpdateEditor;DBN.Enabled:=true;EF:=false;SB1.Hint:='Добавить';RE.ReadOnly:=true; end;

procedure TfrmMain.DSDataChange(Sender: TObject; Field: TField);
begin
if
assigned(frmMain) and Visible and not EF then
begin
UpdateEditor;SetButtons;end; end;

procedure TfrmMain.FormShow(Sender: TObject);
begin
EF:=false;SetButtons;DSDataChange(nil,nil); end;

end.

Файл CompBLOB.dpr:

    program CompBLOB;
uses
Forms, Main in 'Main.pas' {frmMain};
{$R *.RES}

begin
Application.Initialize; Application.CreateForm(TfrmMain, frmMain); Application.Run; end.

С наилучшими пожеланиями
Сергей Лагонский. [000565]



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


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

    try Table1.Post; except MessageDlg ('Ошибка постинга записи', прочее... Table1.Cancel; end;

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



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


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

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

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

    Procedure1 .... begin
ExTable.Edit ; ExTable.FieldByName('...').AsString := ...; ...
end;

procedure2 ..... begin
ExTable.Edit; ..... end;

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



Данные индекса таблицы


Тема: Извлечение данных индекса таблицы

Получить во время выполнения приложения список индексов, ассоциированных с таблицей также просто, как вызвать метод GetIndexNames для компонентов TTable, TQuery или TStoredProc. Метод GetIndexNames возвращает список, доступный в наборе данных для компонента TStrignList, установленного на форме (для нашего примера), или других визуальных компонентов, таких как TListBox, через свойство Items:

    ListBox1.Clear; Table1.GetIndexNames(ListBox1.Items);

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

Но существует возможность получения дополнительной информации об индексах таблицы, а не только их имена. Другие описываемые атрибуты представляют собой имя каждого индекса, имена полей, включающие каждый индекс и опции индекса, используемые при их создании. Получение этих величин немного труднее, чем простое использование GetIndexNames. В основном, данные значения можно получить с помощью простой итерации свойства IndexDefs компонента TTable, TQuery или TStoredProc. Свойство IndexDefs по существу является массивом записей, по одной записи на каждый индекс таблицы.

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

Свойство IndexDefs компонента TTable содержит информацию об индексах таблицы, используемой компонентом TTable, TQuery или TStoredProc. Само свойство IndexDefs в свою очередь также имеет различные свойства, содержащие специфическую информацию об извлекаемых индексах. Объект IndexDefs содержит два свойства: Count: type Integer; доступно только во время выполнения программы и имеет флаг только для чтения; указывает на количество элементов в свойстве Items (например, количество индексов в таблице). Items: type TIndexDef; доступно только во время выполнения программы и имеет флаг только для чтения; массив объектов TIndexDef, каждый объект описывает один индекс таблицы.

Свойство Count объекта IndexDefs является основой для создания счетчика цикла, в теле которого с помощью элементов свойства Items извлекается информация о каждом индексе таблицы. Каждый объект IndexDef, содержащийся в свойстве Items, состоит из множества свойств, описывающих характеристики каждого индекса в отдельности. Все свойства объекта IndexDef доступны только во время выполнения программы и имеют флаг "только для чтения". Вот эти свойства: Expression: type String; содержит выражение, использующееся для dBASE multi-field индексов (индексов для нескольких полей). Fields: type String; содержит поле или несколько полей, на основе которых создан индекс. Name: type String; имя индекса. Options: type TIndexOptions; характеристики индекса (ixPrimary, ixUnique и др.). Перед получением любой информации об индексе (Count или Items) необходимо вызвать метод Update объекта IndexDefs. Это обновляет (или инициализирует) набор индексов для объектов IndexDef.

Примеры

Вот пример простого For-цикла, использующего в качестве счетчика свойство Count объекта IndexDefs, и извлекающего имя каждого индекса (если какой-либо существует) таблицы, представленной компонентом TTable с именем Table1:

    procedure TForm1.ListBtnClick(Sender: TObject); var i: Integer; begin ListBox1.Items.Clear; with Table1 do begin if IndexDefs.Count > 0 then begin for i := 0 to IndexDefs.Count - 1 do ListBox1.Items.Add(IndexDefs.Items[i].Name) end; end; end;

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

    procedure TForm1.FormShow(Sender: TObject); var i: Integer; S: String; begin with Table1 do begin Open; {Обновляем объект IndexDefs} IndexDefs.Update; if IndexDefs.Count > 0 then begin {Устанавливаем колонки и строки сетки для соответствия элементам IndexDefs} SG1.ColCount := 4; SG1.RowCount := IndexDefs.Count + 1; {Устанавливаем заголовки колонок сетки согласно именам свойств TIndexDef} SG1.Cells[0, 0] := 'Name'; SG1.ColWidths[0] := 200; SG1.Cells[1, 0] := 'Fields'; SG1.ColWidths[1] := 200; SG1.Cells[2, 0] := 'Expression'; SG1.ColWidths[2] := 200; SG1.Cells[3, 0] := 'Options'; SG1.ColWidths[3] := 300; {Цикл с опросом IndexDefs.Items} for i := 0 to IndexDefs.Count - 1 do begin {Заполняем ячейки сетки в текущей колонке} SG1.Cells[0, i + 1] := IndexDefs.Items[i].Name; SG1.Cells[1, i + 1] := IndexDefs.Items[i].Fields; SG1.Cells[2, i + 1] := IndexDefs.Items[i].Expression; if ixPrimary in IndexDefs.Items[i].Options then S := 'ixPrimary, '; if ixUnique in IndexDefs.Items[i].Options then S := S + 'ixUnique, '; if ixDescending in IndexDefs.Items[i].Options then S := S + 'ixDescending, '; if ixCaseInsensitive in IndexDefs.Items[i].Options then S := S + 'ixCaseInsensitive, '; if ixExpression in IndexDefs.Items[i].Options then S := S + 'ixExpression, '; if S > ' ' then begin {Отфильтровываем ", "} System.Delete(S, Length(S) - 1, 2); SG1.Cells[3, i + 1] := S; end; end; end; end; end;

Специфичные особенности

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

Таблицы dBASE

При работе с индексами dBASE, свойства Fields и Expression заполняются в зависимости от типа индекса, который может быть как простым (основанным на одном поле), так и сложным (базирующимся на нескольких полях или dBASE-выражениях). Если индекс является простым, свойство Fields будет содержать имя поля таблицы, на основе которого построен индекс, а свойство Expression будет пустым. Если индекс сложный, свойство Expression будет содержать выражение, на основе которого был построен индекс (например, "Field1+Field2"), а свойство Fields будет пустым.

Таблицы Paradox

При работе с первичными индексами Paradox, свойство Name будет пустым, свойство Fields будет содержать поле(я), на основе которых создан индекс, а свойство Options будет содержать ixPrimary. При работе со вторичными индексами, свойство Name будет содержать имя вторичного индекса, свойство Fields будет содержать поле(я), на основе которых создан индекс, а свойство Options может как содержать какое-либо значение, так и быть пустым.

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

Таблицы InterBase

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

Индексы, определенные в команде CREATE TABLE как PRIMARY, в свойстве Name будут содержать значение "RDB$PRIMARYn", где n - порядковый номер символа, однозначно определяющий первичный индекс в пределах метаданных БД. Вторичные индексы содержат фактическое имя индекса.

Внешние ключи также содержат индексы, созданные системой. Такие индексы будут содержаться в свойстве IndexDefs и иметь имя "RDB$FOREIGNn", где n - порядковый номер символа, однозначно определяющий индекс в пределах метаданных БД.

Свойство Fields у индексов, основанных более чем на одном поле, содержит имена полей, разделенных точкой с запятой. Индексы, основанные только на одном поле, в свойстве Fields содержат имя только данного поля. [000942]



Доступ к файлам Paradox через BDE в сети Lantastic Network


Я имею BDE, сеть Lantastic и работающие с ними Delphi-приложения. На одной машине проблем не было, зато другие с BDE работать не смогли.

Возможные варианты: Первая машина имела BDE от фактической Database Engine, а не из Delphi. В секции [386Enh] файла system.ini была строка

network=lantasti.386

вместо правильной

network=*vnetbios,lantasti.386

РЕШЕНИЕ ПРОБЛЕМЫ LANTASTIC 6.0 + BDE

===========================================

Проблема: Доступ к файлам Paradox через BDE в сети Lantastic Network.

Должно быть проблема в файле LPICALLW.DLL. Когда я его удалил, функциональность сети восстановились!!! Чтобы найти файл, загрузитесь без autoexec.bat. Я нашел его в директории lantasti. Если вы не установили к нему путь типа path=c:\dos, то при загрузке LPICALLW.DLL найден не будет... После этого система выведет диалоговое окно с сообщением о неудачной попытке найти файл LPICALLW.DLL. Нажмите OK и вы сможете, используя Borland DELPHI, PARADOX или D-BASE, иметь доступ к файлам Paradox !!!!

LANTNET.DRV загружает LPICALLW.DLL.
LPICALLW.DLL делает массу вызовов. Похоже на то, что это оболочка для всех сетевых вызовов. Может я и ошибаюсь насчет этого файла и дурит все-же BDE. Я думаю что это BDE.

Когда вы удаляете LPICALLW.DLL, вы не можете загрузить WNET.EXE в Windows. Но при это вы имеете доступ к файлам Paradox.

========================================

У меня была эта проблема, и я думаю что нашел причину.
Я работал под Lantastic и при инициализации базы данных имел ошибку сети.
Я выключил 32-битный доступ к файлам, после чего все заработало. На всю работу у меня ушло 3 часа. [000418]



Если в транзакции изменена какая-то


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 ставится только в том случае, если по пpедикату нет индекса. Так, можно пpосто пpоиндексиpовать таблицу orders по полю customer_id, и не забывать делать UPDATE STATISTIC, хотя будет работать и с PAGLOCK. Просто не факт, что UPDATE всегда делает табличную блокировку. [001299]



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


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

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



Hе получается открыть таблицу


Nomadic отвечает:

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



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


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

В модуле 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 [001007]



Имя пользователя Paradox


Вы можете выполнить эту задачу, непосредственно обращаясь к BDE. Включите следующие модули в сецию Uses вашего модуля: DBIPROCS, DBIERRS, DBITYPES

Ниже приведена функция с именем ID, возвращающая сетевое имя входа:

    function ID: String ;
var
rslt: DBIResult ; szErrMsg: DBIMSG ; pszUserName: PChar ; begin
try
Result := '' ; pszUserName := nil ; GetMem(pszUserName, SizeOf(Char) * DBIMAXXBUSERNAMELEN) ; rslt := DbiGetNetUserName(pszUserName); if rslt = DBIERR_NONE then Result := StrPas(pszUserName) else begin DbiGetErrorString(rslt, szErrMsg); raise Exception.Create(StrPas(szErrMsg)); end ; FreeMem(pszUserName, SizeOf(Char) * DBIMAXXBUSERNAMELEN) ; pszUserName := nil ; except on E: EOutOfMemory do ShowMessage('Ошибка. ' + E.Message); on E: Exception do ShowMessage(E.Message); end ; if pszUserName <> nil then FreeMem(pszUserName, SizeOf(Char) * DBIMAXXBUSERNAMELEN) ; end ;

[000420]



Импорт CSV ASCII


...вот то, что вам нужно. Я использую этот компонент для работы с разделителем при импорте ASCII.

Dave

    unit Cdbascii;

interface

uses

SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, DbiErrs, DbiTypes, DbiProcs, DB, DBTables;
type
TAsciiDelimTable = class(TTable) private { Private declarations } fQuote :Char; fDelim :Char; protected { Protected declarations } function CreateHandle: HDBICur; override; Procedure SetQuote(newValue :Char); Procedure SetDelim(newValue :Char); public { Public declarations } constructor Create(AOwner: TComponent); override; destructor Destroy; override; { Эти свойства не должны больше публиковаться } property IndexFieldNames; property IndexName; property MasterFields; property MasterSource; property UpdateMode; published { Published declarations } Property Quote:Char Read fQuote Write setQuote Default '"'; Property Delim:Char Read fDelim Write setDelim Default ','; end;
procedure Register;

implementation

uses
DBConsts;

procedure Register;
begin
RegisterComponents('Data Access', [TAsciiDelimTable]); end;

constructor TAsciiDelimTable.Create(AOwner: TComponent);
Begin
Inherited
Create(AOwner); Exclusive:=True; TableType:=ttASCII; fQuote := '"'; fDelim := ','; End;

destructor TAsciiDelimTable.Destroy;
Begin
Inherited
Destroy; End;

{ Рабочий код }

function CheckOpen(Status: DBIResult): Boolean;
begin
case
Status of DBIERR_NONE: Result := True; DBIERR_NOTSUFFTABLERIGHTS: begin if not Session.GetPassword then DbiError(Status); Result := False; end; else DbiError(Status); end; end;

function TAsciiDelimTable.CreateHandle: HDBICur;
const
OpenModes: array[Boolean] of DbiOpenMode = (dbiReadWrite, dbiReadOnly); ShareModes: array[Boolean] of DbiShareMode = (dbiOpenShared, dbiOpenExcl); var
STableName: array[0..SizeOf(TFileName) - 1] of Char; SDriverType: array[0..12] of Char; begin
if TableName = '' then DBError(SNoTableName); AnsiToNative(DBLocale, TableName, STableName, SizeOf(STableName) - 1); StrPCopy(SDriverType, 'ASCIIDRV-'+Quote+'-'+Delim); Result := nil; while not CheckOpen(DbiOpenTable(DBHandle, STableName, SDriverType, nil, nil, 0, OpenModes[ReadOnly], ShareModes[Exclusive], xltField, False, nil, Result)) do {Повтор}; end;

Procedure TAsciiDelimTable.SetQuote(newValue :Char);
Begin
If
Active Then {    DBError(SInvalidBatchMove); }; fQuote := newValue; End;
Procedure TAsciiDelimTable.SetDelim(newValue :Char);
Begin
If
Active Then {    DBError(SInvalidBatchMove); }; fDelim := newValue; End;

end.

[000419]



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


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

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

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

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

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



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


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

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

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

    unit form2;
interface {...} implementation uses form1; {...}

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

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

    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 и компоненты для работы с базами данных. [001306]



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


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

    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. [001281]



Изображения и InterBase Blob-поля


dBASE и Paradox таблицы имеют в своем арсенале BLOB-поля, позволяющие хранить бинарные данные, в том числе bitmap-формат, отображаемый с помощью компонента TDBImage. В Database Desktop данный тип полей указан как Binary и Graphic (для dBASE и Paradox таблиц, соответственно). Тем не менее, процесс сохранения изображений в InterBase BLOB-полях и их использование в компонентах TDBImage не такой уж простой.

Таблицы InterBase не имеют простого типа BLOB-поля. Есть три варианта, или подтипа: тип 0, тип 1 и подтип, определенный пользователем. Типы 0 и 1 - "встроенные" типы. Тип 0 - BLOB-поля (тип по умолчанию) для хранения общих бинарных данных. Тип 1 - BLOB-поля для хранения текстовых BLOB-данных. Ни один из предопределенных типов не допускает автоматического извлечения данных изображения из BLOB-поля для его последующего отображения в компоненте TDBImage. BLOB-поля типа 0 могут использоваться для хранения данных bitmap-формата, но данные должны извлекаться и передаваться в объект типа TBitmap программным путем. Вот пример ручного извлечения данных изображения, хранящихся в BLOB-поле типа 0 (Table1BLOBField), и его показ в компоненте TImage (не предназначенным для работы с БД) :

    procedure TForm1.ExtractBtnClick(Sender: TObject); begin Image1.Picture.Bitmap.Assign(Table1BLOBField); end;

Естественно, поскольку это должно делаться вручную, данный процесс менее желателен в приложении, нежели автоматическое отображение данных изображения в комбинации BDE и компонента TDBImage. Здесь происходит определение подтипа определенного пользователем BLOB-поля. При работе с данными подтип BLOB-поля учитывается, т.к. сохраненные первыми данные устанавливают тип данных для этого поля для всей таблицы целиком. Таким образом, если данные bitmap-формата оказывается первым загружаемым типом, то данный формат будет единственно возможным для данного поля. До сих пор по умолчанию тип бинарного BLOB-поля (предопределенный тип 0) позволял BDE читать и отображать данные в компоненте TDBImage без особых проблем.

Утилиты Database Desktop допускают создание бинарных BLOB-полей только типа 0 и не имеют возможности самим определять подтипы BLOB-полей. Из-за такого ограничения таблицы, подразумевающие хранение и вывод изображений, должны создаваться с помощью SQL-запросов. Обычно это делается посредством утилиты WISQL, но вполне достаточно выполнение SQL-запроса с помощью компонента TQuery. Ниже приведен SQL-запрос, создающий таблицу с определенным пользователем подтипом BLOB-поля:

    CREATE TABLE WITHBMP ( FILENAME CHAR(12), BITMAP   BLOB SUB_TYPE -1 )

После создания таблицы с совместимыми BLOB-полями, для хранения данных изображения в BLOB-поле и его вывода в компоненте TDBImage используются те же самые методы, что и при работе с таблицами dBASE и Paradox.

Имеется множество способов загрузки изображений в BLOB-поле. Три самых простых метода включают в себя: копирование данных из буфера обмена Windows в компонент TDBImage, связанный с BLOB-полем использование метода LoadFromFile компонента TBLOBField использование метода Assign для копирования объекта типа TBitmap в значение свойства Picture компонента TBDBImage. Первый способ, когда происходит копирование изображения из буфера обмена, вероятно, наиболее удобен в случае, когда необходимо добавить изображение в таблицу при использовании приложения конечным пользователем. В этом случае компонент TDBImage используется в роли интерфейса между BLOB-полем таблицы и изображением, хранящимся в буфере обмена. Метод PasteFromClipboard компонента TDBImage как раз и занимается тем, что копирует изображение из буфера обмена в TDBImage. При сохранении записи изображение записывается в BLOB-поле таблицы.

Поскольку буфер обмена Windows может содержать данные различных форматов, то желательно перед вызовом метода CopyFromClipboard осуществлять проверку формата хранящихся в нем данных. Для этого необходимо создать объект TClipboard и использовать его метод HasFormat, позволяющий определить формат хранящихся в буфере данных. Имейте в виду, что для создания объекта TClipboard вам необходимо добавить модуль Clipbrd в секцию uses того модуля, в котором будет создаваться экземпляр объекта.

Вот исходный код примера, копирующий содержание буфера обмена в компонент TDBImage, если содержащиеся в буфере данные имеют формат изображения:

    procedure TForm1.Button1Click(Sender: TObject); var C: TClipboard; begin C := TClipboard.Create; try if Clipboard.HasFormat(CF_BITMAP) then DBImage1.PasteFromClipboard else ShowMessage('Буфер обмена не содержит изображения!'); finally C.Free; end; end;

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

Этот способ использует метод LoadFromFile компонента TBLOBField, который применяется в Delphi для работы с dBASE-таблицами и двоичными Windows полями или таблицами Paradox и графическими Windows полями; в обоих случаях с помощью данного метода возможно загрузить изображение и сохранить его в таблице.

Методу LoadFromFile компонента TBLOBField необходим единственный параметр типа String: имя загружаемого файла с изображением. Значение данного параметра может быть получено при выборе файла пользователем с помощью компонента TOpenDialog и его свойства FileName.

Вот пример, демонстрирующий работу метода LoadFromFile компонента TBLOBField с именем Table1Bitmap (поле с именем Bitmap связано с таблицей TTable, имеющей имя Table1):

    procedure TForm1.Button2Clicck(Sender: TObject); begin Table1Bitmap.LoadFromFile( 'c:\delphi\images\splash\16color\construc.bmp'); end;

Третий способ для копирования содержимого объекта типа TBitmap в свойство Picture компонента TDBImage использует метод Assign. Объект типа TBitmap может быть как свойством Bitmap свойства-объекта Picture компонента TImage, так и отдельного объекта TBitmap. Как и в методе, копирующем данные из буфера обмена в компонент TDBImage, данные изображения компонента TDBImage сохраняются в BLOB-поле после успешного сохранения записи.

Ниже приведен пример, использующий метод Assign. В нашем случае используется отдельный объект TBitmap. Для помещения изображения в компонент TBitmap был вызван его метод LoadFromFile.

    procedure TForm1.Button3Click(Sender: TObject); var B: TBitmap; begin B := TBitmap.Create; try B.LoadFromFile('c:\delphi\images\splashh\16color\athena.bmp'); DBImage1.Picture.Assign(B); finally B.Free; end; end;

[000593]



Извлечение изображения из BLOB-поля


Тема: Извлечение изображения из BLOB-поля

Извлечение изображения из BLOB-поля таблицы dBASE или Paradox -- без первой записи изображения в файл -- простейший процесс использования метода Assign для сохранения содержимого BLOB-поля в объекте, имеющим тип TBitmap. Отдельный объект TBitmap или свойство Bitmap объекта Picture, в свою очередь являющегося свойством компонента TIMage, могут служить примером совместимой цели для данной операции.

Вот пример кода, демонстрирующего использование метода Assign для копирования изображения из BLOB-поля в компонент TImage.

    procedure TForm1.Button1Click(Sender: TObject); begin Image1.Picture.Bitmap.Assign(Table1Bitmap); end;

В данном примере, объект Table1Bitmap типа TBLOBField - BLOB-поле таблицы dBASE. Данный TBLOBField-объекты был создан с помощью редактора полей (Fields Editor). Если редактор полей для создания TFields для полей таблицы не используется, получить доступ к полям можно с помощью метода FieldByName или свойства Fields, оба они являются членами компонентов TTable или TQuery. В случае ссылки на BLOB-поле таблицы с помощью одного из приведенных членов, перед использованием метода Assign указатель на поле должен быть прежде приведен к типу объекта TBLOBField. Для примера:

    procedure TForm1.Button1Click(Sender: TObject); begin Image1.Picture.Bitmap.Assign(TBLOBField(Table1.Fields[1])); end;

Изображение, хранящееся в BLOB-поле, может быть скопировано непосредственно в отдельный TBitmap объект. Ниже приведен пример, демонстрирующий создание объекта TBitmap и сохранения в нем изображения из BLOB-поля.

    procedure TForm1.Button2Click(Sender: TObject); var B: TBitmap; begin B := TBitmap.Create; try B.Assign(Table1Bitmap); Image1.Picture.Bitmap.Assign(B); finally B.Free; end; end;

[000940]



Извлечение текста из TMemoField


    var P: PChar; S: TMemoryStream; Size: LongInt; begin S := TMemoryStream.Create; MyMemoField.SaveToStream(S); Size := S.Position; GetMem(P, Size + 1); S.Position := 0; S.Read(P^, Size); P[Size] := #0; S.Free; { используем текст в PChar } FreeMem(P, Size + 1); end;

-Steve Schafer [000693]



Я включил dbclient.dll в секцию


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 [001473]



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


Nomadic отвечает:

Вот так:

    RemoteServer.AppServer.MyMethod

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



Как хранить изображения в базе, читать их и модифицировать?


Konstantin Chumak отвечает:

Народ часто спрашивает как хранить изображения в базе, читать их и модифицировать.

Данный пример работает хорошо для базы на ACCESS.

Изображение должно храниться в поле типа OLEOBJECT //CREATE TABLE photodb (ID LONG, Name STRING, Dib OLEOBJECT); // Load images from Database void LoadImageFromDB(long &id, String &Name, Graphics::TBitmap *image) { String table = "soil"; String filedPic = "Dib"; // TADOQuery *qr// String SQLcommand = "SELECT * FROM " + table + ";"; qr->Close(); qr->SQL->Clear(); qr->SQL->Add(SQLcommand); qr->Open(); qr->First(); while( !qr->Eof ) { id = qr->FieldByName("ID")->AsInteger; Name = qr->FieldByName("Name")->AsString; Image->Assign(qr->FieldByName(filedPic)); break; qr->Next(); } qr->Close(); } // заменить изображение void UpdateImageInDB(long id, String Name, Graphics::TBitmap *image) { String table = "soil"; String filedPic = "Dib"; qr->Close(); qr->SQL->Clear(); qr->SQL->Add("UPDATE "+table+" SET "+filedPic+"=:img WHERE ID = "+id+";"); TParameter *param = qr->Parameters->Items[0]; // img - parameter param->DataType = ftGraphic; param->Assign(image); qr->ExecSQL(); qr->Close(); } [001939]



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


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

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

или

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

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

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

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



Как я могу выбрать на клиента


Nomadic отвечает:

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

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



Как можно использовать TClientDataSet


Nomadic отвечает:

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

    { CDS = TClientDataSet }
{ Table1 = TTable }
CDS.Provider := Table1.Provider;
CDS.Open;

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



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


Nomadic отвечает:

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

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



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


Nomadic отвечает:

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



Как записать в 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;

[001359]



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


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

    var
pDatabase: DBDrsc: begin

{ pAlias - PChar, содержащий имя псевдонима} dbiGetDatabaseDesc ( pAlias, @pDatabase ) ;

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



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


    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;

[001295]



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


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

    unit myModalF;

interface
{...}

implementation
{...}

uses
MainForm; {Имя файла родительской формы для вашей модальной формы}
MyModalForm.OnCreate(Sender: TObject);
begin
DBGrid1.DataSource := MyMainForm.DataSource1; end;

[001337]



Общий доступ к данным с DBD & Pdox


Я обнаружил, что открытый Data Desktop, даже без открытых объектов, может создавать "помехи". Также мудрит открытая сессия Delphi, если вы запускаете собранное приложение вне IDE. Я допускаю, что если вы закрываете все объекты под DD, конфигурация BDE быстро себя не очистит. Наверное мне следовало бы запустить мое приложение пару недель спустя, но я реально предполагаю, что множество сеансов Paradox блокируют бедный BDE.

Пожалуйста запустите утилиту BDE Config, поставляемую с Delphi и с PdoxWin. Перейдите на страницу SYSTEM и проверьте, установлен ли параметр LOCAL SHARE. Если вы хотите, что несколько приложений имели доступ к данным Paradox, LOCAL SHARE должен быть установлен в TRUE (и должны быть загружены SHARE или VCACHE).

При этом вы должны принимать во внимание, что если вы хотите, чтобы "LOCAL SHARE = TRUE" все время работал правильно и надежно, вы должны также определить уникальные индивидуальные каталоги для всех приложений, использующих BDE для доступа к таблицам Paradox. В противном случае, "поведение по умолчанию" заставит BDE использовать текущий рабочий каталог как частный. И, как вы все догадываетесь, Pdox блокирует данный каталог, считая его "частной собственностью" и не допуская к табличкам остальных соперников . . . то-то, а вы говорите "блокируют бедный BDE".

Mark Ostroff [000737]



Очень медленный доступ к таблице при первом обращении


Данная проблема возникает из-за того, что BDE вначале запрашивает базу данных для получения информации о таблице, прежде чем он начнет с ней работать. Как только появляется информация о таблице, она кэшируется и обращение к таблице во время всего сеанса (пока TDatabase.Connection имеет значение True) происходит практически мгновенно. Для того, чтобы использовать кэшируемую информацию и при последующем запуске приложения, в конфигурации BDE найдите необходимый псевдоним и установите BDE CACHE = TRUE и BDE CACHE DIR = 'C:\temp' или любой другой удобный каталог.

ПРИМЕЧАНИЕ: При любом изменении структуры таблицы Вам придется удалять кэш вручную. Имя файла, в котором хранится кэш, Вы можете узнать, посмотрев в любом текстовом редакторе файл SCache.INI. [000028]



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


В моем проекте имеется подобная функция, определяющая количество элементов: В обработчике события 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;

[001264]



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


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

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

или

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

[001265]



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


Предположим, что пользователь изменил строковое поле в 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)). [001456]



Почему при добавлении/изменении


Nomadic отвечает:

A: Во-первых, должно быть RequestLive := True; во-вторых, чтобы запрос был редактируемым, он должен удовлетворять требованиям, изложенным в помощи при поиске по "live result sets". [001569]



Подскажите как правильно показать на эк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;

[001303]



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


Тема: Получение физического пути к таблице
Отправлено: Август 13, 1996
Автор: Xavier Pacheco

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

    pDBDesc = ^DBDesc; DBDesc = packed record          { Описание данной базы данных } 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;

[001027]



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


Вам нужно воспользоваться свойством 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)); 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. [001283]



Получение Memo с помощью Query


Получение Memo-значения в область Edit без использования Memo-поля.

Разместите на вашей форме объект Query (Query1) Разместите на вашей форме объект Edit (Edit1) Разместите на вашей форме объект Button (Button1) Дважды щелкните на Query и добавьте поле Memo. (Biolife.db использует текстовое поле) Установите свойство Query1 SQL следующим образом: Select * from Biolife Установите свойство Query1 Active в True Добавьте следующий код в обработчик события кнопки Button1 OnClick:

    procedure TForm1.Button1Click(Sender: TObject); var bs : TBlobStream; p  : array [0..50] of char; begin FillChar(p, SizeOf(p), #0); bs:= TBlobStream.Create(Query1Notes, bmRead); try bs.Read(p,50); finally bs.Free; end; Edit1.Text:=StrPas(p); end;

[000939]



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


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

    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;

[001325]



Советы по Delphi

Использование таблиц Access I
Использование таблиц Access II








Использование таблиц Access I


Может кто-нибудь, предпочтительно из персонала Borland, ПОЖАЛУЙСТА, дать мне ПОЛНЫЙ рассказ о том, как с помощью Delphi и сопутствующего программного обеспечения получить доступ и работать с базами данных MS Access. Среди прочего, мне необходимо узнать...

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

Драйвер ODBC, предусмотренный для доступа к Access 2.0, разработан только для работы в пределах среды Microsoft Office. Для работы со связкой ODBC/Access в Delphi, вам необходим Microsoft ODBC Desktop Driver kit, part# 273-054-030, доступный через Microsoft Direct за $10.25US (если вы живете не в США, воспользуйтесь службой WINEXT). Он также доступен в январском выпуске MSDN, Level 2 (Development Platform) CD4 \ODBC\X86 как часть ODBC 2.1 SDK. Имейте в виду, что смена драйверов (в частности Desktop Drivers) может негативно сказаться на работе других приложений Microsoft. Для информации (и замечаний) обращайтесь в форум WINEXT.

Также вам необходимы следующие файлы ODBC:

Минимум: ODBC.DLL 03.10.1994, Версия 2.00.1510 ODBCINST.DLL 03.10.1994, Версия 2.00.1510 ODBCINST.HLP 11.08.1993 ODBCADM.EXE 11.08.1993, Версия 1.02.3129 Рекомендуется: ODBC.DLL 12.07.1994, Версия 2.10.2401 ODBCINST.DLL 12.07.1994, Версия 2.10.2401 ODBCINST.HLP 12.07.1994 ODBCADM.EXE 12.07.1994, Версия 2.10.2309 Нижеследующие шаги приведут вас к искомой цели:

Используя администратора ODBC, установите источник данных (datasource) для вашей базы данных. Не забудьте задать путь к вашему mdb-файлу. Для нашего примера создайте источник с именем MYDSN. Загрузите утилиту BDE Configuration. Выберите пункт "New Driver". Назначьте драйверу имя (в нашем случае ODBC_MYDSN). В выпадающем списке драйверов выберите "Microsoft Access Driver (*.mdb) В выпадающем списке имен выберите MYDSN Перейдите на страницу "Alias" (псевдонимы). Выберите "New Alias" (новый псевдоним). Введите MYDSN в поле имени. Для Alias Type (тип псевдонима) выберите ODBC_MYDSN. На форме Delphi разместите компоненты DataSource, Table, и DBGrid. Установите DBGrid1.DataSource на DataSource1. Установите DataSource1.DataSet на Table1. Установите Table1.DatabaseName на MYDSN. В свойстве TableName компонента Table1 щелкните на стрелочку "вниз" и вы увидите диалог "Login". Нажмите OK и после короткой паузы вы увидите список всех имен ваших таблиц. Выберите одно. Установите свойство Active Table1 в True и данные вашей таблицы появятся в табличной сетке. [000376]



Использование таблиц Access II


Можно ли как-то в Delphi работать с файлами Microsoft Access? Я слышал что некоторые программисты пробовали, но у них ничего не получилось.

Из приложений Delphi вы можете получить доступ к .MDB-файлам Microsoft Access, используя драйверы ODBC. Delphi действительно может дать все необходимое, но некоторые вещи не столь очевидные. Вот шаги для достижения вашей цели.

Что вам нужно: Первое: проверьте, установлен ли ODBC Administrator (файл ODBCADM.EXE в WINDOWS\SYSTEM, вам также необходим файл DBCINST.DLL для установки новых драйверов и ODBC.DLL). Администратор ODBC должен присутствовать в Панели Управления в виде иконки ODBC. Если у вас его не было, то после установки Delphi он должен появиться. Если вы получаете сообщение типа "Your ODBC is not up-to-date IDAPI needs ODBC greater then 2.0", у вас имеется старая версия администратора и вы должны обновить ее до версии, включенной в поставку Delphi. Проверьте, имеете ли вы доступ к драйверу Access ODBC, установленному в Windows. Вы можете сделать это, щелкнув на "Drivers" в диалоговом окне "Data Sources", появляющемся при запуске ODBC Administrator. Delphi должна в диалоге добавить пункты Access Files (*.mdb) и Access Data (*.mdb), работающие с файлами Access 1.10 и использующие драйвер SIMBA.DLL (имейте в виду, что для данного DLL необходимы также файлы RED110.DLL и SIMADMIN.DLL, устанавливаемые для вас Delphi). Данные файлы должны поставляться с дистрибутивом вашей программы как часть ReportSmith Runtime библиотеки. Если вы хотите работать с файлами Access 2.0 или 2.5, вам необходимо иметь другой набор драйверов от Microsoft. Ключевой файл - MSAJT200.DLL, также необходимы файлы MSJETERR.DLL и MSJETINT.DLL. В США набор ODBC Desktop Drivers, Version 2.0. стоит $10.25. Он также доступен в январском выпуске MSDN, Level 2 (Development Platform) CD4 \ODBC\X86 как часть ODBC 2.1 SDK. Очевидно есть обновление этих драйверов для файлов Access 2.5 на форуме MSACCESS CompuServe. Имейте в виду, что драйвер Access ODBC, поставляемый с некоторыми приложениями Microsoft (например, MS Office) могут использоваться только другими MS-приложениями. К сожалению, они могут сыграть с вами злую шутку: сначала заработать, а потом отказать в совершенно неподходящий момент! Поэтому не обращайте внимания (запретите себе обращать внимание!) на строчку "Access 2.0 for MS Office (*.mdb)" в списке драйверов ODBC Administrator. Вы можете установить новые ODBC драйверы с помощью ODBC Administrator в Панели Управления.

Добавление источника данных ODBC (Data Source): если у вас имеются все необходимые файлы, можете начинать. Представленный здесь пример использует драйвер Access 1.10, обеспечиваемый Delphi. Используя ODBC Administrator, установите источник данных для ваших файлов Access: щелчок на кнопке "Add" в окне "data sources" выведет диалог "Add Data Source", выберите Access Files (*.mdb) (или что-либо подходящее, в зависимости от установленных драйверов). В диалоге "ODBC Microsoft Access Setup" необходимо ввести имя в поле "Data Source Name". В данном примере мы используем "My Test". Введите описание "Data Source" в поле Description. Щелкните на "Select Database" для открытия диалога "Select Database". Перейдите в директорию, где хранятся ваши Access .MDB-файлы и выберите один. Мы выберем файл TEST.MDB в директории C:\DELPROJ\ACCESS. Нажмите OK в диалоге "Setup". Теперь в списке источников данных (Data Sources) должен появиться "My Test" (Access Files *.mdb). Нажмите Close для выхода из ODBC Administrator. Используя этот метод, вы можете установить и другие, необходимые вам, источники данных.

Настройка Borland Database Engine: загрузите теперь Borland Database Engine (BDE) Configuration Utility. На странице "Drivers" щелкните на кнопке New ODBC Driver. Имейте в виду, что это добавит драйвер Access в BDE и полностью отдельное управление дополнительно к драйверам Access в Windows, устанавливаемым при помощи ODBC Administrator. В открывшемся диалоге Add ODBC Driver в верхнем поле редактировании введите ACCESS (или что-то типа этого). BDE автоматически добавит на первое место ODBC_. В combobox, расположенном немного ниже, выберите Access Files (*.mdb). Выберите Data Source в следующем combobox (Default Data Source Name), это должен быть источник данных, который вы установили с помощью ODBC Administration Utility. Здесь можно не беспокоиться о вашем выборе, поскольку позднее это можно изменить (позже вы узнаете как это можно сделать). Нажмите OK. После установки драйвера BDE, вы можете использовать его более чем с одним источником данных ODBC, применяя различные псевдонимы (Alias) для каждого ODBC Data Source. Для установки псевдонима переключитесь на страницу "Aliases" и нажмите на кнопку "New Alias". В диалоговом окне "Add New Alias" введите необходимое имя псевдонима в поле "Alias Name". В нашем примере мы используем MY_TEST (не забывайте, что пробелы в псевдониме недопустимы). В combobox Alias Type выберите имя ODBC-драйвера, который вы только что создали (в нашем случае ODBC_ACCESS). Нажмите OK. Если вы имеете более одного ODBC Data Source, измените параметр ODBC DSN ("DSN" = "Data Source Name") в списке "Parameters" псевдонима на подходящий источник данных ODBC Data Source, как установлено в ODBC Administrator. Имейте в виду, что вы не должны ничего добавлять в параметр Path (путь), так как ODBC Data Source уже имеет эту информацию. Если вы добавляете параметр Path, убедитесь, что путь правильный, в противном случае ничего работать не будет! Теперь сохраните конфигурацию BDE, выбирая пункты меню File|Save, и выходите из Database Engine Configuration Utility.

В Delphi: Создайте новый проект и расположите на форме компоненты Table и DataSource из вкладки Data Access палитры компонентов. Затем из вкладки Data Controls выберите компонент DBGrid и также расположите его на форме. В Table, в Инспекторе Объектов, назначьте свойству DatabaseName псевдоним MY_TEST, установленный нами в BDE Configuration Utility. Теперь спуститесь ниже и раскройте список TableName. Вас попросят зарегистрироваться в базе данных Access MY_TEST. Обратите внимание, что если бюджет не установлен, то User Name и Password можно не заполнять, просто нажмите на кнопку OK. После некоторой паузы раскроется список, содержащий доступные таблицы для ODBC Data Source указанного псевдонима BDE. Выберите TEST. В DataSource, в Инспекторе Объектов, назначьте свойству DataSet таблицу Table1. В DBGrid, также в Инспекторе Объектов, назначьте свойству DataSource значение DataSource1. Возвратитесь к таблице, и в том же Инспекторе Объектов установите свойство Active в True. Данные из таблицы TEST отобразятся в табличной сетке. Это все! Одну вещь все-таки стоит упомянуть: если вы создаете приложение, использующее таблицы Access и запускаете его из-под Delphi IDE, то при попытке изменения данных в таблице(ах) вы получите ошибку. Если же вы запустите скомпилированный .EXE-файл вне Delphi (предварительно Delphi закрыв), то все будет ОК. Сообщения об ошибках ODBC, к несчастью, очень туманные и бывает достаточно трудно понять его источник в вашем приложении, в этом случае проверьте установку ODBC Administrator и BDE Configuration Utility, они также могут помочь понять источник ошибки. Для получения дополнительной информации обратитесь к ODBC 2.0 Programmer's Reference или SDK Guide от Microsoft Press (ISBN 1-55615-658-8, цена в США составляет $24.95). В этом документе вы получите исчерпывающую информацию о возможных ошибках при использовании Access-файлов посредством ODBC. Также здесь вы можете найти рапорты пользователей о найденных ошибках, в том числе и при использовании Delphi. Более того, я выяснил, что большинство описанных проблем возникает при неправильных настройках ODBC, т.е. те шаги, которые я описал выше. Надеюсь, что с развитием технологии доступа к базам данных такие сложности уйдут в прошлое. Кроме того, имейте в виду, что если вам необходимо создать новую таблицу Access 1.10, вы можете воспользоваться Database Desktop, включаемый в поставку Delphi.

Авторы данной технологии Ralph Friedman (CompuServe 100064,3102), Bob Swart и Chris Frizelle. [000514]



Удаление и восстановление индексов


Slava Kostin пишет:

Часто приходится удалять индексы из таблицы для ускорения процесса закачки туда данных, потом приходится переиндексировать. Данный класс TDBWork призван облегчить этот процесс. procedure DropIndexes; - удаляет все индексы таблицы, предварительно сохраняя их в файле <имя таблицы>.set в каталоге программы procedure CreateIndexes; - создает все индексы, читая информацию о них из файла <имя таблицы>.set. После успешного завершения операции данный файл удаляется. Пример использования:

    procedure TForm1.Button1Click(Sender: TObject);
var i: TDBWork;
begin
i:=TDBWork.Create; i.OpenTable(DM.Table1); i.DropIndexes; i.Free; end;

procedure TForm1.Button2Click(Sender: TObject);
var i: TDBWork;
begin
i:=TDBWork.Create; i.OpenTable(DM.Table1); i.CreateIndexes; i.Free; end;

    (*
Часто приходится удалять индексы из таблицы для ускорения процесса
закачки туда данных, потом приходится переиндексировать.
Данный класс TDBWork призван облегчить этот процесс.

procedure DropIndexes; - удаляет все индексы таблицы, предварительно
сохраняя их в файле <имя таблицы>.set в каталоге программы procedure CreateIndexes; - создает все индексы, читая информацию о них
из файла <имя таблицы>.set. После успешного завершения операции данный файл удаляется.
Пример использования:

procedure TForm1.Button1Click(Sender: TObject);
var i: TDBWork;
begin
i:=TDBWork.Create; i.OpenTable(DM.Table1); i.DropIndexes; i.Free; end;

procedure TForm1.Button2Click(Sender: TObject);
var i: TDBWork;
begin
i:=TDBWork.Create; i.OpenTable(DM.Table1); i.CreateIndexes; i.Free; end;

*)

unit DBWork;

interface
uses Forms,Sysutils,Db,dbtables,inifiles;

type
TIndexInfo=record Name: String; Fields: String; DescFields: String; Options: TIndexOptions; end;
type
TDBWork=class private prgpath,ininame: String; tbl: TTable; IndInfo: array of TIndexInfo; public constructor Create; overload; constructor Create(t: TTable); overload; procedure OpenTable(t: TTable); procedure ReadIndexInfo; procedure DropIndexes; procedure CreateIndexes; procedure SaveToFile; procedure LoadFromFile; end;
implementation

{ TDBWork }

{Автор: Slava Kostin}
constructor TDBWork.Create;
begin
prgpath:=ExtractFilePath(Application.ExeName); tbl:=Nil; end;

constructor TDBWork.Create(t: TTable);
begin
prgpath:=ExtractFilePath(Application.ExeName); OpenTable(t); end;

procedure TDBWork.CreateIndexes;
var i: Integer;
e,a: Boolean; begin
LoadFromFile; with tbl do begin a:=Active; if Active then Close; e:=Exclusive; Exclusive:=true; for i:=0 to Length(IndInfo)-1 do if(IndInfo[i].Name<>'') then AddIndex(IndInfo[i].Name,IndInfo[i].Fields, IndInfo[i].Options,IndInfo[i].DescFields); Exclusive:=e; Active:=a; end; DeleteFile(ininame); end;

procedure TDBWork.DropIndexes;
var i: Integer;
e,a: Boolean; begin
SaveToFile; with tbl do begin a:=Active; if Active then Close; e:=Exclusive; Exclusive:=true; for i:=0 to Length(IndInfo)-1 do if(IndInfo[i].Name<>'') then DeleteIndex(IndInfo[i].Name); Exclusive:=e; Active:=a; end; end;

procedure TDBWork.LoadFromFile;
var i,c: Integer;
ini: TMemIniFile; Section: String; begin
ini:=TMemIniFile.Create(ininame); c:=ini.ReadInteger('Common','DefsCount',0); SetLength(IndInfo,c); for i:=0 to c-1 do begin Section:='IndexDef #'+IntToStr(i); IndInfo[i].Name:=ini.ReadString(Section,'Name',''); IndInfo[i].Fields:=ini.ReadString(Section,'Fields',''); IndInfo[i].DescFields:=ini.ReadString(Section,'DescFields',''); IndInfo[i].Options:=[]; if ini.ReadBool(Section,'ixPrimary',false) then IndInfo[i].Options:=[ixPrimary]; if ini.ReadBool(Section,'ixUnique',false) then IndInfo[i].Options:=IndInfo[i].Options+[ixUnique]; if ini.ReadBool(Section,'ixDescending',false) then IndInfo[i].Options:=IndInfo[i].Options+[ixDescending]; if ini.ReadBool(Section,'ixCaseInsensitive',false) then IndInfo[i].Options:=IndInfo[i].Options+[ixCaseInsensitive]; if ini.ReadBool(Section,'ixExpression',false) then IndInfo[i].Options:=IndInfo[i].Options+[ixExpression]; if ini.ReadBool(Section,'ixNonMaintained',false) then IndInfo[i].Options:=IndInfo[i].Options+[ixNonMaintained]; end; ini.Free; end;

procedure TDBWork.OpenTable(t: TTable);
begin
tbl:=t; ReadIndexInfo; ininame:=prgpath+tbl.TableName+'.set'; end;

procedure TDBWork.ReadIndexInfo;
var i: Integer;
begin
with tbl.IndexDefs do begin Update; SetLength(IndInfo,Count); for i:=0 to Count-1 do begin IndInfo[i].Name:=Items[i].Name; IndInfo[i].Fields:=Items[i].Fields; IndInfo[i].DescFields:=Items[i].DescFields; IndInfo[i].Options:=Items[i].Options; end; end; end;

procedure TDBWork.SaveToFile;
var i: Integer;
ini: TMemIniFile; Section: String; begin
ini:=TMemIniFile.Create(ininame); ini.WriteInteger('Common','DefsCount',Length(IndInfo)); for i:=0 to Length(IndInfo)-1 do begin Section:='IndexDef #'+IntToStr(i); ini.WriteString(Section,'Name',IndInfo[i].Name); ini.WriteString(Section,'Fields',IndInfo[i].Fields); ini.WriteString(Section,'DescFields',IndInfo[i].DescFields); ini.WriteBool(Section,'ixPrimary',ixPrimary in IndInfo[i].Options); ini.WriteBool(Section,'ixUnique',ixUnique in IndInfo[i].Options); ini.WriteBool(Section,'ixDescending',ixDescending in IndInfo[i].Options); ini.WriteBool(Section,'ixCaseInsensitive',ixCaseInsensitive in IndInfo[i].Options); ini.WriteBool(Section,'ixExpression',ixExpression in IndInfo[i].Options); ini.WriteBool(Section,'ixNonMaintained',ixNonMaintained in IndInfo[i].Options); end; ini.UpdateFile; ini.Free; end;

end.

[001962]



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


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

Данный документ объясняет как выполнить запрос в фоновом режиме, используя класс 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 снабжены подробными комментариями.

[001012]



Загрузка изображений в Blob-поля


Имеется несколько способов загрузки изображения в BLOB-поле таблицы dBASE или Paradox. Три самых простых метода включают в себя:

копирование данных из буфера обмена Windows в компонент TDBImage, связанный с BLOB-полем использование метода LoadFromFile компонента TBLOBField использование метода Assign для копирования объекта типа TBitmap в значение свойства Picture компонента TBDBImage. Первый способ, когда происходит копирование изображения из буфера обмена, вероятно, наиболее удобен в случае, когда необходимо добавить изображение в таблицу при использовании приложения конечным пользователем. В этом случае компонент TDBImage используется в роли интерфейса между BLOB-полем таблицы и изображением, хранящимся в буфере обмена. Метод PasteFromClipboard компонента TDBImage как раз и занимается тем, что копирует изображение из буфера обмена в TDBImage. При сохранении записи изображение записывается в BLOB-поле таблицы.

Поскольку буфер обмена Windows может содержать данные различных форматов, то желательно перед вызовом метода CopyFromClipboard осуществлять проверку формата хранящихся в нем данных. Для этого необходимо создать объект TClipboard и использовать его метод HasFormat, позволяющий определить формат хранящихся в буфере данных. Имейте в виду, что для создания объекта TClipboard вам необходимо добавить модуль Clipbrd в секцию uses того модуля, в котором будет создаваться экземпляр объекта.

Вот исходный код примера, копирующий содержание буфера обмена в компонент TDBImage, если содержащиеся в буфере данные имеют формат изображения:

    procedure TForm1.Button1Click(Sender: TObject); var C: TClipboard; begin C := TClipboard.Create; try if Clipboard.HasFormat(CF_BITMAP) then DBImage1.PasteFromClipboard else ShowMessage('Буфер обмена не содержит изображения!'); finally C.Free; end; end;

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

Этот способ использует метод LoadFromFile компонента TBLOBField, который применяется в Delphi для работы с dBASE-таблицами и двоичными Windows полями или таблицами Paradox и графическими Windows полями; в обоих случаях с помощью данного метода возможно загрузить изображение и сохранить его в таблице.

Методу LoadFromFile компонента TBLOBField необходим единственный параметр типа String: имя загружаемого файла с изображением. Значение данного параметра может быть получено при выборе файла пользователем с помощью компонента TOpenDialog и его свойства FileName.

Вот пример, демонстрирующий работу метода LoadFromFile компонента TBLOBField с именем Table1Bitmap (поле с именем Bitmap связано с таблицей TTable, имеющей имя Table1):

    procedure TForm1.Button2Clicck(Sender: TObject); begin Table1Bitmap.LoadFromFile( 'c:\delphi\images\splash\16color\construc.bmp'); end;

Третий способ для копирования содержимого объекта типа TBitmap в свойство Picture компонента TDBImage использует метод Assign. Объект типа TBitmap может быть как свойством Bitmap свойства-объекта Picture компонента TImage, так и отдельного объекта TBitmap. Как и в методе, копирующем данные из буфера обмена в компонент TDBImage, данные изображения компонента TDBImage сохраняются в BLOB-поле после успешного сохранения записи.

Ниже приведен пример, использующий метод Assign. В нашем случае используется отдельный объект TBitmap. Для помещения изображения в компонент TBitmap был вызван его метод LoadFromFile.

    procedure TForm1.Button3Click(Sender: TObject); var B: TBitmap; begin B := TBitmap.Create; try B.LoadFromFile('c:\delphi\images\splashh\16color\athena.bmp'); DBImage1.Picture.Assign(B); finally B.Free; end; end;

[000538]



Запись/чтение чисел в Blob-поле


Мне нужно записать серию чисел в файл Paradox в blob-поле. Числа получаются из значений компонент, размещенных на форме. Затем мне нужно будет считывать числа из blob-поля и устанавливать согласно им значения компонент. Как мне сделать это?

Вы можете начать свое исследование со следующего модуля:

    unit BlobFld;
interface
uses
SysUtils, WinTypes, WinProcs, Messages, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, Buttons, DBTables, DB, ExtCtrls, DBCtrls, Grids, DBGrids;
type TFrmBlobFld = class(TForm) BtnWrite: TBitBtn; Table1: TTable; DataSource1: TDataSource; DBNavigator1: TDBNavigator; LbxDisplayBlob: TListBox; Table1pubid: TIntegerField; Table1comments: TMemoField; Table1UpdateTime: TTimeField; Table1Real1: TFloatField; Table1Real2: TFloatField; Table1Real3: TFloatField; Table1Curr1: TCurrencyField; Table1Blobs: TBlobField; Table1Bytes: TBytesField; CbxRead: TCheckBox; procedure BtnWriteClick(Sender: TObject); procedure DataSource1DataChange(Sender: TObject; Field: TField); procedure FormShow(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); private { Private-Deklarationen } public { Public-Deklarationen } end;
var FrmBlobFld: TFrmBlobFld;
implementation
{$R *.DFM}
type ADouble  = array[1..12] of double; PADouble = ^ADouble;
procedure TFrmBlobFld.BtnWriteClick(Sender: TObject); var i:      integer; myBlob: TBlobStream; v:      longint; begin Table1.Edit;
myBlob := TBlobStream.Create(Table1Blobs, bmReadWrite); try v := ComponentCount; myBlob.Write(v, sizeof(longint));
for i := 0 to ComponentCount - 1 do begin v := Components[i].ComponentIndex; myBlob.Write(v, sizeof(longint)); end; finally Table1.Post; myBlob.Free; end; end;
procedure TFrmBlobFld.DataSource1DataChange(Sender: TObject; Field: TField); var i:        integer; myBlob:   TBlobStream; t:        longint; v:        longint; begin if CbxRead.Checked then begin LbxDisplayBlob.Clear;
myBlob := TBlobStream.Create(Table1Blobs, bmRead); try myBlob.Read(t, sizeof(longint)); LbxDisplayBlob.Items.Add(IntToStr(t));
for i := 0 to t - 1 do begin myBlob.Read(v, sizeof(longint)); LbxDisplayBlob.Items.Add(IntToStr(v)); end; finally myBlob.Free; end; end; end;
procedure TFrmBlobFld.FormShow(Sender: TObject); begin Table1.Open; end;
procedure TFrmBlobFld.FormClose(Sender: TObject; var Action: TCloseAction); begin Table1.Close; end;
end.

- Ralph Friedman [000978]



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


Вся хитрость заключается в использовании 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;

[001388]



CHARTFX - минимум максимум


Так можно сделать с ChartFX в Delphi 2.... Я думаю то же самое будет и в D1...

    cfxStockTrends.Adm[CSA_MIN] := X;       //устанавливаем минимум по оси Y cfxStockTrends.Adm[CSA_MAX] := Y;       //Устанавливаем максимум по оси Y

[001151]