一点心得:在 Delphi 中使用原生 ADO 控制数据库,班门弄斧了 ( 积分: 100 )

  • 主题发起人 主题发起人 vvyang
  • 开始时间 开始时间
V

vvyang

Unregistered / Unconfirmed
GUEST, unregistred user!
我发现很多朋友在开发数据库时都使用 Delphi 自带的 ADO 组件 或 Diamond ADO,其实在 Delphi 中使用原生 ADO 接口也是十分方便和有效的。我使用原生 ADO 开发项目已有很长一段时间,也回答过一些朋友类似的问题,现在把自己的一点心得与大家分享,班门弄斧,只是希望能对大家有所帮助。当然,这帖子也是原生的,不是转贴的。

一、优点
1、大家知道 Delphi 对 ADO 接口进行了一番包装后形成了 ADOExpress,我想 Borland 的主要目的还是想与自己的数据敏感控件相连。然而事实上数据敏感控件并不是那么耀眼,如果你希望编出来的程序稍微有点水准的话就别用那玩意;如果你很少使用数据敏感控件,那么 ADOExpress 基本上失去了其应有的作用,无数冗余的属性、虚方法,不管你用不用得到一股脑给你编译进去,也会使你的程序再大上 200K;效率么,不说了。
2、MSDN 和 VB 中的例子你可以搬过来就用。
3、关于代码重用:我给大家的例子都是以函数或过程形式,重用性不好么?
4、别说帖子太长,那你看看 DB.pas, ADODB.pas 多长?

二、基本储备
1、一些必须的单元
uses
Variants, ComObj;

2、一些基本常数(其它查 ADODB2000.pas 或搜索 adovbs.inc):
const
adOpenDynamic = $00000002;
adOpenStatic = $00000003;

adLockOptimistic = $00000003;
adLockBatchOptimistic = $00000004;

adConnectUnspecified = $FFFFFFFF;
adAsyncConnect = $00000010;

adStateClosed = $00000000;
adStateOpen = $00000001;
adStateConnecting = $00000002;
adStateExecuting = $00000004;
adStateFetching = $00000008;

adUseServer = $00000002;
adUseClient = $00000003;

adModeReadWrite = $00000003;

adXactCursorStability = $00001000;

adCmdText = $00000001;
adCmdTable = $00000002;
adCmdStoredProc = $00000004;
adCmdFile = $00000100;

adAffectCurrent = $00000001;
adAffectGroup = $00000002;
adAffectAll = $00000003;
adAffectAllChapters = $00000004;

adEmpty = $00000000; //空
adInteger = $00000003; //长整
adSingle = $00000004; //单精
adDouble = $00000005; //双精
adCurrency = $00000006; //货币
adGUID = $00000048; //全球唯一的标志码
adDate = $00000007; //日期
adDBDate = $00000085; //日期值(yyyymmdd)
adDBTime = $00000086; //时间值(hhmmss)
adDBTimeStamp = $00000087; //日期时间值(yyyymmdd hhmmss)
adBoolean = $0000000B; //布尔
adVarChar = $000000C8; //字符串
adVarWChar = $000000CA; //文本
adLongVarWChar = $000000CB; //备注

//存储过程参数结构,只定义了常用的部分,如果需要自己加吧
type
TParameter_ = record
Name: WideString;
Type_: LongWord;
Direction: LongWord;
Size: Longint;
Value: OleVariant;
end;

TParameters_ = array of TParameter_;

3、一些基本函数和过程
//创建 Connection 对象
function CreateConnection: OleVariant;
//释放 Connection 对象;cnn 为 Connection 对象
procedure FreeConnection(var cnn: OleVariant);
//创建 Recordset 对象
function CreateRecordset: OleVariant;
//释放 Recordset 对象;rst 为 Recordset 对象
procedure FreeRecordset(var rst: OleVariant);
//创建 Command 对象
function CreateCommand: OleVariant;
//释放 Command 对象;cmd 为 Command 对象
procedure FreeCommand(var cmd: OleVariant);
//用 Connection 连接到 SQLServer 数据库;cnn 为 Connection 对象,srv 主机名或 IP 地址,uid 用户名,pwd 密码,db 数据库名
function ConnectToDB(cnn: OleVariant; const srv, uid, pwd, db: string): Boolean;
//执行 SQL 语句,有返回行,无事务处理;cnn 为 Connection 对象,rst 为 Recordset 对象,sql 为 SQL 语句(可以是存储过程)
function ExecSQL(cnn, rst: OleVariant; const sql: string): Boolean;
//执行 SQL 语句,无返回行,有事务处理;cnn 为 Connection 对象,cmd 为 Command 对象,sql 为 SQL 语句(可以是存储过程)
function ExecSQLA(cnn, cmd: OleVariant; const sql: string): Boolean;
//执行存储过程;cnn 为 Connection 对象,cmd 为 Command 对象,rst 为 Recordset 对象(用于接收来自存储过程的数据集);prc 为存储过程名; prms 为参数集合
function ExecProc(cnn, cmd, rst: OleVariant; const prc: string; prms: TParameters_): Boolean;

function CreateConnection: OleVariant;
begin
try
Result := CreateOleObject('ADODB.Connection');
Result.CursorLocation := adUseServer;
Result.IsolationLevel := adXactCursorStability;
Result.Mode := adModeReadWrite;
Result.Provider := 'SQLOLEDB.1';
except
if not VarIsEmpty(Result) then Result := Unassigned;
end;
end;

procedure FreeConnection(var cnn: OleVariant);
begin
if not VarIsEmpty(cnn) then
begin
if cnn.State <> adStateClosed then cnn.Close;
cnn := Unassigned;
end;
end;

function CreateRecordset: OleVariant;
begin
try
Result := CreateOleObject('ADODB.Recordset');
Result.CacheSize := 1000;
Result.CursorType := adOpenStatic;
Result.CursorLocation := adUseServer;
Result.LockType := adLockOptimistic;
except
if not VarIsEmpty(Result) then Result := Unassigned;
end;
end;

procedure FreeRecordset(var rst: OleVariant);
begin
FreeConnection(rst);
end;

function CreateCommand: OleVariant;
begin
try
Result := CreateOleObject('ADODB.Command');
Result.CommandType := adCmdText;
Result.CommandTimeout := 5;
except
if not VarIsEmpty(Result) then Result := Unassigned;
end;
end;

procedure FreeCommand(var cmd: OleVariant);
begin
if not VarIsEmpty(cmd) then cmd := Unassigned;
end;

function ConnectToDB(cnn: OleVariant; const srv, uid, pwd, db: string): Boolean;
begin
Result := not VarIsEmpty(cnn);
if Result then
begin
if cnn.State <> adStateClosed then cnn.Close;
cnn.ConnectionString :=
'Provider=SQLOLEDB.1;Persist Security Info=True;Initial Catalog=' +
db + ';Data Source=' + srv + ';Connect Timeout=10;' +
'Use Procedure for Prepare=1';
try
cnn.Open(cnn.ConnectionString, uid, pwd, adConnectUnspecified);
except
Result := False;
end;
end;
end;

function ExecSQL(cnn, rst: OleVariant; const sql: string): Boolean;
begin
Result := not (VarIsEmpty(cnn) or VarIsEmpty(rst)) and (cnn.State = adStateOpen);
if Result then
begin
if rst.State <> adStateClosed then rst.Close;
try
rst.Open(sql, cnn, adOpenStatic, adLockOptimistic, adCmdText);
except
Result := False;
end;
end;
end;

function ExecSQLA(cnn, cmd: OleVariant; const sql: string): Boolean;
begin
Result := not (VarIsEmpty(cnn) or VarIsEmpty(cmd)) and (cnn.State = adStateOpen);
if Result then
begin
cnn.BeginTrans;
try
cmd.ActiveConnection := cnn;
cmd.CommandText := sql;
cmd.Prepared := True;
cmd.Execute;
cnn.CommitTrans;
except
cnn.RollbackTrans;
Result := False;
end;
end;
end;

function ExecProc(cnn, cmd, rst: OleVariant; const prc: string; prms: TParameters_): Boolean;
var
i: Longint;
begin
Result := not (VarIsEmpty(cnn) or VarIsEmpty(cmd)) and (cnn.State = adStateOpen);
if Result then
begin
try
cmd.CommandText := prc;
cmd.CommandType := adCmdStoredProc;
cmd.ActiveConnection := cnn;

//之前,将我们自己的参数集合中的输入参数添入 Command 参数集合
for i := Low(prms) to High(prms) do
case prms.Direction of
//未知类型的参数(adParamUnknown)既不算作输入参数也不算作输出参数
adParamInput, adParamInputOutput:
cmd.Parameters[prms.Name].Value := prms.Value;
end;

rst := cmd.Execute;

//之后,将输出参数从 Command 参数集合读取到我们自己的参数集合
for i := Low(prms) to High(prms) do
case prms.Direction of
//未知类型的参数(adParamUnknown)既不算作输入参数也不算作输出参数
adParamOutput, adParamInputOutput:
prms.Value := cmd.Parameters[prms.Name].Value;
adParamReturnValue:
prms.Value := cmd.Parameters[0].Value;
end;
except
Result := False;
end;
end;
end;

三、访问数据
1、最前 rst.MoveFirst;
2、最后 rst.MoveLast;
3、向前 rst.MovePrevious;
4、向后 rst.MoveNext;
5、取当前记录 rst.Fields[0].Value 或 rst.Fields['字段名'].Value;
6、修改当前记录 rst.Update(rst.Fields[0].Name, 某值);
7、取消修改 rst.CancelUpdate;
8、删除当前记录 rst.Delete(adAffectCurrent);
9、删除所有记录 rst.Delete(adAffectAll);
10、追加记录
rst.AddNew;
rst.Fields[0].Value := 值1;
rst.Fields[1].Value := 值2;
rst.Update;
11、刷新 rst.Refresh;
12、记录数 rst.RecordCount
15、其它方法和属性查 MSDN 或 ADO 的帮助;

四、一些例子

//变量声明
var
cnn, rst, cmd: OleVariant;

//创建对象
procedure TForm1.FormCreate(Sender: TObject);
begin
cnn := CreateConnection;
rst := CreateRecordset;
cmd := CreateCommand;
end;

//释放对象
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeCommand(cmd);
FreeRecordset(rst);
FreeConnection(cnn);
end;

//连接数据库
procedure TForm1.Button1Click(Sender: TObject);
begin
if ConnectToDB(cnn, '127.0.0.1', 'sa', 'ok', 'Northwind') then
Caption := '连接成功'
else Caption := '连接失败';
end;

//取记录
procedure TForm1.Button2Click(Sender: TObject);
begin
if ExecSQL(cnn, rst, 'SELECT * FROM Products') then
Caption := VarToStr(rst.Fields['ProductName'].Value);
end;

//执行存储过程
(*
CREATE PROCEDURE GetProductName
@ProductID int,
@ProductName varchar(50) OUTPUT
AS
SELECT @ProductName = ProductName
FROM Products
WHERE ProductID = @ProductID
RETURN @ProductID
*)
procedure TForm1.Button3Click(Sender: TObject);
var
prms: TParameters_;
begin
SetLength(prms, 3);
//参数 0 接收返回值;Name 取什么都行,但 Direction 必须标明为adParamReturnValue
prms[0].Name := '@RETURN_VALUE';
prms[0].Direction := adParamReturnValue;
//参数 1 作为输入参数,Name 必须与实际参数名相同,Direction 标明 adParamInput
prms[1].Name := '@ProductID';
prms[1].Direction := adParamInput;
prms[1].Value := 8;
//参数 2 作为输出参数,Name 必须与实际参数名相同,Direction 标明 adParamOutput
prms[2].Name := '@ProductName';
prms[2].Direction := adParamOutput;
//执行存储过程;如果不需要 Recordset 对象接收返回的数据集,就把它置空
ExecProc(cnn, cmd, Null, 'GetProductName', prms);
ShowMessage(prms[0].Value);
ShowMessage(prms[2].Value);
end;

五、原生 ADO 与 Delphi ADOExpress 组件的对应关系
1、Connection <=> ADOConnection.ConnectionObject;
2、Recordset <=> ADODataSet.Recordset;
3、Command <=> ADOCommand.CommandObject;
4、? <=> ADOQuery,因为 ADOQuery 根本就不是原生 ADO 对象
5、ExecSQL <=> ADODataSet.Open;
6、ExecSQLA <=> ADOCommand.Execute;
7、有了上面几个其它的就不多说了

六、与数据库结构有关的一些函数
1、动态改变字段名称
uses ComObj;
//Access
//TableName: 表名; OldColName: 原字段名; NewColName: 新字段名;
procedure RenameField(const TableName, OldColName, NewColName: string);
var
DB, Col: OleVariant;
begin
DB := CreateOleObject('ADOX.Catalog');
DB.ActiveConnection := ADOConnection1.ConnectionObject;
Col := CreateOleObject('ADOX.Column');
Col := DB.Tables[TableName].Columns[OldColName];
Col.Name := NewColName;
end;

//SQLServer
procedure RenameField(const TableName, OldColName, NewColName: string);
begin
with ADOCommand1 do
begin
CommandText := 'EXEC sp_rename ''' + TableName + '.' + OldColName +
''',''' + NewColName + ''',''COLUMN'';';
Excute;
end;
end;

2、取得 Access 库中的表结构
type
TTableDef = record
Name,
DateCreated,
LastUpdated,
Description: string;
end;

TTableDefs = array of TTableDef;

procedure GetTableDefs(const DBName: string; out TableDefs: TTableDefs);
var
DBEngine, DB: OleVariant;
I: Longint;
begin
try
DBEngine := CreateOleObject('DAO.DBEngine.36');
DB := DBEngine.OpenDatabase(DBName);
SetLength(TableDefs, Longint(DB.TableDefs.Count));
for I := Low(TableDefs) to High(TableDefs) do
begin
TableDefs.Name := DB.TableDefs.Name;
TableDefs.DateCreated := DB.TableDefs.DateCreated;
TableDefs.LastUpdated := DB.TableDefs.LastUpdated;
try
TableDefs.Description := DB.TableDefs.Properties['Description'].Value;
except
TableDefs.Description := '';
end;
end;
finally
DB := Unassigned;
DBEngine := Unassigned;
end;
end;

3、取得 Access 表中的字段结构
type
TFieldDef = record
Name: string;
Types,
Size: Longint;
Description: string;
end;

TFieldDefs = array of TFieldDef;

procedure GetFieldDefs(const DBName, TableName: string; out FieldDefs: TFieldDefs);
var
DBEngine, DB: OleVariant;
I: Longint;
begin
try
DBEngine := CreateOleObject('DAO.DBEngine.36');
DB := DBEngine.OpenDatabase(DBName);
SetLength(FieldDefs, Longint(DB.TableDefs[TableName].Fields.Count));
for I := Low(FieldDefs) to High(FieldDefs) do
begin
FieldDefs.Name := DB.TableDefs[TableName].Fields.Name;
FieldDefs.Types := DB.TableDefs[TableName].Fields.Type;
FieldDefs.Size := DB.TableDefs[TableName].Fields.Size;
try
FieldDefs.Description := DB.TableDefs[TableName].Fields.Properties['Description'].Value;
except
FieldDefs.Description := '';
end;
end;
finally
DB := Unassigned;
DBEngine := Unassigned;
end;
end;

4、不用 SQL 语句,仅用 ADOX,创建 Access 数据库、表、字段、字段备注(同样适用于 SQLServer)
function CreateMDB(const DBName, TableName: string): Boolean;
var
cat, tbl, col: OleVariant;
begin
Result := True;
try
try
//生成数据库
cat := CreateOleObject('ADOX.Catalog');
cat.Create('Provider=Microsoft.Jet.OLEDB.4.0;Data Source=' + DBName);

//生成表
tbl := CreateOleObject('ADOX.Table');
tbl.ParentCatalog := cat;
tbl.Name := TableName;

//生成一个带备注、自动编号的长整字段
col := CreateOleObject('ADOX.Column');
col.ParentCatalog := cat;
col.Name := '带自动编号的长整字段';
col.Type := adInteger;
col.DefinedSize := 4;
//自动编号字段就是长整字段,仅仅多了下面这句话
col.Properties['Autoincrement'].Value := True;
col.Properties['Description'].Value := '自动编号长整字段的说明';
tbl.Columns.Append(col, adEmpty, 0);
col := Unassigned;
//生成一个带备注的文本字段
col := CreateOleObject('ADOX.Column');
col.ParentCatalog := cat;
col.Name := '文本字段';
col.Type := adVarWChar;
col.DefinedSize := 50;
col.Properties['Description'].Value := '文本字段的说明';
tbl.Columns.Append(col, adEmpty, 0);
//生成一个不带备注的双精字段,比上面要简单的多
tbl.Columns.Append('双精字段', adDouble, 8);
//生成一个不带备注的备注字段
tbl.Columns.Append('备注字段', adLongVarWChar, 16);

cat.Tables.Append(tbl);
except
Result := False;
end;
finally
cat := Unassigned;
tbl := Unassigned;
col := Unassigned;
end;
end;

七、进行分页显示数据
俺们几乎每天都见到网页用 ADO 进行分页(每次只取一定数量的记录),但是在 Delphi 中分页也成了稀罕的东西;
分页有三个常用的属性:PageSize—页面大小,PageCount—页面总数,AbsolutePage—决定跳转到哪一页面。
1、首先取得数据集,不罗嗦了
ADODataSet1.Open....
2、下面这个例子,将指定页面的数据读到 Memo1 中
procedure GetPageData(const nPage: Integer);
var
i: Integer;
begin
wiht ADODataSet1.Recordset do
begin
if (State = adStateClosed) or (nPage < 1) or (nPage > PageCount) then Exit;
Memo1.Clear;
AbsolutePage := nPage;
//这里只能用 for 不能用 while(Eof 仍然是数据集的结尾)
for i := 1 to PageSize do
begin
Memo1.Lines.Add(Fields['ProductName'].Value);
MoveNext;
//防止因最后一页未满而产生越界
if Eof then Exit;
end;
end;
end;

八、其它
1、希望此贴能对朋友们有所帮助,我写的这些都是经过实战检验的;
2、觉得有用的朋友可以试一下,如果觉得没用就当我没写,耽误你时间了。
 
我发现很多朋友在开发数据库时都使用 Delphi 自带的 ADO 组件 或 Diamond ADO,其实在 Delphi 中使用原生 ADO 接口也是十分方便和有效的。我使用原生 ADO 开发项目已有很长一段时间,也回答过一些朋友类似的问题,现在把自己的一点心得与大家分享,班门弄斧,只是希望能对大家有所帮助。当然,这帖子也是原生的,不是转贴的。

一、优点
1、大家知道 Delphi 对 ADO 接口进行了一番包装后形成了 ADOExpress,我想 Borland 的主要目的还是想与自己的数据敏感控件相连。然而事实上数据敏感控件并不是那么耀眼,如果你希望编出来的程序稍微有点水准的话就别用那玩意;如果你很少使用数据敏感控件,那么 ADOExpress 基本上失去了其应有的作用,无数冗余的属性、虚方法,不管你用不用得到一股脑给你编译进去,也会使你的程序再大上 200K;效率么,不说了。
2、MSDN 和 VB 中的例子你可以搬过来就用。
3、关于代码重用:我给大家的例子都是以函数或过程形式,重用性不好么?
4、别说帖子太长,那你看看 DB.pas, ADODB.pas 多长?

二、基本储备
1、一些必须的单元
uses
Variants, ComObj;

2、一些基本常数(其它查 ADODB2000.pas 或搜索 adovbs.inc):
const
adOpenDynamic = $00000002;
adOpenStatic = $00000003;

adLockOptimistic = $00000003;
adLockBatchOptimistic = $00000004;

adConnectUnspecified = $FFFFFFFF;
adAsyncConnect = $00000010;

adStateClosed = $00000000;
adStateOpen = $00000001;
adStateConnecting = $00000002;
adStateExecuting = $00000004;
adStateFetching = $00000008;

adUseServer = $00000002;
adUseClient = $00000003;

adModeReadWrite = $00000003;

adXactCursorStability = $00001000;

adCmdText = $00000001;
adCmdTable = $00000002;
adCmdStoredProc = $00000004;
adCmdFile = $00000100;

adAffectCurrent = $00000001;
adAffectGroup = $00000002;
adAffectAll = $00000003;
adAffectAllChapters = $00000004;

adEmpty = $00000000; //空
adInteger = $00000003; //长整
adSingle = $00000004; //单精
adDouble = $00000005; //双精
adCurrency = $00000006; //货币
adGUID = $00000048; //全球唯一的标志码
adDate = $00000007; //日期
adDBDate = $00000085; //日期值(yyyymmdd)
adDBTime = $00000086; //时间值(hhmmss)
adDBTimeStamp = $00000087; //日期时间值(yyyymmdd hhmmss)
adBoolean = $0000000B; //布尔
adVarChar = $000000C8; //字符串
adVarWChar = $000000CA; //文本
adLongVarWChar = $000000CB; //备注

//存储过程参数结构,只定义了常用的部分,如果需要自己加吧
type
TParameter_ = record
Name: WideString;
Type_: LongWord;
Direction: LongWord;
Size: Longint;
Value: OleVariant;
end;

TParameters_ = array of TParameter_;

3、一些基本函数和过程
//创建 Connection 对象
function CreateConnection: OleVariant;
//释放 Connection 对象;cnn 为 Connection 对象
procedure FreeConnection(var cnn: OleVariant);
//创建 Recordset 对象
function CreateRecordset: OleVariant;
//释放 Recordset 对象;rst 为 Recordset 对象
procedure FreeRecordset(var rst: OleVariant);
//创建 Command 对象
function CreateCommand: OleVariant;
//释放 Command 对象;cmd 为 Command 对象
procedure FreeCommand(var cmd: OleVariant);
//用 Connection 连接到 SQLServer 数据库;cnn 为 Connection 对象,srv 主机名或 IP 地址,uid 用户名,pwd 密码,db 数据库名
function ConnectToDB(cnn: OleVariant; const srv, uid, pwd, db: string): Boolean;
//执行 SQL 语句,有返回行,无事务处理;cnn 为 Connection 对象,rst 为 Recordset 对象,sql 为 SQL 语句(可以是存储过程)
function ExecSQL(cnn, rst: OleVariant; const sql: string): Boolean;
//执行 SQL 语句,无返回行,有事务处理;cnn 为 Connection 对象,cmd 为 Command 对象,sql 为 SQL 语句(可以是存储过程)
function ExecSQLA(cnn, cmd: OleVariant; const sql: string): Boolean;
//执行存储过程;cnn 为 Connection 对象,cmd 为 Command 对象,rst 为 Recordset 对象(用于接收来自存储过程的数据集);prc 为存储过程名; prms 为参数集合
function ExecProc(cnn, cmd, rst: OleVariant; const prc: string; prms: TParameters_): Boolean;

function CreateConnection: OleVariant;
begin
try
Result := CreateOleObject('ADODB.Connection');
Result.CursorLocation := adUseServer;
Result.IsolationLevel := adXactCursorStability;
Result.Mode := adModeReadWrite;
Result.Provider := 'SQLOLEDB.1';
except
if not VarIsEmpty(Result) then Result := Unassigned;
end;
end;

procedure FreeConnection(var cnn: OleVariant);
begin
if not VarIsEmpty(cnn) then
begin
if cnn.State <> adStateClosed then cnn.Close;
cnn := Unassigned;
end;
end;

function CreateRecordset: OleVariant;
begin
try
Result := CreateOleObject('ADODB.Recordset');
Result.CacheSize := 1000;
Result.CursorType := adOpenStatic;
Result.CursorLocation := adUseServer;
Result.LockType := adLockOptimistic;
except
if not VarIsEmpty(Result) then Result := Unassigned;
end;
end;

procedure FreeRecordset(var rst: OleVariant);
begin
FreeConnection(rst);
end;

function CreateCommand: OleVariant;
begin
try
Result := CreateOleObject('ADODB.Command');
Result.CommandType := adCmdText;
Result.CommandTimeout := 5;
except
if not VarIsEmpty(Result) then Result := Unassigned;
end;
end;

procedure FreeCommand(var cmd: OleVariant);
begin
if not VarIsEmpty(cmd) then cmd := Unassigned;
end;

function ConnectToDB(cnn: OleVariant; const srv, uid, pwd, db: string): Boolean;
begin
Result := not VarIsEmpty(cnn);
if Result then
begin
if cnn.State <> adStateClosed then cnn.Close;
cnn.ConnectionString :=
'Provider=SQLOLEDB.1;Persist Security Info=True;Initial Catalog=' +
db + ';Data Source=' + srv + ';Connect Timeout=10;' +
'Use Procedure for Prepare=1';
try
cnn.Open(cnn.ConnectionString, uid, pwd, adConnectUnspecified);
except
Result := False;
end;
end;
end;

function ExecSQL(cnn, rst: OleVariant; const sql: string): Boolean;
begin
Result := not (VarIsEmpty(cnn) or VarIsEmpty(rst)) and (cnn.State = adStateOpen);
if Result then
begin
if rst.State <> adStateClosed then rst.Close;
try
rst.Open(sql, cnn, adOpenStatic, adLockOptimistic, adCmdText);
except
Result := False;
end;
end;
end;

function ExecSQLA(cnn, cmd: OleVariant; const sql: string): Boolean;
begin
Result := not (VarIsEmpty(cnn) or VarIsEmpty(cmd)) and (cnn.State = adStateOpen);
if Result then
begin
cnn.BeginTrans;
try
cmd.ActiveConnection := cnn;
cmd.CommandText := sql;
cmd.Prepared := True;
cmd.Execute;
cnn.CommitTrans;
except
cnn.RollbackTrans;
Result := False;
end;
end;
end;

function ExecProc(cnn, cmd, rst: OleVariant; const prc: string; prms: TParameters_): Boolean;
var
i: Longint;
begin
Result := not (VarIsEmpty(cnn) or VarIsEmpty(cmd)) and (cnn.State = adStateOpen);
if Result then
begin
try
cmd.CommandText := prc;
cmd.CommandType := adCmdStoredProc;
cmd.ActiveConnection := cnn;

//之前,将我们自己的参数集合中的输入参数添入 Command 参数集合
for i := Low(prms) to High(prms) do
case prms.Direction of
//未知类型的参数(adParamUnknown)既不算作输入参数也不算作输出参数
adParamInput, adParamInputOutput:
cmd.Parameters[prms.Name].Value := prms.Value;
end;

rst := cmd.Execute;

//之后,将输出参数从 Command 参数集合读取到我们自己的参数集合
for i := Low(prms) to High(prms) do
case prms.Direction of
//未知类型的参数(adParamUnknown)既不算作输入参数也不算作输出参数
adParamOutput, adParamInputOutput:
prms.Value := cmd.Parameters[prms.Name].Value;
adParamReturnValue:
prms.Value := cmd.Parameters[0].Value;
end;
except
Result := False;
end;
end;
end;

三、访问数据
1、最前 rst.MoveFirst;
2、最后 rst.MoveLast;
3、向前 rst.MovePrevious;
4、向后 rst.MoveNext;
5、取当前记录 rst.Fields[0].Value 或 rst.Fields['字段名'].Value;
6、修改当前记录 rst.Update(rst.Fields[0].Name, 某值);
7、取消修改 rst.CancelUpdate;
8、删除当前记录 rst.Delete(adAffectCurrent);
9、删除所有记录 rst.Delete(adAffectAll);
10、追加记录
rst.AddNew;
rst.Fields[0].Value := 值1;
rst.Fields[1].Value := 值2;
rst.Update;
11、刷新 rst.Refresh;
12、记录数 rst.RecordCount
15、其它方法和属性查 MSDN 或 ADO 的帮助;

四、一些例子

//变量声明
var
cnn, rst, cmd: OleVariant;

//创建对象
procedure TForm1.FormCreate(Sender: TObject);
begin
cnn := CreateConnection;
rst := CreateRecordset;
cmd := CreateCommand;
end;

//释放对象
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeCommand(cmd);
FreeRecordset(rst);
FreeConnection(cnn);
end;

//连接数据库
procedure TForm1.Button1Click(Sender: TObject);
begin
if ConnectToDB(cnn, '127.0.0.1', 'sa', 'ok', 'Northwind') then
Caption := '连接成功'
else Caption := '连接失败';
end;

//取记录
procedure TForm1.Button2Click(Sender: TObject);
begin
if ExecSQL(cnn, rst, 'SELECT * FROM Products') then
Caption := VarToStr(rst.Fields['ProductName'].Value);
end;

//执行存储过程
(*
CREATE PROCEDURE GetProductName
@ProductID int,
@ProductName varchar(50) OUTPUT
AS
SELECT @ProductName = ProductName
FROM Products
WHERE ProductID = @ProductID
RETURN @ProductID
*)
procedure TForm1.Button3Click(Sender: TObject);
var
prms: TParameters_;
begin
SetLength(prms, 3);
//参数 0 接收返回值;Name 取什么都行,但 Direction 必须标明为adParamReturnValue
prms[0].Name := '@RETURN_VALUE';
prms[0].Direction := adParamReturnValue;
//参数 1 作为输入参数,Name 必须与实际参数名相同,Direction 标明 adParamInput
prms[1].Name := '@ProductID';
prms[1].Direction := adParamInput;
prms[1].Value := 8;
//参数 2 作为输出参数,Name 必须与实际参数名相同,Direction 标明 adParamOutput
prms[2].Name := '@ProductName';
prms[2].Direction := adParamOutput;
//执行存储过程;如果不需要 Recordset 对象接收返回的数据集,就把它置空
ExecProc(cnn, cmd, Null, 'GetProductName', prms);
ShowMessage(prms[0].Value);
ShowMessage(prms[2].Value);
end;

五、原生 ADO 与 Delphi ADOExpress 组件的对应关系
1、Connection <=> ADOConnection.ConnectionObject;
2、Recordset <=> ADODataSet.Recordset;
3、Command <=> ADOCommand.CommandObject;
4、? <=> ADOQuery,因为 ADOQuery 根本就不是原生 ADO 对象
5、ExecSQL <=> ADODataSet.Open;
6、ExecSQLA <=> ADOCommand.Execute;
7、有了上面几个其它的就不多说了

六、与数据库结构有关的一些函数
1、动态改变字段名称
uses ComObj;
//Access
//TableName: 表名; OldColName: 原字段名; NewColName: 新字段名;
procedure RenameField(const TableName, OldColName, NewColName: string);
var
DB, Col: OleVariant;
begin
DB := CreateOleObject('ADOX.Catalog');
DB.ActiveConnection := ADOConnection1.ConnectionObject;
Col := CreateOleObject('ADOX.Column');
Col := DB.Tables[TableName].Columns[OldColName];
Col.Name := NewColName;
end;

//SQLServer
procedure RenameField(const TableName, OldColName, NewColName: string);
begin
with ADOCommand1 do
begin
CommandText := 'EXEC sp_rename ''' + TableName + '.' + OldColName +
''',''' + NewColName + ''',''COLUMN'';';
Excute;
end;
end;

2、取得 Access 库中的表结构
type
TTableDef = record
Name,
DateCreated,
LastUpdated,
Description: string;
end;

TTableDefs = array of TTableDef;

procedure GetTableDefs(const DBName: string; out TableDefs: TTableDefs);
var
DBEngine, DB: OleVariant;
I: Longint;
begin
try
DBEngine := CreateOleObject('DAO.DBEngine.36');
DB := DBEngine.OpenDatabase(DBName);
SetLength(TableDefs, Longint(DB.TableDefs.Count));
for I := Low(TableDefs) to High(TableDefs) do
begin
TableDefs.Name := DB.TableDefs.Name;
TableDefs.DateCreated := DB.TableDefs.DateCreated;
TableDefs.LastUpdated := DB.TableDefs.LastUpdated;
try
TableDefs.Description := DB.TableDefs.Properties['Description'].Value;
except
TableDefs.Description := '';
end;
end;
finally
DB := Unassigned;
DBEngine := Unassigned;
end;
end;

3、取得 Access 表中的字段结构
type
TFieldDef = record
Name: string;
Types,
Size: Longint;
Description: string;
end;

TFieldDefs = array of TFieldDef;

procedure GetFieldDefs(const DBName, TableName: string; out FieldDefs: TFieldDefs);
var
DBEngine, DB: OleVariant;
I: Longint;
begin
try
DBEngine := CreateOleObject('DAO.DBEngine.36');
DB := DBEngine.OpenDatabase(DBName);
SetLength(FieldDefs, Longint(DB.TableDefs[TableName].Fields.Count));
for I := Low(FieldDefs) to High(FieldDefs) do
begin
FieldDefs.Name := DB.TableDefs[TableName].Fields.Name;
FieldDefs.Types := DB.TableDefs[TableName].Fields.Type;
FieldDefs.Size := DB.TableDefs[TableName].Fields.Size;
try
FieldDefs.Description := DB.TableDefs[TableName].Fields.Properties['Description'].Value;
except
FieldDefs.Description := '';
end;
end;
finally
DB := Unassigned;
DBEngine := Unassigned;
end;
end;

4、不用 SQL 语句,仅用 ADOX,创建 Access 数据库、表、字段、字段备注(同样适用于 SQLServer)
function CreateMDB(const DBName, TableName: string): Boolean;
var
cat, tbl, col: OleVariant;
begin
Result := True;
try
try
//生成数据库
cat := CreateOleObject('ADOX.Catalog');
cat.Create('Provider=Microsoft.Jet.OLEDB.4.0;Data Source=' + DBName);

//生成表
tbl := CreateOleObject('ADOX.Table');
tbl.ParentCatalog := cat;
tbl.Name := TableName;

//生成一个带备注、自动编号的长整字段
col := CreateOleObject('ADOX.Column');
col.ParentCatalog := cat;
col.Name := '带自动编号的长整字段';
col.Type := adInteger;
col.DefinedSize := 4;
//自动编号字段就是长整字段,仅仅多了下面这句话
col.Properties['Autoincrement'].Value := True;
col.Properties['Description'].Value := '自动编号长整字段的说明';
tbl.Columns.Append(col, adEmpty, 0);
col := Unassigned;
//生成一个带备注的文本字段
col := CreateOleObject('ADOX.Column');
col.ParentCatalog := cat;
col.Name := '文本字段';
col.Type := adVarWChar;
col.DefinedSize := 50;
col.Properties['Description'].Value := '文本字段的说明';
tbl.Columns.Append(col, adEmpty, 0);
//生成一个不带备注的双精字段,比上面要简单的多
tbl.Columns.Append('双精字段', adDouble, 8);
//生成一个不带备注的备注字段
tbl.Columns.Append('备注字段', adLongVarWChar, 16);

cat.Tables.Append(tbl);
except
Result := False;
end;
finally
cat := Unassigned;
tbl := Unassigned;
col := Unassigned;
end;
end;

七、进行分页显示数据
俺们几乎每天都见到网页用 ADO 进行分页(每次只取一定数量的记录),但是在 Delphi 中分页也成了稀罕的东西;
分页有三个常用的属性:PageSize—页面大小,PageCount—页面总数,AbsolutePage—决定跳转到哪一页面。
1、首先取得数据集,不罗嗦了
ADODataSet1.Open....
2、下面这个例子,将指定页面的数据读到 Memo1 中
procedure GetPageData(const nPage: Integer);
var
i: Integer;
begin
wiht ADODataSet1.Recordset do
begin
if (State = adStateClosed) or (nPage < 1) or (nPage > PageCount) then Exit;
Memo1.Clear;
AbsolutePage := nPage;
//这里只能用 for 不能用 while(Eof 仍然是数据集的结尾)
for i := 1 to PageSize do
begin
Memo1.Lines.Add(Fields['ProductName'].Value);
MoveNext;
//防止因最后一页未满而产生越界
if Eof then Exit;
end;
end;
end;

八、其它
1、希望此贴能对朋友们有所帮助,我写的这些都是经过实战检验的;
2、觉得有用的朋友可以试一下,如果觉得没用就当我没写,耽误你时间了。
 
谢谢楼主~
 
顶,向楼主学习。
 
学习学习!谢谢楼主![:D]
 
To vvyang:
能够跟详细一点介绍一些DAO的信息吗!特别是去表中字段的相关属性;如:字段的数据类型,说明,大小,格式等等的。谢谢了
 
收藏!学习学习!谢谢楼主![:D][:D][:D][:D]
 
学习啊!谢谢
 
楼主写的东西我都看完了! 没有耽误我的时间 ^_^
顶! 让更多人知道.
 
To vvyang
能给你的邮件吗?帮我解决一些关于ADO的问题
 
To vvyang
能给你的邮件吗?帮我解决一些关于DAO的问题
 
To vvyang
能给你的邮件吗?帮我解决一些关于DAO的问题.取表中字段的相关属性;如:字段的数据类型,说明,大小,格式等等的。谢谢了

我的联系方法:liangshihong409@126.com
QQ:120513682
 
收藏!学习学习!谢谢楼主!
 
To Lsh1982:
你的问题我的帖子里已有回答,在第六部分的第3小条,你拿过去就能用了。
我的 E-mail:zh5430@yahoo.com.cn
 
数据处理的时候应该优先使用原生ADO,省资源,速度快。
ADO Express控件只是为了和Delphi的数据感知控件合用的。
 
楼主够大方,藏贴!![:)]
 
To vvyang:
不行的,我试过了,我把我的问题传给你好了(我是在delphi6下做的,用了控件diamondAccess),如果你没有,也可以在deliphi7下做(可以不用控件DiamondAccess)。现在我希望我的字段的相关属性(类型,说明,大小,是否为空,索引等等Access数据库中的常规项我希望都可以显示出来)可以显示出来,你帮我解决一下,谢谢了!
 
谢谢楼主了,有时间研究一下!
 
后退
顶部