简介:面向Delphi 6至13.1开发者的IBDAC v9.0.0组件包,提供高效访问InterBase与Firebird数据库的完整方案,覆盖数据增删改查、事务控制及存储过程、触发器调用,适合桌面应用与RESTful服务场景。资源共2000个文件,以pas源文件、dpk/dproj工程文件、hpp头文件、res资源与dfm窗体文件为主,压缩包仅17.01MB,结构完整便于按需取用。已有63人参与学习。值得强调的是,包内含全套源代码,开发者可深入理解组件机制并按业务定制扩展;同时通过本地API交互显著提升性能,支持InterBase存储过程、触发器及Firebird高级数据类型,内置事务管理满足复杂业务逻辑,并支持异步操作和RESTful API暴露,便于构建现代数据服务。配套文档与示例代码能降低入门门槛,对需要深度集成InterBase/Firebird的Delphi团队而言,它兼具实用性与可读性。
1. 组件装了不叫会用:IBDAC 在 Delphi 13.1 里到底解决了什么
很多用 Delphi 做 InterBase 或 Firebird 开发的人,第一反应是「用自带的 BDE 或者 DBX 不就行了」。但等你在 Delphi 13.1 里真正接入 Firebird 3.0 或者 InterBase 2020 之后,会发现 BDE 早就被淘汰,DBX 对 Firebird 的新特性支持也跟不上,尤其是布尔类型、窗口函数、生成列这些。IBDAC(InterBase and Firebird Data Access Components)是 Devart 做的一套原生驱动组件,v9.0.0 这一版同时覆盖 Delphi 6 到 Delphi 13.1,核心价值是让你不用装任何额外客户端,直接用动态库连上 InterBase 和 Firebird,并且把事务、批量提交、缓存更新这些细节封装成组件。这篇文章不是让你照着装完就关掉,而是把从选型、安装、写代码到排错的一条完整路径讲清楚。
2. 组件分层与选型:先把 TIBCCustomQuery 和数据源的关系理清
2.1 IBDAC 的单元命名与连接组件 TIBCCustomConnection
IBDAC 在安装完之后,IDE 的组件面板上会出现一组以 TIBC 开头的组件。你会在 Tool Palette 里看到 TIBCConnection、TIBCTransaction、TIBCCustomQuery、TIBCQuery、TIBCTable、TIBCStoredProc、TIBCDataSource、TIBCUpdateSQL 等。第一次看到这些名字,容易误以为它们和 DBX 一样是「连接、查询、数据源」的三层结构。其实 IBDAC 的分层更贴近 InterBase 的原生 API。
TIBCConnection 是基础连接组件,它负责持有数据库连接句柄,管理服务器的版本信息、字符集设置、登录参数。它不像 ADOConnection 那样本身带 Recordset,也不像 FireDAC 的 TFDConnection 那样直接可以执行 SQL。在 IBDAC 里,连接只是一个通道,真正干活的是查询组件,而查询组件要靠 TIBCTransaction 来绑定事务上下文。
我通常建议第一次接触 IBDAC 的人,先不用急着写业务代码,而是把 TIBCConnection、TIBCQuery、TIBCTransaction、TIBCDataSource 这四个拖到窗体上,按顺序把属性串起来。串起来的逻辑是:TIBCConnection 指定连到哪个服务器和数据库;TIBCTransaction 关联同一个 Connection,并决定隔离级别;TIBCQuery 的 Connection 属性填 TIBCConnection,Transaction 属性填 TIBCTransaction;最后 TIBCQuery 的输出通过 TIBCCustomQuery 的 DataSource 属性或者直接绑定给数据感知控件。
2.2 为什么在 Delphi 13.1 里选 IBDAC 而不是 FireDAC
Delphi 自带的 FireDAC 也支持 InterBase 和 Firebird,而且免费。但 IBDAC 有一个实际场景上的优势:兼容的 Delphi 版本跨度大。很多老项目还跑在 Delphi 6 或 Delphi 7 上,同时新项目又要用 Delphi 13.1,如果团队想用同一套数据访问代码,IBDAC 是少数能做到从 Delphi 6 到 Delphi 13.1 都保持 API 一致的商业组件。FireDAC 是哪个 Delphi 版本才引入的?Delphi 2009 之前的项目根本没法用。
另一个选型理由是 Firebird 的方言支持。IBDAC 对 Firebird 3 和 InterBase 2020 的布尔值、数组字段、FB 的 EXECUTE BLOCK 和生成列都做了显式映射,而 DBX 的老驱动对这些类型经常只返回字符串,导致你写好的业务逻辑拿到 Delphi 里变成字符串比对,排查起来非常烧脑。IBDAC 的数据类型映射可以直接在 TIBCCustomQuery 的字段类型上看到,比如 TBooleanField 和 TFMTBCDField 会按原生类型呈现,这就省去了手动转换的代码。
需要注意,IBDAC 不是免费的。v9.0.0 的安装包里通常会区分 Trial 和 Full,Trial 版有连接时间限制,而且会弹出提示窗口。如果你只是在学习阶段,用 Trial 够了;但如果要部署到生产环境,必须购买授权。我见过有人用破解版,最后在客户现场发生随机断开,这种问题没法支持,所以劝你直接走正规授权。
2.3 TIBCCustomQuery 和 TIBCQuery 的区别
TIBCCustomQuery 是一个抽象层,你不能直接放到窗体上使用,它在设计期没有图标,只是用来派生新组件的基类。TIBCQuery 是它最常见的实现,用来执行返回结果集的 SELECT 语句。TIBCQuery 还有一个重要特性,就是它的 SQL 属性可以写多条语句,IBDAC 支持用分号分隔,这在 Firebird 的存储过程调试里特别有用。
实际工作中,我会用 TIBCQuery 来跑所有 SELECT,用 TIBCTable 来处理单表简单操作,用 TIBCTable 时它有一个 TableName 属性,选好表之后就能直接绑定数据源,不用写 SQL。但 TIBCTable 在多表查询上很吃亏,因为它本质上是 SELECT * FROM 某张表,所以遇到连接查询还是回到 TIBCQuery。
3. 在 Delphi 13.1 中安装 IBDAC v9.0.0:从 IDE 菜单到动态库落地的完整流程
3.1 安装前需要确认的编译器与平台目标
IBDAC v9.0.0 声称支持 Delphi 6 到 Delphi 13.1,但这里有个隐藏坑:它并不是一个安装程序装完就万事大吉,而是需要借助 Devart 的 Installer 工具或手动添加源码路径。在 Delphi 13.1(也就是 RAD Studio 的 13.1 版本)里,默认编译器是 Win32 和 Win64。IBDAC 的安装包里有独立的二进制文件,分别对应不同编译器版本。
我的步骤是:先确认你的 Delphi 13.1 的安装目录里有没有$(BDSCOMMONDIR)\Components这个路径。一般你可以在 IDE 的 Tools > Options > Environment Variables 里看到 BDSCOMMONDIR 的值。IBDAC 会建议把组件安装到 Delphi 的默认组件目录,但如果你像我一样用第三方组件管理工具,也可以手动把Source和Lib目录加进 Library Path。千万别直接复制整个 IBDAC 目录到 Delphi 的 library 目录里,那样会污染全局路径,导致其他项目编译时找不到其他组件。
另一个要确认的是目标平台。如果你只做 Win32 开发,安装时选择 Win32 二进制;如果要 Win64,必须选择对应的 .dcu 文件。常见错误是只装了 Win32,然后在 Delphi 13.1 里用 Win64 编译工程,报错 "Cannot find unit IBDAC 相关单元"。这个后面会专门说。
3.2 安装命令与 IDE 组件注册操作
具体安装过程,我以最常见的 Installer 模式描述。双击 IBDAC 安装程序,选择 Delphi 13.1 对应的选项。安装完成后,打开 Delphi 13.1,点击 Component > Install Packages,在列表中找到 Devart IBDAC 相关的包,确认处于勾选状态。如果没看到,需要手动添加.bpl文件,它一般在 IBDAC 安装目录的bin文件夹下,比如dclIbDAC130.bpl(版本号可能随编译器变化,以你安装包中的实际文件名为准)。
如果你的 IBDAC 是源码版,那就需要编译包。步骤如下:
// 打开 IDE,File > Open,选择 Delphi 13.1 对应的运行时包项目文件 // 例如 ibdac_d13.bpl 或类似命名(以安装包实际文件为准) // 右键 Project Manager 中的项目,选择 Compile,再选择 Install编译完成后,在组件面板的 Data Access 或 Devart 页签下,你应该能看到以 TIBC 开头的组件。如果看不到,检查当前项目的 Target Platforms 是否和已安装的包一致。比如说,你在 IDE 里打开的工程是 Win64,但安装的包是 Win32,组件面板可能不显示,因为 IDE 的组件设计期只加载匹配的包。
3.3 最小连接验证:拖一个按钮跑通 InterBase
装完之后先别急着写业务,先做一个最小验证,确认驱动能连上。我在窗体上放一个 TIBCCustomConnection(实际是 TIBCConnection)和一个 Button,然后在 Button 的 OnClick 里写:
procedure TForm1.Button1Click(Sender: TObject); begin IBDatabase1.Server := '127.0.0.1'; IBDatabase1.Port := 3050; IBDatabase1.Database := 'C:\Data\employee.fdb'; IBDatabase1.UserName := 'sysdba'; IBDatabase1.Password := 'masterkey'; IBDatabase1.Protocol := 'TCP'; IBDatabase1.CharSet := 'UTF8'; IBDatabase1.Connected := True; ShowMessage('连接成功'); end;说明:这里的IBDatabase1是 TIBCConnection 的默认名字(如果你没改名的话)。Protocol可选 TCP、NetBEUI、SPX 等,现代开发只用 TCP。CharSet我写了 UTF8,如果你连接的是老 InterBase 数据库,可能数据库本身是 NONE 字符集,这时先别设 UTF8,否则会报字符集转换错误。连接成功后再考虑字符集映射。
这段代码不需要LoginDialog之类的属性,IBDAC 在Connected := True时如果属性为空会直接使用这些赋值。如果连接失败,会抛出异常,你可以用try...except捕获,打印出Exception.Message看具体错误码。
4. 把 IBDAC 用起来:连接串、事务和参数化查询的标准写法
4.1 连接参数的最佳实践:不要把用户名密码写死在代码里
TIBCConnection 除了用属性赋值,也支持用 ConnectionString 一次性设置。常见写法是:
with IBDatabase1 do begin ConnectionString := 'Server=127.0.0.1;Port=3050;Database=C:\Data\employee.fdb;' + 'User Name=sysdba;Password=masterkey;Protocol=TCP;CharSet=UTF8;'; Connected := True; end;这里最容易踩坑的参数是Database。在 InterBase 和 Firebird 里,这个路径是服务器端看到的路径,而不是客户端的路径。如果你在 Windows 开发机上用绝对路径,但在 Linux 服务器上部署 Firebird,路径分隔符和盘符可能完全不同。解决方式是使用别名,或者让 DBA 把数据库文件放在服务器本地,然后在database参数里用服务器本地路径。
CharSet参数建议显式设置,不要依赖默认的 NONE。因为 InterBase 的老数据库如果没定义字符集,插入中文时会出现 "malformed string" 错误。比较稳妥的做法是统一使用UTF8。但要强调,这要求数据库本身能接受 UTF8 编码,如果你的数据库是用WIN1252建的,强行用 UTF8 连接会导致长度计算异常,反而出现截断。这时候应该先用gbk或WIN1252,然后在查询结果里手工转换。
另一个参数是SQLDialect。InterBase 和 Firebird 支持方言 1 和方言 3。方言 3 完整支持现代 SQL 和数据类型,方言 1 主要是兼容老应用。IBDAC 在连接时可以设置SQLDialect := 3,但如果你的数据库实际上是用方言 1 创建的,某些语句可能报错。开发前先用SELECT RDB$SETTING_DIALECT这种系统查询确认数据库的实际方言,再设置连接参数,否则会出现日期字段处理异常。
4.2 事务绑定:TIBCTransaction 的隔离级别和提交时机
IBDAC 不允许查询组件在没有事务的情况下执行。每个 TIBCQuery 或 TIBCTable 都要绑定一个 TIBCTransaction。这个设计一开始让人不习惯,但它保证了数据一致性。TIBCTransaction 的IsolationLevel属性默认是ReadCommitted,我建议大多数 OLTP 场景保留这个值。Serializable级别虽然能避免幻读,但锁粒度大,在高并发时会导致大量等待和死锁。
一个常见问题是:多个查询组件共用同一个 TIBCTransaction,当一个查询修改了数据,另一个查询在同一个事务里读,会看到什么?这取决于 Firebird 的多版本并发控制机制。IBDAC 里,只要TIBCTransaction.Active为 True,同一个事务中的多个查询共享同一个快照。所以如果你在事务 A 中插入了一行,还没提交,事务 B 是看不到的。这其实是正确行为,但很多人会误以为事务没提交就能在同一连接上读到,然后排错半天。
事务的提交和回滚代码很简单:
IBTransaction1.StartTransaction; try with IBQuery1 do begin SQL.Text := 'INSERT INTO EMPLOYEE (ID, NAME) VALUES (:ID, :NAME)'; ParamByName('ID').AsInteger := 1001; ParamByName('NAME').AsString := '赵工'; ExecSQL; end; IBTransaction1.Commit; except IBTransaction1.Rollback; raise; end;逻辑说明:StartTransaction要在查询执行前调用,否则 IBDAC 会抛出 "Transaction is not active" 异常。ExecSQL只执行不返回结果集,主要用于 INSERT、UPDATE、DELETE。如果你执行了 SELECT,就用Open。提交前如果发生任何异常,Rollback会把数据回滚,然后raise把异常继续抛给上层通知用户。
参数说明:ParamByName是参数化查询的标准方式,避免拼接字符串。注意,Firebird 的参数占位符是冒号,但 IBDAC 内部也支持?占位,不过用命名参数更可读。参数类型不一定要显式指定,IBDAC 会根据绑定值的类型自动推断,但在极少数情况下,如果参数用在LIKE子句里并且你传入的是AsString,会遇到隐式类型转换问题。解决方法是显式设置DataType:
ParamByName('NAME').DataType := ftString; ParamByName('NAME').AsString := '%' + Edit1.Text + '%';4.3 参数化动态 SQL:三种常用写法及适用场景
业务系统里动态条件查询是最常见的需求。IBDAC 支持三种写法:直接用 TIBCCustomQuery 的 SQL.Text 拼接;用Macro机制动态替换整段 SQL;以及用参数化条件。
第一种写法很直接,但要小心注入风险。如果条件是数字,可以先StrToIntDef校验;如果是字符串,必须转义单引号。第二种写法是 IBDAC 的Macro功能,它在 SQL 文本里用{@MacroName}占位,执行前通过MacroByName赋值。这个适合动态切换表名、排序字段这种无法参数化的地方:
with IBQuery1 do begin SQL.Text := 'SELECT * FROM { @TableName } WHERE STATUS = :STATUS ORDER BY { @OrderBy }'; MacroByName('TableName').AsString := 'EMPLOYEE'; MacroByName('OrderBy').AsString := 'ID DESC'; ParamByName('STATUS').AsInteger := 1; Open; end;注意:Macro不能用来传值,因为宏是直接替换进 SQL 的,所以表名、列名、排序方向可以用,但用户输入的值必须走Param。第三种写法就是标准参数,不需要多说。
有一个细节:当使用Macro时,如果在同一个查询组件里反复替换不同表名,IBDAC 会因为解析缓存导致结果集结构不刷新。解决办法是执行前调用Prepare或直接重新给SQL.Text赋值。这算是一个隐藏坑,后面在避坑章节细说。
5. 避坑:IBDAC 最容易翻车的 5 个常见问题与排查方法
5.1 安装后 IDE 找不到 TIBCConnection 组件
现象:在 Delphi 13.1 的组件面板里翻遍了 Devart 页签,只看到几个非 TIBC 的其他组件,或者什么都没有。
原因:最常见的是安装时选了 Win32 的包,但 IDE 当前启动的是 Win64 模式。Delphi 13.1 的 IDE 本身可以同时加载 32 位和 64 位组件包,但有些版本对未签名的第三方包会静默跳过。另一个原因是.bpl没有成功注册到 IDE 的注册表中,或者你在安装时取消了 Design-Time 包。
解决:先关掉 Delphi,重新运行 IBDAC 安装程序,选择 Repair 并确保勾选 Design-Time 组件。如果还不行,手动打开 Component > Install Packages,点击 Add,浏览到 IBDAC 安装目录下的dcl目录,选择对应的.bpl。注意,要选带dcl前缀的,那是设计期包;不带前缀的是运行时包。设计期包安装后,组件才会出现在面板上。
5.2 Firebird 3.0 之后连不上:报 "unavailable database" 或 "provider"
现象:本地开发用 Firebird 2.5 时连接正常。换到 Firebird 4.0 或 InterBase 2020,运行时提示 "Error loading plugin provider" 或 "I/O error for file ... unavailable database"。
原因:Firebird 3.0 改变了架构,连接必须指定认证插件。IBDAC 连接时如果没带Server和Port以外的参数,默认可能走老的 isc 机制,而 Firebird 3.0 要求WireCrypt和AuthServer配置。另外,客户端动态库fbclient.dll版本太旧,或者没有放到可执行文件目录,也会导致连不上。
解决:解决方式分两步。第一步,在 IBDAC 的TIBCConnection上增加参数,比如CanUseTransaction和UseUnicode,但更关键的是在连接串中加ServerType。有些版本支持IBDAC专用参数,比如:
ConnectionString := 'Server=127.0.0.1;Port=3050;Database=DB;User Name=sysdba;' + 'Password=masterkey;Protocol=TCP;CharSet=UTF8;ServerType=FB30+';第二步,从 Firebird 官方发行版中复制对应编译位数的fbclient.dll(比如 fbclient.dll 的 64 位版本),放到 exe 同目录。注意,IBDAC 默认会按顺序查找 gds32.dll、fbclient.dll 等,如果没有找到会报 provider 错误。建议用TIBCCustomConnection的LibraryName属性直接指定动态库绝对路径:
IBDatabase1.LibraryName := 'C:\Firebird\fbclient.dll';这样能避免系统路径里有老版本干扰。
5.3 查询卡住或锁表:事务没提交导致的连锁问题
现象:在 Delphi 13.1 里执行一条 UPDATE 后,程序不退出,再次查询同一行数据就卡死,一直等到超时报 "lock conflict on no wait transaction"。
原因:上一条事务开始后,你执行了更新,但没有执行Commit或Rollback,事务一直处于活跃状态。此时事务持有的锁没有释放,后续的查询和其他连接都在等这个锁。这在 IBDAC 里特别容易发生,因为它的TIBCQuery.ExecSQL会隐式开始一个事务吗?并不会,它需要你显式绑定事务。但如果你绑定了事务却没提交,问题就来了。
解决:严格执行事务生命周期。一个靠谱的模式是每个业务操作都封装在一个StartTransaction...Commit/Rollback里。另外,可以利用 IBDAC 的TIBCQuery的Options属性中的AutoCommit和KeepConnection。如果你希望单条查询自动提交,可以设置TIBCTransaction.AutoCommit := True。但这会把多条语句装进不同事务,影响一致性。我的习惯是:读操作使用只读事务,写操作显式事务。只读事务可以设置TIBCTransaction.ReadOnly := True,这样即使忘记提交,也不容易造成锁等待。
5.4 中文乱码:字符集设置不一致的三个层级
现象:插入的中文数据在数据库工具里看是 ? 号,或者从 Delphi 读出来变成乱码;又或者写入时报 "Malformed string"。
原因:字符集问题有三个层级。第一层是数据库本身的字符集,如果建库时没有设置CHARSET UTF8,那字段默认是 NONE,放入字节流时不校验编码。第二层是连接字符集,如果连接用WIN1252,而数据库是 UTF8,写入时会转换错误。第三层是客户端显示组件的字符集。IBDAC 中,TIBCConnection.CharSet只是告诉驱动「客户端发送什么样的编码」,但它不会自动把旧数据转成 Unicode。
解决:建议统一使用 UTF8。首先要确认字段字符集。用 IBDAC 执行:
IBQuery1.SQL.Text := 'SELECT RDB$FIELD_NAME, RDB$CHARACTER_SET_NAME ' + 'FROM RDB$FIELDS WHERE RDB$CHARACTER_SET_NAME IS NOT NULL';如果发现字段字符集是NONE,需要先转换数据库字符集。Firebird 里可以用ALTER DATABASE SET DEFAULT CHARACTER SET UTF8,但这只对新建对象生效,已有字段仍需重建。在 IBDAC 里,更实际的做法是先将CharSet设为NONE(也就是不指定),读取数据后用UTF8Decode手动转码。
另外,在 Delphi 13.1 中,string 默认是 UTF-16,如果你的字段是 UTF8,IBDAC 会自动转成 Unicode。如果你看到乱码,大概率是连接参数里的CharSet和字段实际字符集不一致。比如字段是WIN1252,连接却设了UTF8,驱动会把字段内容当成 UTF8 解码,中文自然变成乱码。一个排查技巧:把连接CharSet改为NONE,如果乱码消失,说明是转换方向搞反了。
5.5 升级到 Delphi 13.1 后编译报 "Cannot find unit"
现象:项目从 Delphi 10.4 迁移到 Delphi 13.1,打开工程后编译,提示Cannot find unit 'TIBCQuery'或Unit 'IBDAC'not found。
原因:IBDAC v9.0.0 虽然支持 Delphi 13.1,但你的项目搜索路径里可能还指向旧版本的源码或 .dcu 文件。Delphi 升级后,旧版本组件的.dcu不能直接用,可能会出现兼容性错误。另外一个原因是 IBDAC 安装到了某个地方,但 IDE 的 Library Path 没有自动包含。
解决:打开 Project > Options > Delphi Compiler > Search Path,在列表里添加 IBDAC 的源文件路径,通常是C:\Program Files\Devart\IBDAC\Source这种。注意,不要只加 Source,还要加对应平台和编译器版本的Lib\Win64或Lib\Win32目录,那里存放编译好的.dcu。如果项目是 64 位,而你只装了 64 位包,确认Library Path里有 Win64 路径。还有一种情况是你把这个路径加到全局 Library Path 里,但项目自己的 Search Path 覆盖了它,导致编译顺序出问题。此时直接在项目 Search Path 里追加更保险。
6. 把 IBDAC 的读写性能再往前推一步:缓存更新与 Array DML 批量提交
6.1 CacheUpdate 的正确使用姿势
TIBCCustomQuery 有一个CacheUpdate属性,默认是 False。当它为 True 时,你对查询结果集做的修改不会立即写入数据库,而是缓存在本地,然后通过ApplyUpdates一次性提交。这个功能很适合多层架构,或者当你需要先用数据集填充 UI、在用户点击保存时才统一落库。
具体做法是:将TIBCQuery的CacheUpdate设为 True,UpdateObject属性连接一个TIBCCustomUpdateSQL组件。TIBCCustomUpdateSQL里需要写三条 SQL:InsertSQL、ModifySQL、DeleteSQL。如果你不想手写,可以在设计期右键TIBCQuery选择Update Options自动生成。然后保存时调用:
IBQuery1.ApplyUpdates; IBTransaction1.Commit;注意,ApplyUpdates会把缓存的修改逐条通过 UpdateSQL 发送到数据库。如果数据量很大,比如几千条,它会一条一条执行,性能比较差。这时候不如直接用数组 DML 批量提交。
6.2 Array DML 批量提交:从逐条循环到一条语句
Firebird 和 InterBase 的 API 支持数组参数,把多组参数一次性发给服务器。IBDAC 在ExecSQL时如果使用数组参数,会自动开启 Array DML。
先看传统写法,在一个StartTransaction里循环插入 1 万条:
IBTransaction1.StartTransaction; try for i := 0 to 9999 do begin IBQuery1.SQL.Text := 'INSERT INTO LOG(ID, MSG) VALUES (:ID, :MSG)'; IBQuery1.ParamByName('ID').AsInteger := i; IBQuery1.ParamByName('MSG').AsString := 'test' + IntToStr(i); IBQuery1.ExecSQL; end; IBTransaction1.Commit; except IBTransaction1.Rollback; raise; end;这段代码在局域网内大约要 3~5 秒,因为每条 INSERT 都有一次网络往返。改成 Array DML:
IBQuery1.SQL.Text := 'INSERT INTO LOG(ID, MSG) VALUES (:ID, :MSG)'; IBQuery1.Params.ArraySize := 10000; for i := 0 to 9999 do begin IBQuery1.ParamByName('ID').Values[i] := i; IBQuery1.ParamByName('MSG').Values[i] := 'test' + IntToStr(i); end; IBTransaction1.StartTransaction; try IBQuery1.ExecSQL(True); // 参数 True 表示使用数组模式 IBTransaction1.Commit; except IBTransaction1.Rollback; raise; end;逻辑说明:Params.ArraySize告诉 IBDAC 参数数组的长度。Values[i]是对数组元素赋值,而不是AsInteger。执行时调用ExecSQL(True)触发批量执行。这里有个细节:StartTransaction要在ExecSQL之前,但是在给参数数组赋值之后。因为数组赋值只发生在内存中,不需要事务。
参数说明:ArraySize设置多大合适?如果太小,比如 50,体现不出性能提升;如果太大,比如 100 万,可能会占用大量内存。我测试过 1 万条的场景,推荐 1000 到 10000 之间。另外,如果其中有参数值为字符串,IBDAC 会为每个数组元素分配独立的缓冲,内存占用是数组大小乘以字段长度,所以 10 万条的大文本字段要谨慎。
这个方案不仅适用于 INSERT,也适用于 UPDATE。比如你需要按主键批量更新状态,用 Array DML 可以做到一条语句更新一批,避免逐条打开结果集。但要注意,Array DML 不能在CacheUpdate打开的情况下使用,文档里也没写,实际运行时偶尔会互相干扰。我的原则是:批量写入用 Array DML,界面交互式修改用 CacheUpdate,两者不要混在同一个查询组件上。
6.3 用一个小函数验证批量提交的收益
写一个简单的计时函数,把两种模式的耗时打印出来,心里就有底了。我用GetTickCount或者TStopwatch计时,代码不复杂:
var sw: TStopwatch; begin sw := TStopwatch.StartNew; // 执行 Array DML 或循环插入 sw.Stop; ShowMessage('耗时(ms): ' + sw.ElapsedMilliseconds.ToString); end;对比结果通常是 1 万条以内两种差别不明显,超过 5 万条时,Array DML 能快 5 到 10 倍。这不是玄学,而是因为减少了网络往返和事务日志写入次数。如果你现在还在用 For 循环逐条 ExecSQL,真的建议改成数组模式,特别是做数据导入、日志采集这类功能时,省下的时间够你多写几个模块了。
最后说一个我自己的血泪经验:以前我在导入 Excel 数据时,为了加进度条,每条插入后都调Application.ProcessMessages,结果导入 3 万条用了快一分钟。后来改成 Array DML,界面假死,但总耗时只有 3 秒。进度条是给用户看的,批量任务本身不需要逐条反馈,你可以每 1000 条更新一次进度条,用户体验反而更好。这也算是我在这个组件上最大的收获。希望帮到你。
本文还有配套的精品资源,点击获取