`

delphi数据库开发

阅读更多

Delphi企业版的Borland数据库引擎(BDE)可以高性能地连接SQL服务器,比如InterBase、Microsoft SQL Server、Oracle、InformixDynamic Server、
Sybase Adaptive Server和DB2。

数据集的每一列被称为一个字段,每一行被称为一个记录。
·数据集:是一个分立的数据记录的集合。数据集由VCL的TDataSet表示。
·表:一种特殊类型的数据集。表一般是一个实际存储在磁盘上、包含有数据记录的文件。VCL的TTable类中封装了它的各种功能。
·查询:也是一种特殊类型的数据集。它可以被看作是执行了特殊命令后所产生的“内存表”,这些命令一般是对物理表或表集的操作。
  在VCL中由TQuery类来处理查询。
·数据库:指磁盘中的一个目录(在处理像Paradox、dBASE文件这样的非服务器数据的情况下),或是一个SQL数据库(当使用SQL服务器时)。
  在一个数据库中可以包含多个表。在VCL中有一个TDataBase类。
·索引:用于数据库表排序的规则。所谓基于特定字段建立索引是指利用该字段的值作为表的排序依据。在TTable中包含了一些对索引进行
  操作的方法和属性。

TDataSet类位于整个开放数据集体系结构的顶层。TDataSet是一个用来抽象地表示数据集的记录和字段的组件。为了实现对一些特殊的物理
数据格式的访问,你可以在程序中继承并覆盖TDataSet类的方法。TBDEDataSet就是以这种方式从TDataSet派生而来的与BDE数据源通信的基
类。

BDE数据访问组件:TTable、TQuery和TStoredProc。它们是从TDBDataSet(TDataSet->TBDEDataSet->TDBDataSet)派生而来的。TDataSet是一
个封装了数据集管理、浏览和操作的抽象类。而TBDEDataSet则是一个表达特定的BDE数据集的抽象组件。在TDBDataSet中引入了BDE数据库和
会话的概念。TTable是表达数据库表中的数据和结构的组件; TQuery是利用SQL对数据进行查询并返回数据集的组件;TStoredProc是封装了
SQL服务器上的一个存储过程的组件。

1、打开一个数据集:
在对数据集进行操作之前,必须先将它打开,有以下两种方法:
Table1.Open;
Table1.Active:=true;
使用第二种方法的开销更小一些,因为利用Open方法来实现最终还是要将数据集的Active属性设为True。不过,这点小开销可以忽略。
在一系列的操作完成后,必须用以下两种方法之一将它关闭:
Table1.Close;
Table2.Active:=false;
注意:如果是访问SQL服务器,那么在打开数据库中的一个数据集之前,一定要先建立数据库连接。当你关闭掉数据库中最后一个数据集时,
连接就会终止。建立和断开数据库的连接都会有一些开销。如果你经常执行开关数据库的操作,那么应该利用TDatabase组件来管理对SQL服
务器的连接。
2、浏览数据集:
TDataSet提供了简单的方法来浏览数据集中的记录。First()将当前的记录指针定位在数据集中的第一个记录;Last()把当前的记录指针定位
在数据集中的最后一个记录;Next()和Prior()分别使当前记录指针向前或向后移动一个记录。另外,MoveBy()方法用于向前或向后移动一定
数量的记录。

BOF和EOF都是TDataSet中布尔类型的属性,分别表示当前记录是否是第一个记录和最后一个记录。如果需要遍历数据集中的每一条记录,最
容易实现的方法就是利用一个while循环来使指针从第一个记录不断地向后移动直到EOF为真。如下所示:
Table1.First; //定位到第一条记录上
while not Table1.EOF do
begin
  //对当前记录进行操作
  Table1.Next; //移到下一条记录
end;
注意:
只有在下列情况下BOF才为真:
·刚刚打开数据集。
·刚刚调用了First()方法。
·调用TDataSet.Prior()失败,说明没有前一个记录。
只有在以下情况下,EOF为真:
·打开一个空数据集。
·刚刚调用了Last()方法。
·调用TDataSet.Next()方法失败,说明后面已没有记录。
很显然,如果数据集是空的,则BOF和EOF都是True。
由于以上原因,在通过点击TDBGrid组件中显示的记录来浏览数据集时(实际上TDBGrid组件是调用了TDataSet.MoveBy方法),TTable组件的数
据集的BOF并没有在点击到第一个记录时变为true,EOF属性也没有在点击到最后一个记录时变为true。
3、书签:
书签能在数据集中保存当前记录的位置,以便以后可以回到这个位置。Delphi 用TBookmarkStr类型来表示书签,TTable的Bookmark属性便是
此种类型,例如,以下代码先将Table1当前记录的位置保存在变量BM中,再从BM中恢复记录的位置:
var
  BM:TBookmarkStr;
begin
  BM:=Table1.Bookmark;
  ...
  Table1.Bookmark:=BM;
end;

TDataSource:
  是一个数据库连接中介,它使数据访问组件(如TTable)能向数据感知组件(如TDBGrid和TDBEdit等)提供数据,而且,它还包含了一些使数
据操作变得更简单的属性和事件。
TDataSource的State属性表示当前连接的底层数据集的状态。它可以表明数据集是处于未活动的状态还是插入、编辑、设置键值或是计算字段状态。
TDataSet的OnDataChange事件是在数据集变成活动状态或数据感知组件通知数据集数据发生改变时被触发。
OnUpdateData事件在记录被提交或更新时被触发,在处理此事件时,一般会改变数据感知组件显示的数据,这种改变依赖于数据库表的内容。

利用TField及其派生对象,可以很容易地访问任何数据集的字段。不但可以通过数据集的字段对象来取得当前字段的值、设置该字段值,而
且能够通过修改当前字段对象的属性来改变它在程序中的行为。另外,你还能通过增加、删除、排序和查找字段以及计算产生新字段来修改
数据集。
1. 字段值:
在TDataSet中提供了一个默认的数组属性FieldValues[],这个数组以Variant的形式返回数据集当前活动记录的某个字段的值。因为FieldValues
属性返回的是Variant类型,所以它比使用TField.asXXX要慢。例如,Table1.FieldValues['intValue']比Table1.Fields[1].AsInteger(或
Table1.FieldByName('intValue').AsInteger)要慢。但它有个好处,就是可以用一个语句就获得多个字段的值,例如:
var
  va:Variant;
  i:Integer;
begin
  va:=VarArrayCreate([0,2],varVariant); //创建一个variant数组,有3个元素,下标从0开始,元素的类型为varVariant;
  va:=Table1['name;category;length']; //获得Table1数据集当前活动记录的name字段、category字段和length字段的值
  i:=va[2]; //将获得的length字段值赋给变量i
end;
2、字段类型:
  TField的DataType属性可以告诉你字段的类型,所返回的字段类型只与数据库类型有关,与ObjectPascal的数据类型无关。DataType属性
的类型是TFieldType,定义如下:
type
  TFieldType=(ftUnknown,ftString,ftSmallint,ftInteger,ftWord,ftBoolean,ftFloat,ftCurrency,ftBCD,ftDate,ftTime,ftDateTime,
    ftBytes,ftVarBytes,ftAutoInc,ftBlob,ftMemo,ftGraphic,ftFmtMemo,ftParadoxOle,ftDBaseOle,ftTypedBinary,ftCursor,ftFixedChar,
    ftWideString,ftLargeInt,ftADT,ftArray,ftReference,ftDataSet,ftOraBlob,ftOraClob,ftVariant,ftInterface,ftIDispatch,ftGuid);
3. 字段名和编号
利用TField的FieldName属性能得到某个字段的字段名,利用FieldNo属性可以获取某个字段对应的编号。例如:
var
  s:String;
  i:Integer;
begin
  s:=Table1.Fields[0].FieldName; //第一个字段的名字
  i:=Table1.FieldByName('OrderNo').FieldNo;//字段OrderNo的索引
end;
注意:使用TDataSet的FieldList属性可以确定一个数据集中包含有多少字段。FieldList属性将包括抽象数据类型(ADT)在内的所有嵌套字
段展开进行描述。为了向后兼容,FieldCount属性仍然可用,但它要跳过所有ADT字段。
4. 操作字段数据
下面是编辑当前记录中一个或多个字段的三个操作步骤:
1) 先调用数据集的Edit()方法使数据集处于编辑状态。
2) 给当前字段赋新值。
3) 调用数据集的Post()方法将数据的变化提交给数据库;此步如果不做而直接转入下一步,我们对数据的修改也会自动提交给数据库。
例如:
Table1.Edit;
Table1['age']:=23;
Table1.Post;
提示:在某些情况下,数据集中的数据是只读的。例如,一个存在于CD-ROM上的表文件,或是个非活动的查询结果。所以,在编辑数据前,
要先利用CanModify属性判断数据集中是否包含只读的数据。只有在CanModify属性为TRUE时,才可以编辑数据。
在一个数据集中插入或添加记录时需要以下操作:
1) 先调用数据集的Insert()或Append()方法,使数据集进入相应模式。
2) 对数据集中的字段赋值。
3) 调用数据集的Post()方法将数据的变化提交给数据库;此时,也可以不调用它,当指针转到下一个记录时,数据变化会自动提交。
注意:数据集在插入、添加或编辑状态时,记住,只要离开当前记录,对数据的改变就会被自动提交给数据库。因此,在编辑数据时要小心
使用Next()、Prior()、First()、Last()或MoveBy()等方法。可以利用数据集的Cancel()方法来取消当前对数据集的修改。例如:
Table1.Edit;
Table1['age']:=23;
Table1.Cancel;
Cancel()方法不但取消了对数据的修改,也同时取消了当前数据集的模式状态,回到了浏览模式,也就是说,必须再调用Edit、Intsert或Append
方法才能对数据集进行修改。
数据集的Delete()方法用于将当前记录删除。

注意:使用字段编辑器来为数据集添加管理字段,这样做的好处有:它在窗口类中生成了这些字段的字段对象,它们能在代码中受控制,具有
属性和事件,所以可通过这种方式来对数据集的某些字段进行复杂的控制。
 TField派生对象及其字段类型和相应的ObjectPascal类型
--------------------------------------------------------------------------------------------------------------------------
TField派生对象  祖先类型 字段类型 Object Pascal类型
---------------------------------------------------------------------------------------------------------------------------
TStringField   TField  ftString  String
TWideStringField  TStringField  ftWideString  WideString
TGuidField   TStringField  ftGuid  TGUID
TNumericField  TField   *   *
TIntegerField   TNumericField  ftInteger Integer
TSmallIntField   TIntegerField  ftSmallInt  SmallInt
TLargeintField   TNumericField  ftLargeint  Int64
TWordField   TIntegerField  ftword  Word
TAutoIncField  TIntegerField  ftAutoInc Integer
TFloatField   TNumericField  ftFloat  Double
TCurrencyField   TFloatField  ftCurrency  Currency
TBCDField   TNumericField  ftBCD  Double
TBooleanField   TField  ftBoolean  Boolean
TDateTimeField   TField  ftDateTime  TDateTime
TDateField   TDateTimeField  ftDate  TDateTime
TTimeField   TDateTimeField  ftTime   TDateTime
TBinaryField   TField   *   *
TBytesField  TBinaryField  ftBytes  无
TVarBytesField   TBytesField  ftVarBytes  无
TBlobField  TField  ftBlob   无
TMemoField   TBlobField  ftMemo   无
TGraphicField   TBlobField  ftGraphic  无
TObjectField   TField   *   *
TADTField  TObjectField  ftADT   无
TArrayField   TObjectField  ftArray  无
TDataSetField   TObjectField ftDataSet  TDataSet
TReferenceField  TDataSetField  ftReference
TVariantField  TField  ftVariantOle  Variant
TInterfaceField  TField  ftInterface  IUnknown
TIDispatchField  TInterfaceField ftIDispatch IDispatch
TAggregateField  TField   无  无
--------------------------------------------------------------------------------------------------------------------------
* TField层次中的抽象类。

TDataSet.FieldKind属性:
1.计算字段(calculated field)就是单独对每个记录的某个或某些字段进行计算得到的结果作为值的附加字段,它不属于数据表的一部分,但在
数据感知组件中作为记录的一个字段来显示,它对应于一个字段对象,可以通过字段编辑器来新建一个计算字段,当数据集中有具有计算字
段时,数据集对象的OnCalcFields事件会被触发,所以应该在这个事件的处理句柄中加入代码来计算出计算字段的值。
2.统计字段Aggregate field)就是对所有记录的同一个字段的值进行统计得到的结果作为值的附加字段,它一般是在查询语句中用SUM()函数得到
的字段,不属于数据表的一部分。
3.查找字段(Lookup field)是一个在一个数据集中创建用于获取其他数据集数据的字段。它也不属于数据表的一部分,可以通过字段编辑器来新建
一个查找字段,并需要在New Field对话框中的Lookup definition中设置如下:
Key Fields是查找字段所在的数据集中用来查找其它数据集中数据的依据字段
Dataset是查找字段的值的来源数据集
Lookup Keys是来源数据集中的用来匹配查找条件的字段,一般这里要选择来源数据集中与key Fields相匹配的字段
Result Field是来源数据集中用来给查找字段赋值的字段
所以由上面4个选项可得到查找字段的值为:select [Result Field] from Dataset where Dataset.[Lookup Keys]=[Key Fields]
4.数据字段(TDataSet.FieldKind为fkData)是一般的字段种类,可以新建一个数据字段来复盖已经存在的字段以达到改变原有字段的数据类型等功能。
5.内部计算字段(TDataSet.FieldKind为fkInternalCalc)由数据库服务器或Client DataSet对象在运行时计算得到的并且保存成数据集的一部分。
注意:
当TDataSet.AutoCalcFields为true(默认)时,只有以下情况才会触发OnCalcFields事件和自动计算查找字段的值:
1、数据集被打开
2、数据集进入编辑状态
3、对记录进行修改
4、焦点从数据感知组件的一个列移动到另一个列或从一个数据感知组件移动到另一个数据感知组件。
当TDataSet.AutoCalcFields为false时,只有以下情况才会触发OnCalcFields事件和自动计算查找字段的值:
1、数据集被打开
2、数据集进入编辑状态
3、从数据库中重新获得一个记录

在字段编辑器的字段列表框中的字段可以直接被拖到窗体上,Delphi会感知到拖到窗体上的字段的类型,并且为它选择合适的数据感知组件,
Delphi会检查是否有与数据集相连的TDataSource对象。如有必要的话,会自动创建一个TDataSource对象,并将数据集、TDataSource对象与
数据感知组件连接起来。

TBlobField和字段类型:
  BLOB(Binary Large Object)字段用来保存容量不确定的数据。BLOB最适合于保存大量的文本、图像或类似OLE对象那样的原始数据流。
  TBlobField用来封装BLOB字段,它有一个TBlobType类型的属性BlobType,该属性用于表明保存于BLOB字段的数据类型。在DB单元中,TBlobType的声明如下:
type
  TBlobType=ftBlob..ftOraClob;
 TBlobField字段类型
-----------------------------------------------------
字段类型  数据类型
-----------------------------------------------------
ftBlob    无类型的或用户定义的数据类型
ftMemo    文本类型
ftGraphic   Windows位图类型
ftFmtMemo   Paradox的格式化备注类型
ftParadoxOle   Paradox OLE对象类型
ftDBaseOLE   DBASE OLE对象类型
ftTypedBinary   已存在类型的原始数据表示类型
ftCursor..ftDataSet  不是合法的BLOB类型
ftOraBlob   Oracle 8表中的BLOB字段
ftOraClob   Oracle 8表中的CLOB字段
-----------------------------------------------------
TBlobField组件读写数据时,要完成的大部分工作是把文件加载到BLOB字段或把BLOB字段保存到文件。这时,还可以使用TBlobStream对象。
TBlobStream是一个针对数据库表中的BLOB字段的TStream派生对象。例如以下代码说明如何使用TBlobField对象从磁盘文件中加载WAVE文件到
Blob字段中,将Blob字段中的数据保存到磁盘文件中,使用TBlobStream对象来播放保存在Blob字段中的Wave数据:
procedure TMainForm.AddSoundBtnClick(Sender:TObject);
begin
  //tblSoundsWave是一个TBlobField对象,它由字段编辑器生成,属于tblSounds表的一个Blob字段
  //TBlobField.LoadFromFile用来从磁盘文件中加载数据到TBlobField对象对应的Blob字段中
  if OpenDialog1.Execute then
    tblSoundsWave.LoadFromFile(OpenDialog1.FileName);
end;

procedure TMainForm.SaveSoundBtnClick(Sender:TObject);
begin
  //TBlobField.SaveToFile用来将TBlobField对象对应的Blob字段中的数据保存到磁盘文件中。
  if SaveDialog1.Execute then
    tblSoundsWave.SaveToFile(SaveDialog1.FileName);
end;

procedure TMainForm.PlaySoundBtnClick(Sender:TObject);
var
  BS:TBlobStream;
  MS:TMemoryStream;
begin
  BS:=TBlobStream.Create(tblSoundsWave,bmRead); //创建与TBlobField对象关联的Blob流,提示:如果要以bmReadWrite方式打开TBlobStream流,数据集必须处于编辑、插入或添加模式。
  Screen.Cursor:=crHourGlass; //出现等待光标
  try
    MS:=TMemoryStream.Create; //创建内存流
    try
      MS.CopyFrom(BS,BS.Size);  //从Blob流中拷贝数据到内存流中
      Win32Check(PlaySound(MS.Memory,0,SND_SYNC or SND_MEMORY)); //调用PlaySound API函数播放内存流中的Wave数据
    finally
      MS.Free;
    end;
  finally
    Screen.Cursor:=crDefault;
    BS.Free;
  end;
end;

刷新数据集:
  可以使用TDataset的Refresh()方法来更新数据集,它相当于先调用Close()方法,再调用Open()方法,但是调用Refresh()方法要快一些。
Refresh()方法适用于所有的本地表;而对于来自SQL数据库服务器的数据库,Refresh()方法使用起来有些限制。在BDE试图进行Refresh()操
作之前,必须对SQL数据库表和查询定义一个唯一的索引。Refresh()并不工作于连接到SQL数据库的TQuery组件。

TDataSet.State属性用来确定数据集当前的状态,取值如下:
 TDataSetState的值
------------------------------------------------------------------------------------------------------------------
取值  含义
------------------------------------------------------------------------------------------------------------------
dsBrowse  浏览模式,这是刚打开数据集时的默认状态
dsCalcFields  OnCalcFields事件已发生,对记录值的计算正在进行中
dsEdit   编辑模式,意味着Edit()方法已被调用,而编辑后的数据尚未被提交
dsInactive  数据集被关闭
dsInsert  插入模式,即insert()被调用,但变化还没有提交
dsSetKey  设置键值模式,意味着SetKey()被调用,而GotoKey()尚未被调用
dsNewValue  数据集处于NewValue属性被访问的临时状态,由内部访问
dsOldValue  数据集处于OldValue属性被访问的临时状态,由内部访问
dsCurValue  数据集处于CurValue属性被访问的临时状态,由内部访问
dsFilter  数据集正在处理一个记录过滤器、查找字段或其他需要用到过滤器的操作的状态
dsBlockRead  这个值被设置时,数据正被写入缓冲区,此时数据库表中指针的移动并不触发数据感知组件的更新和事件的发生
dsInternalCalc  一个字段值正在被计算,以供一个有fkInternalCalc类型的Fieldkind属性的字段使用
dsOpening  数据集处于正在打开状态但是还没有结束。这种状态发生在数据集被异步打开时
-------------------------------------------------------------------------------------------------------------------

过滤器:
  过滤器能对数据集实现一些简单的数据搜索或过滤。其最主要的好处是不需要索引,也不需要对数据集做任何其他的准备工作。当然,过
滤器和基于索引的查找比起来,速度还是稍微慢一些。为数据集指定一个过虑器有以下两种方法:
1、给数据集的Filter属性指定一个过滤条件字符串,例如:某字段的值=指定值,这样只有符合这个过滤条件的记录才会保留在数据集中,
其它的记录就被过滤掉了。
2、在数据集的OnFilterRecord事件中加入类似于以下的代码作为过滤条件:
procedure TForm1.Table1FilterRecord(DataSet: TDataSet;
  var Accept: Boolean);
begin
  Accept:=Table1['terms']='FOB'; //当Accept为真时,对应的记录就保留在数据集中,否则就被过滤掉。
end;
不管选择哪种方法都要将数据集的Filtered属性设置为true以启用数据集的过滤器功能。这两种方法性能不同,使用Filter属性来指定过滤条件
比使用OnFilterRecord事件来指定过滤条件要高效,因为Filter属性指定的过滤条件将会被作为数据集获得数据时Where子句中的条件来传递给
数据库,这通常比利用OnFilterRecord事件来逐个记录地搜索要快得多。但某些情况下,Filter属性可能派不上用场,必须用OnFilterRecord
事件来完成。
TDataSet还提供FindFirst()、FindNext()、FindPrior()和FindLast()等方法。利用这些方法可以找出那些符合筛选条件的数据记录。这些
方法要在OnFilterRecord事件处理过程调用,来对未经过滤的数据集进行操作。根据OnFilterRecord事件处理过程中给出的过滤条件,可以
找出第一个、下一个、前一个或最后一个匹配的记录。每一个方法都不用输入参数,其返回值是一个表明是否找到匹配记录的布尔值。
定位一个记录:
  TDataSet提供了一个名为Locate()的方法,它使用过滤器在数据集中查找符合过滤条件的记录,而且与数据集中的任何索引都没有关系,
因为它会自动切换不同的索引,所以它能以最快的速度找到特定的记录,并且程序代码不用在添加或修改了索引之后需要修改。Locate()方
法声明如下:
function Locate(const KeyFields:String;const KeyValues:Variant;Options:TLocateOptions):Boolean;
type
  TLocateOption=(loCaseInsensitive,loPartialKey);
  TLocateOptions=set of TLocateOption;
说明:KeyFields参数是用来指定查找记录时所依据的字段,KeyValues参数是依据字段需要匹配的值,Options参数指定查找选项,
loCaseInsensitive表示区分大小写,loPartialKey表示部分匹配。Locate()方法如果找到一个匹配的记录就会返回true。

TTable对象:
  对SQL数据库表,如果所要查找的字段没有定义相应的索引,那么查询的效率将会很低以致于无法忍受。
1. FindKey
TTable组件的FindKey()方法可用来查找一个某些字段跟指定值相匹配的记录。它只有一个类型为array of TVarRec的参数,是一个开放数组。
例如下面语句查找一个记录,它的第一个字段取值为123、第二个字段包含"hello"字符串:
if not Table1.FindKey([123,'hello']) then MessageBeep(0);
2、SetKey与GotoKey
TTable的SetKey()方法能够使数据库表进入设置键值的模式(即设置查找记录所用的搜索条件的状态)。在指定了查找条件后就可以调用
GotoKey()来从头到尾地搜索一个记录。前面的例子如果改用SetKey()..GotoKey()来写,则为:
with Table1 do
begin
  SetKey;
  Fields[0].AsInteger:=123;
  Fields[1].AsString:='Hello';
  if not GotoKey then MessageBeep(0);
end;
3、 最接近的匹配
与前面类似,你也可以调用FindNearest()或SetKey()..GotoNearest()来查找数据库表中与条件最接近匹配的记录。
注意:如果FindKey、FindNearest、GotoKey或GotoNearest能找到记录,则将记录指针指向该记录,并返回true,否则返回false,记录指针
不移动。当上面的方法找到多个记录时,如果TTable.KeyExclusive为false时,则记录指针定位在第一个匹配的记录上,若KeyExclusive为
True时则记录指针定位在最后一个匹配的记录上。
提示:如果是在一个已建立了索引的表中搜索,最好使用FindKey()和FindNearest(),而不用SetKey()..GotoX(),因为前者所需要的代码少
一些,而且使用出错的可能性也小一些。
4. 使用哪个索引
以上的方法都假设你想基于数据库表的主索引来进行查找。如果使用副索引进行查找,需要设置表对象的IndexName属性来指定一个索引。
例如,如果数据库表有一个基于Company字段的副索引,名为ByCompany,下面的代码查找Unisco公司:
with Table1 do
begin
  IndexName:='ByCompany'; //将IndexName设为空字符串时,表示使用默认索引
  SetKey;
  FieldValues['Company']:='Unisco';
  GotoKey;
end;
注意:在表打开的情况下,通过给TTable.IndexName赋值来切换索引会有一些额外的开销。
5、范围过滤器
范围(range)能使你过滤数据库表的数据,对于符合条件的记录,其字段取值都在指定的范围内。范围的工作原理与基于关键字段的查询类似。
在查找过程中,可以对一个给定的数据库表调用SetRange()方法来设定取值范围,或者依次调用SetRangeStart()、SetRangeEnd()和
ApplyRange()方法来完成同样的操作。
注意:对于dBASE表和Paradox表来说,只能基于定义了索引的字段进行查找。对SQL数据库来说,如果不是针对定义了索引的字段来设置范围
的话,性能将受到损害。
  SetRange()需要传递两组array of TVarRec类型的参数,第一组参数表示字段取值范围的下限,第二组则表示字段取值范围的上限。下面
的代码从数据库中过滤出那些第一个字段取值大于等于10并且小于等于15的记录:
Table1.SetRange([10],[15]);
  要使用ApplyRange()来设定取值范围,可以按照下面的操作步骤:
1) 调用SetRangeStart()方法,并修改数据库表的Fields[]数组属性来设置关键字段的起始值。
2) 调用SetRangeEnd()方法,并再次修改Fields[]数组属性来设置关键字段的截止值。
3) 调用ApplyRange()建立新的取值范围过滤器。
对前面的例子,可以改成:
with Table1 do
begin
  SetRangeStart;
  Fields[0].AsInteger:=10; //起始值为10
  SetRangeEnd;
  Fields[0].AsInteger:=15; //结束值为15
  ApplyRange; //应用取值范围过滤器到数据集中
end;
如果想要从一个数据库表中取消一个范围过滤器,并且把数据库恢复到调用ApplyRange()前的状态,可以调用TTable组件的CancelRange()方法。

主/从表
  较典型的例子是客户表(Customer)和订单表(Order)。这是一种一对多的关系,因为一个客户可能有多个订单,这时,Customer是主表,
Order是从表。当两个数据表建立起主从关系之后,从表在数据感知组件中的数据会自动根据主表对应的数据感知组件的当前选择位置而进行
过滤,即只显示与主表对应的数据感知组件当前选择位置的记录相对应的所有从表记录。以下例子说明如何建立两个表的主从关系:Table1
的TableName为Curstomer.DB,DataSource1将Table1的数据发送到DBGrid1上显示,Table2的TableName为Order.DB,DataSource2将Table2的数据
发送到DBGrid2上显示,这样Table1应该是主表,Table2是从表,将Table2的MasterSource设置为DataSource1,点击Table2的MasterFields
属性的右边的按钮,在弹出的[Field Link Designer]对话框中设置主从表的关联字段:将"Available Indexs"设为从表中与主表关联的字段名,
这样就将从表的活动索引改为基于关联字段的索引。在"Detail Fields"列表中选择从表中要来关联主表的字段名,再在"Master Fields"列表中
选择主表中要来关联从表的字段名,再点击中间的"Add"按钮,这样主从表的字段关联关系就被添加到"Joined Fields"列表中,点击"OK"按钮就
可以完成主从表关联关系的定义了。

通过调用TTable.CreateTable来创建基于目录和文件的数据表,创建步骤如下:
1) 创建一个TTable实例。
2) 将其DatabaseName属性设为一个目录或已有的数据库别名。
3) 通过TableName属性来指定数据库表的名称,要求是唯一的。
4) 设置TableType属性以指明要创建的数据库表类型。如果此属性设为ttDefault,表示数据库表的类型
对应于TableName属性中给出的扩展名。例如.DB后缀表示是Paradox表,.DBF后缀表示是dBASE表。
5) 调用TTable.FieldDefs对象的Add()方法,向数据库表中添加字段,Add()方法有4个参数:
. 一个字符串类型的参数,用于指定字段名称。
. 一个TFieldType类型的参数,用于指定字段类型。
. 一个Word类型的参数,用于指定字段的尺寸。要注意,此参数只对String类型和Memo类型的字段适用,而整型、时期等类型字段的大小通常是固定的,所以不需要定义指定字段的尺寸。
. 一个布尔型的参数,用于表明字段的值是否必须非空。对于强制非空的字段来说,在把记录提交到数据库表中时,都必须有值。
6) 如果要为数据库表建立一个索引,则需要调用Table.IndexDefs对象的Add()方法来定义索引字段。IndexDefs.Add()方法需要传递以下三个参数:
. 一个字符串类型的参数,用于指定索引的名称。
. 一个字符串类型的参数,用于指定索引字段的名称。如果是复合索引,可以用分号把多个字段隔开。
. 一个TIndexOptions类型的参数,用于指定索引类型。
7) 调用TTable的CreateTable()。
例如:
with TTable.Create(Self) do
begin
  DatabaseName:='c:\temp';
  TableName:='Foo';
  TableType:=ttParadox;
  with FieldDefs do
  begin
    Add('Age',ftInteger,0,true);
    Add('Name',ftString,25,false);
    Add('Weight',ftFloat,0,false);
  end;
  IndexDefs.Add('','Age',[ixPrimary,ixUnique]);
  CreateTable;
end;
注意:TTable.CreateTable()仅适用于本地表,对于SQL表,要使用TQuery组件.

数据模块:
  数据模块可以被多个项目、开发组甚至整个企业共享。数据模块用VCL中的TDataModule类来操纵。TDataModule可以被看作是一个不可见的
窗体,在该窗体内可以放置需要用到的数据访问组件。创建TDataModule的实例是很简单的,只要使用File|New菜单项并从对象库中选择
Data Module项即可.

其它继承自TDataSet的数据集组件
1、TQuery组件使你能够通过SQL语句从一个或多个数据库表中获取特定的数据集。如果要执行一个不需要返回结果的查询(例如插入操作),
可以调用Execute()方法,而不是Open()方法。。RequesLive属性的作用是表明返回的结果是否是可修改的。如果要返回的结果集可以被修
改,这个属性应当设为True。注意:查询包含一个HAVING子句或包含抽象数据类型(ADT)的字段时,不能编辑返回的数据集,通过访问数据集对
象的CanModify属性来判断是否可以被修改.
2、TStoredProc组件提供了执行SQL服务器上的存储过程的方法。

文本文件数据库:
  Delphi为在程序中访问文本文件数据库提供了有限的支持。这种数据库必须包含两个文件:一个数据文件,其扩展名必须是.TXT,另一个
是概要(schema)文件,其扩展名为.SCH,而且这两个文件的名称必须相同(例如FOO.TXT和FOO.SCH)。概要文件提供了诸如数据库名、字段名
、长度和类型等信息,以使BDE知道如何解释数据文件。概要文件的格式和Windows的INI文件类似。分节的名称(Section Name)就是数据库表
的名称(去掉扩展名)。
  概要文件中的项及其取值
---------------------------------------------------------------------------------------------------------------------------
项  可能的取值 含义
---------------------------------------------------------------------------------------------------------------------------
FILETYPE  VARYING  文件中的每一个字段都可能占用可变长度的空间。字段之间用一个特殊的字符隔开,字符串之间也用一
    个特定的字符隔开
  FIXED   每个字段都根据从行首开始的特定的偏移量定位
CHARSET  (很多种)  指定使用哪种语言驱动程序,通常使用ASCII
DELIMITER  (任何字符)  指定一个字符用来分隔CHAR类型的字段。这只适用于VARYING类型的数据库表
SEPARATOR  (任何字符)  指定一个字符用来分隔字段。这只适用于VARYING类型的数据库表
---------------------------------------------------------------------------------------------------------------------------
在概要文件中,数据库表的每一个字段都必须有一个条目。其格式如下:
FieldX=FieldName,FileType,Size,DecimalPlaces,Offset
说明:X代表字段编号,从1到字段总数;FieldName是字段名,可以是任何字符串,但不能有引号或字符串分隔符;FieldType是字段类型,取
值如下:
----------------------------------------------------------
CHAR   字符或字符串类型的字段
BOOL   布尔型字段(取值为T或F)
DATE   日期字段,格式由BDE配置程序指定
FLOAT  64位的浮点数
LONGINT  32位的整数
NUMBER   16位的整数
TIME   时间字段,格式由BDE配置程序指定
TIMESTAMP  日期和时间字段,格式由BDE配置程序指定
-----------------------------------------------------------
Size是指字段的长度(字符数)。对于数值型字段来说,其取值必须小于等于20;DecimalPlaces只对FLOAT字段有意义,用于指定小数点的位
置;Offset只对FIXED类型的数据库表有意义,指定某个字段的起始位置。
例如,以下是一个Fixed类型的数据表的概要文件:
[OPTEAM]
FILETYPE=FIXED
CHARSET=ASCII
Field1=EmpNo,LONGINT,04,00,00
Field2=Name,CHAR,16,00,05
Field3=OfficeNo,CHAR,05,00,21
Field4=PhoneExt,LONGINT,04,00,27
Field5=Height,FLOAT,05,02,32
下面是一个VARYING类型的表的概要文件:
[OPTEAM2]
FILETYPE=VARYING
CHARSET=ASCII
DELIMITER="
SEPARATOR=,
Field1=EmpNo,LONGINT,04,00,00
Field2=Name,CHAR,16,00,05
Field3=OfficeNo,CHAR,05,00,21
Field4=PhoneExt,LONGINT,04,00,27
Field5=Height,FLOAT,05,02,32
注意:BDE对概要文件的格式是非常讲究的。如果字符的位置放错或有拼错的单词,BDE就无
法识别数据。如果你在访问数据时遇到麻烦,不妨检查一下概要文件。
下面是上面两个概要文件对应的数据文件(文件中每一行对应一个记录):
OPTEAM.txt文件:
2093 Steve TEixeira C2121 1234 6.5
3265 Xavier Pacheco C0001 3456 5.6
2610 Lino Tadros E2126 5678 5.11
0909 Mr. T  B0087 1234 5.9
OPTEAM2.txt文件:
2093,"Steve TEixeira","C2121",1234,6.5
3265,"Xavier Pacheco","C0001",3456,5.6
2610,"Lino Tadros","E2126",5678,5.11
0909,"Mr. T","B0087",1234,5.9
  使用TTable组件可以访问文本文件数据库,方式与访问其他类型的数据库表相似。首先,要把DatabaseName属性设为一个包含数据文件
(TXT)和概要文件(SCH)的别名或目录,再把TableType属性设为ttASCII,设置TableName属性来指定一个文本数据库表。再在TDataSource
组件与TDBGrid组件之间建立关联关系,这样,就可以从网格中看到每个字段的值,如果所有的字段都挤在一列里,就可能是由于BDE无法
正确读取概要文件。
文本文件数据库的限制:
·不支持索引。
·不能使用TQuery组件来访问文本数据库表。
·不支持删除记录。
·不支持插入记录。插入的记录会被加到表的末尾。
·不支持引用完整性验证。
·不支持BLOB字段。
·不能编辑VARYING类型的数据库表。
·文本数据库总是以独占方式打开的。因此,只能在程序代码中打开文本数据库,而不能在设计期打开。

Microsoft 数据访问简介:
· UDA(Universal Data Access)是Microsoft提供的通用数据访问策略,包括ADO、OLE DB和ODBC。它不光提供了数据库的访问能力,对于其他
的数据存储技术也支持,比如目录服务、Excel的表格数据和Exchange服务器数据。
·ODBC(Open Database Connectivity)是目前经确认的最好的数据访问技术。ODBC结构包含了一个普通的基于SQL的API,它利用对应的驱动程
序来开发特定的数据库程序。由于存在巨大的市场,并且对现在的任何数据库都支持,所以, O D B C在很长一段时间内还将使用,尽管它已
存在了很长时间。
·RDO(Remote Data Objects)为ODBC提供了一个COM的封装。其目的是简化ODBC的开发和在Visual Basic和VBA程序中发展ODBC。
·Jet是安装在Microsoft Access中的数据库引擎。Jet支持Microsoft Access本身的MDB数据库和ODBC。
·DAO(Data Access Objects)是另一个基于COM的数据访问API。DAO提供了对Jet和ODBC的封装。
·ODBCDirect是Microsoft后来为DAO添加的对ODBC的直接访问。强于通过Jet对ODBC进行支持。
·OLE DB是一种普通和简化的基于COM的数据访问规则和API。OLE DB被设计成为独立于特殊的数据库后端和底层结构,并且它是Microsoft最新
的数据连接方案。驱动程序(称为OLE DB提供者)可以通过OLE DB与任何数据存储建立链接。
·ADO (ActiveX Data Objects)为开发者提供了一个更加友好的对OLE DB的封装。
·RDS(Remote Data Services)是一个为建立多用户系统而基于ADO对ADO数据源进行远程访问的技术。RDS过去是ADC(Advanced Data Connector)。
·MDAC(Microsoft Data Access Components)是经过验证的分布式UDA的实现和文件。MDAC包括四种不同的技术:ODBC、OLE DB 、ADO和RDS。

ADOExpress组件:
·TADOConnection组件被用于建立一个与ADO数据存储的连接。可以把多个ADO数据集和命令组件与一个TADOConnection组件关联以共享连接。
·TRDSConnection组件通过RDS的DataSpace对象的功能,封装了一个远程的RDS连接。TRDSConnection组件使用时,在ComputerName参数中指定
RDS服务器的名称,并在ServerName属性中设置RDS服务器的ProgID。
·TADODataSet 和TADOCommand提供了比BDE类型更强的ADO类型数据操纵能力。TADODataSet组件是用于获取和操纵ADO数据的主要的组件。该组
件可以操纵数据库表、执行SQL查询和存储过程;TADOCommand组件类似于BDE的TQuery.Excute()和TStoredProc.ExecProc(),用来执行SQL语句,
而不返回结果。TADOCommand组件也能执行SQL语句并返回结果集,但是该结果集必须通过TADODataSet组件操纵。下面的代码显示了如何将
TADOCommand组件的查询结果输入一个TADODataSet:
ADODataSet.RecordSet:=ADOCommand.Execute;
·TADOTable、TADOQurey和TADOStoredProc分别提供了数据库表、查询和存储过程组件,并与BDE组件兼容。


VCL的数据库体系结构是用BDE(Borland Database Engine)通信的,通过BDE可以按相同的方式访问不同类型的数据库。尽管这增加了可靠性、
可伸缩性和易用性,但有一个不足: BDE的许多功能无法在VCL的数据库框架中实现。所以要访问BDE的这些功能,就必需扩展VCL的数据库功能。
所有BDE的函数、类型和常量都是在BDE单元中定义的。凡是要直接调用BDE API的单元一定要引用BDE单元。在\Borland\Borland Shared\BDE
目录中有个BDE32.hlp帮助文件,这个帮助文件中包含了所有BDE API的详细说明以及很好的Object Pascal和C语言的例子。

所有BDE函数都返回一个DBIRESULT类型的值,以表明函数调用是成功还是失败。每次都要检查函数的返回值是很讨厌的,为此, Delphi定义了
一个过程叫Check(),它需要传递一个DBIRESULT类型的参数。如果BDE函数没有调用成功,Check()就会触发异常。使用例子如下:
var
  A:array[0..dbiMaxUserNameLen] of Char;
begin
  { 处理错误和调用在同一行代码中进行。}
  { 如果有错误,将触发异常}
  Check(dbiGetNetUserName(A));
  // 继续做其他事情
end;

许多BDE函数需要传递数据库指针的句柄作为参数。大致说来,指针是一种BDE对象,它代表数据库的某个位置。指针句柄的类型是hBDICur。
在Dephi的项目表、查询和存储过程中,指针对应着当前记录。TTable、TQuery和TSoredProc的Handle属性就是指针的句柄。当调用需传递指
针句柄的BDE函数时,可以用Handle属性作为参数。有些BDE的函数需要传递数据库的句柄作为参数。数据库句柄的类型是hDBIDb。对于dBASE
或Paradox来说,它代表数据库(本地或网络)的工作目录;对于SQL数据库来说,它代表数据库文件。TDatabase的Handle属性就是数据库的句
柄。如果连接数据库时没有用TDatabase组件,可以通过TTable、TQuery和TStoredProc的DBHandle属性获得数据库句柄。当要直接调用BDE函
数而这些函数需要传递数据库指针的句柄作为参数时,应当保证传递过去的确实是数据集的当前记录所对应的指针。只要调用数据集组件(它
们都是从TDataSet继承下来的)的UpdateCursorPos()方法就可以使当前记录与指针同步。当调用了BDE函数后,有可能使指针的位置发生了变
化,此时应当通知VCL使当前记录的位置与指针的位置同步。为此,必须在调用了BDE函数后立即调用数据集组件的CursorPosChanged()方法。
请看下面的代码:
procedure DoSomethingWithTable(T:TTable);
begin
  T.UpdateCursorPos;
  // 此处调用B D E函数
  T.CursorPosChanged;
end;

有一个经典的SQL程序问题:如果一个SQL语句试图返回非常大的查询结果,用户就不得不等待很长时间,从而占用宝贵的服务器和带宽。而
TQuery组件又没有提供限制查询结果的手段。但BDE API中的DbiSetProp()函数能够做到这一点。,第一个参数是指针的句柄,第二个参数应
当设为curMAXROWS,最后一个参数应当设为一个最大行数,以限制返回的查询结果。调用该函数的最理想位置是在TQuery的PrepareCursor()
方法中,它在开始查询后立刻予以调用。例如:
type
  TRestrictedQuery=class(TQuery)
  private
    FMaxRowCount:Longint;
  protected
    procedure PrepareCursor;override;
  published
    property MaxRowCount:Longint read FMaxRowCount write FMaxRowCount;
  end;

procedure Register;

implementation

procedure TRestrictedQuery.PrepareCursor;
begin
  inherited PrepareCursor;
  if FMaxRowCount>0 then
    Check(DbiSetProp(hDBIObj(Handle),curMAXROWS,FMaxRowCount));
end;

procedure Register;
begin
 RegisterComponents('DDG',[TRestrictedQuery]);
end;

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics