ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

力控组态软件连接Access数据库:ODBC驱动配置与32/64位排查实战

力控组态软件连接Access数据库:ODBC驱动配置与32/64位排查实战

简介:力控组态软件与Access数据库的数据交互PDF教程,面向工业自动化、过程控制领域的组态工程师、上位机开发人员及高职院校相关专业学生,帮助解决力控工程连接并操作Access数据库的常见需求。资源仅含1个PDF文档,压缩包大小1.85MB,内容紧凑但步骤完整,适合快速上手与查阅,已有484人学习下载。教程从力控新建工程和Access建表讲起,涵盖字段设计、中间变量建立、数据表绑定,以及SQLConnect、SQLInsert、SQLDisconnect三个函数的参数含义与脚本写法,并专门提醒数据源名称一致性、语句末尾分号等易错细节;同时给出按钮动作脚本编写、编译运行与数据库落地验证的完整闭环,读者可以按步骤复现,最终将力控变量成功写入Access数据库。对于不熟悉数据库交互或需要排错思路的工程师,这份资料能显著缩短摸索时间,可直接作为项目报表与记录功能开发的参考笔记。

1. 力控与 Access 数据库:这个组合值不值得用,先看数据规模

把力控组态软件和 Access 数据库接在一起,是中小型自控项目里很常见的需求。力控负责采集现场数据,Access 负责把这些数据沉淀下来,供日报表、班长查询或第三方系统读取。有人一听 Access 就皱眉,觉得它不够"专业",但在几十个监控点的车间里,Access 文件式部署反而比 MySQL 更省事,不用装独立服务,拷贝即备份,配 ODBC 就能读写。不过这个组合的难点不在 SQL,而在"位宽"——32 位力控进程配 64 位 ODBC 驱动,往往一卡就是一天。下面按我常用的落地顺序,把从建库到 DSN、从脚本入库到排错的完整路径捋一遍,适合正在做力控项目的工程师,也适合想评估这个方向的选型人员。

2. 力控与 Access 的数据链路:ODBC 是中间人,选型先看清位宽

2.1 力控历史数据的短板,Access 为什么能补位

力控自带历史数据库,能记录模拟量、开关量的变化趋势。历史数据默认落在工程目录下的私有文件里,文件名和时间段绑定,力控的趋势控件能读,但工程师想拿 Excel 做二次透视、让第三方报表工具直接读取,就非常别扭。私有格式还是个黑匣子,现场调试时想查一段异常数据,要么打开力控的历史曲线,要么写脚本导出,效率和直读数据库差很多。

Access 补位的方式是承担"对外读写的窗口"这个角色。力控的采集脚本把关键变量写入 Access 表,报表系统、上位系统再通过 ODBC/JDBC 读这张表。这样数据链路清晰,力控只管写,报表只管读。几十个监控点、每天几千条记录的场景下,Access 的查询响应基本无感。更重要的,Access 是文件型数据库,不需要后台服务,工控机重装系统后,把备份的 mdb 文件复制回去,历史数据就回来了,省去数据库运维成本。

但这里要修一条边界:秒级写入会迅速把表膨胀到百万行。Access 在处理超过几十万条记录时,SELECT不带索引会明显变慢。所以我的方案往往留两层:秒级原始值进力控自带历史库,分钟级均值进 Access。这样 Access 的数据量可控,报表查询也稳。常见的定时任务里,用累加器统计一分钟内的均值、最大值和最小值,再统一写一条记录。这个取舍是选型阶段就要定的,别等到数据跑起来再改。

2.2 ODBC 驱动与 32/64 位数据源的错位

Access 的读写依赖 Microsoft Access Database Engine,也就是 ACE 驱动,ODBC 管理器会把它暴露成"Microsoft Access Driver (*.mdb, *.accdb)"。这个驱动分 32 位和 64 位两版。问题在于力控的开发系统和运行系统通常是 32 位进程,而 Windows 控制面板里的"ODBC 数据源(64 位)"是默认入口。你用 64 位管理器创建的 DSN,32 位力控根本看不到,于是运行时就会冒出"请先安装 access 数据库 64 位系统驱动程序"这类提示,严重误导排查方向。

很多人在这步被卡半天,不是因为没装驱动,而是 ODBC 管理器入口用错了。64 位系统上,32 位 ODBC 管理器藏得较深,路径是C:\Windows\SysWOW64\odbcad32.exe。你可以在"运行"框直接输入这个路径打开它,看到的才是 32 位进程能识别的 DSN 列表。同样的驱动在 32 位和 64 位管理器里都能注册,但它们属于不同的注册表节点,互不通用。如果你打开的是 64 位管理器,里面对应的注册表路径是HKLM\SOFTWARE\ODBC;而 32 位管理器走的是HKLM\SOFTWARE\WOW6432Node\ODBC。这就是驱动装了、DSN 却消失的底层原因。

在实际项目中,我会先确认力控进程位数。在任务管理器里找到力控的运行程序,带"32 位"字样就要走 32 位链路。另外,有些力控版本在工具栏里有"环境诊断"界面,能直接显示当前运行模式的位数。如果现场装过多个驱动版本,出现 ACE 驱动重复、条目混乱的情况,最干净的做法是把所有 Access Database Engine 卸载重装,并记录安装日志。不要同时保留旧版 Jet 驱动和 ACE 驱动去测试,那样只会增加排查复杂度。

2.3 单机、C/S 与文件共享的边界

选型时还要想清楚部署模式。力控作为上位机组态软件,通常跑在一台工控机上,采集 PLC、仪表的数据,Access 文件存放在本机就是单机模式。这种模式下,唯一的 DB 客户端就是力控进程,没有并发写冲突,Access 的锁机制完全够用。如果项目要求办公网里的电脑也能看历史数据,可以开放文件共享让报表电脑只读访问,但只读访问同样要避免多人同时打开,否则 Access 会生成锁定文件ldb,造成整个库的只读。

如果两台力控分别采集不同区域,还要同时写同一个 Access 文件,那就得换思路了。Access 的锁定粒度是整表或整页,两条写事务可能互相阻塞,一旦某个客户端异常退出,锁文件残留会导致其他客户端一直读不了库。这种场景在工程现场经常被低估,等到交接班报表同时读取时才发现性能崩盘。我的判断标准是:写入进程超过一个,就直接用 SQL Server 或 MySQL Community;只有单写入进程时,Access 才是安全的。

2.4 连接方式小结:DSN、无 DSN 和中间层

力控读写 Access 的常见方式有三种:一是配置系统 DSN,然后通过DSN=名称连接,适合固定现场;二是无 DSN 连接字符串,直接在脚本里写驱动和路径,省去管理器配置,适合开发和测试;三是通过中间层程序(比如 C# 写的服务)把数据写入 Access,力控只和中间层交互,适合复杂的业务逻辑。前两种都依赖 ODBC,第三种增加了进程开销,但能把写入和查询规范化。下面以 DSN 方式为主展开,因为它最直观,排错也容易隔离。

3. 从建库到 DSN:把 Access 文件变成力控看得见的"数据源"

3.1 建库与建表:字段类型和命名直接影响后期脚本

第一步是准备一个.mdb文件。打开 Microsoft Access,新建空白数据库,命名为ForceData.mdb,保存到C:\ForceData\这样的纯英文路径。如果现场没有安装完整版 Office,可以装一个 Access Runtime,也能满足建库和日常打开。.mdb比.accdb的兼容性更广,力控的低版本和旧报表组件都能认,所以除非你明确要使用.accdb的新特性,否则我建议选用.mdb。

建表有两种方式:在表设计视图里手点,或者在查询视图里跑一段 SQL。用 SQL 建表的优点是字段定义可以保存到脚本仓库,现场重装时能快速重建。以历史数据表为例:

CREATE TABLE HistoryData ( ID AUTOINCREMENT PRIMARY KEY, TagName TEXT(50) NOT NULL, TagValue DOUBLE NOT NULL, CreateTime DATETIME NOT NULL );

AUTOINCREMENT是 Access 的自动编号,作为主键能帮我们在查询最新记录时按ORDER BY ID DESC取数。TEXT(50)限制标签名长度,避免脏数据写坏表格。DOUBLE存浮点数,如果是整数型的开关量,也建议用 DOUBLE 统一,省得力控脚本里频繁做类型转换。DATETIME对应力控的时间字符串,要注意时区和格式统一。

字段命名的坑集中在空格、中文和保留字上。比如把时间字段命名为Date,部分查询工具会把它识别为日期函数,最好用CreateTime、SampleTime。标签名如果在业务里本身就是中文(比如1#炉温度),写入表时尽量把它映射成Furnace1_Temp这样的英文别名,报表显示时再做中文映射。这样力控脚本拼接 SQL 时不用处理中英文括号,Access 表结构也更稳定。

3.2 用 32 位 ODBC 管理器创建系统 DSN

接下来是创建系统 DSN。这一步的关键是找到 32 位 ODBC 管理器。在 64 位 Windows 上,打开C:\Windows\SysWOW64\odbcad32.exe。如果点击后弹出的是"32 位 ODBC 数据源管理器",就对了。进入后切到"系统 DSN"选项卡。系统 DSN 对所有用户可见,比用户 DSN 更适合服务型的力控进程。

创建步骤:

  1. 点击"添加",从驱动列表里找到Microsoft Access Driver (*.mdb, *.accdb)。如果列表里没有,先安装 Access Database Engine 2016 Redistributable,再重新打开管理器。
  2. 在"数据源名称"里填ForceData,不要用中文和空格。描述可留空。
  3. 点击"选择",定位到C:\ForceData\ForceData.mdb。
  4. 保持默认的连接设置,点击"测试连接"。
  5. 测试成功后,点确定保存 DSN。

这里容易出现两个分支。如果你看到驱动列表里有Microsoft Access Driver (*.mdb),但没有*.accdb条目,说明系统装的是旧版 Jet 驱动,只能读.mdb文件,无法读.accdb。而如果你只有一个同时包含*.mdb, *.accdb的 ACE 驱动,但 DSN 指向的是.accdb,也会报"不是有效的数据源"之类的错误。解决方法是保持文件格式和驱动选项一致,不要在 DSN 创建后再去改文件扩展名。

注意:力控如果跑在 64 位进程下,才使用控制面板里的"ODBC 数据源(64 位)";绝大多数力控工程都是 32 位,请以 SysWOW64 路径为准。

3.3 绕过 DSN:连接字符串直接指定驱动和路径

DSN 虽然直观,但现场换电脑时需要重建,多一步操作。另一种常见做法是无 DSN 连接,直接在力控脚本里写连接字符串。这需要你在代码里把驱动名、文件路径写全。以力控脚本为例:

// 无 DSN 连接 Access 文件 int connID; connID = SQLConnect("Driver={Microsoft Access Driver (*.mdb, *.accdb)};DBQ=C:\\ForceData\\ForceData.mdb;UID=;PWD=");

参数说明:Driver必须和 ODBC 管理器里显示的一字不差,包括括号和通配符;DBQ是 Access 文件绝对路径;UID和PWD留空。路径中的反斜杠在力控脚本字符串里建议写成双反斜杠,如果写成单反斜杠在某些版本中会被当成转义符,导致连接失败。这种方式的优点是环境迁移时只要改一个字符串,缺点是驱动名写错时排查起来比 DSN 版难,因为错误提示只反馈到连接层。

3.4 测试连接与第一句 SELECT

创建完 DSN 或连接字符串后,先不要急着写业务脚本。最简单的验证是在力控里执行一句SELECT 1,或者查询表是否存在:

int dsID; dsID = SQLSelect(connID, "SELECT TOP 1 ID FROM HistoryData"); if(dsID < 0) Message("表访问失败"); else { Message("表访问正常"); SQLClose(dsID); }

TOP 1是 Access 方言,表示只取第一行。如果这句能通过,说明 DSN、驱动、文件路径、表结构四层都通了。反之,逐层检查:先在 ODBC 管理器里用"测试连接"按钮验证文件;再用 Access 直接打开表看结构;最后检查力控脚本里的连接字符串。大多数情况下,问题都出在驱动位数或文件路径上,表层排查比查 SQL 快得多。

4. 力控脚本读写 Access:SQLConnect 到 SQLInsert 的最小闭环

4.1 初始化连接:与窗体生命期绑定

在力控的脚本体系里,数据库连接最好与窗体或工程的生命周期绑定。我一般把SQLConnect写在主窗体的打开事件里,把SQLDisconnect写在关闭事件里。连接句柄用一个全局变量保存,供窗口里的按钮事件和定时器事件调用。这样整个工程运行期间只用一条 ODBC 连接,避免频繁创建和释放带来的文件锁问题。

// 力控脚本:全局变量区声明 int connID; // 主窗体初始化事件 connID = SQLConnect("DSN=ForceData;UID=;PWD="); if(connID < 0) { // 连接失败时给运行人员明确提示 Message("数据库连接失败,请检查系统DSN ForceData"); return; }

参数说明:DSN=ForceData对应 3.2 创建的系统 DSN;UID和PWD在 Access 无安全机制时留空,如果有自定义账户,则填入 Access 的用户名和密码。SQLConnect返回值在不同力控版本里可能是整数或句柄,我的经验是判断负数比判断零更可靠。连接失败时,力控日志会记录一个错误码,结合 ODBC 管理器里的测试连接结果去对照。

这里有个容易被忽略的点:如果力控工程是以"副屏运行"或"服务方式"启动,那么当前登录用户可能没有读取 ODBC 系统 DSN 的权限。系统 DSN 的注册表位于LOCAL_MACHINE,对所有人可见,但某些组策略会限制服务进程访问。如果你发现手动运行力控正常,计划任务启动时连不上库,优先检查服务账号的配置。

4.2 写入数据:两种 SQLInsert 用法与字符串拼接

写入是力控脚本中最常用的操作。力控提供的 SQLInsert 在不同版本里有两种形态:一种是接受完整的 SQL 语句,一种是接受表名和字段名列表。为了让脚本好读,我推荐使用完整 SQL 语句的形态,因为它和 Access 的 SQL 语法一致,方便粘贴到 Access 中排查。

// 力控脚本:插入一条实时数据 string tagName; float tagValue; string timeStr; string sqlStr; tagName = "PT_101"; tagValue = PV_GetVal("PT_101"); // 读取当前值 timeStr = GetSysTime(); // 当前时间字符串 sqlStr = "INSERT INTO HistoryData (TagName, TagValue, CreateTime) VALUES ('" + tagName + "', " + FloatToStr(tagValue, -1) + ", #" + timeStr + "#)"; if(SQLInsert(connID, sqlStr) < 0) { Message("写入失败"); }

注意这里的时间值我用#包围而不是单引号,这是 Access 特有的日期时间分隔符。力控的GetSysTime()返回格式通常是2024-05-01 10:30:00,Access 用#或'都能识别,但#在 Access SQL 里更标准,能避免某些区域设置下把日期当成字符串。FloatToStr的第二参数-1表示不限制小数位数,如果希望统一格式,可以写3,输出三位小数。

另一个粒度的建议:不要每个采集周期都执行这条 INSERT。力控的采集周期可能是 500ms 或 1s,一秒一条记录一天就是 86400 条,Access 撑不了几个月。所以我在实际工程里会把INSERT放在一个定时器里,每 60 秒执行一次,写入这 60 秒内的平均值、最大值和最小值。表结构可以加AvgValue、MinValue、MaxValue三个字段,同样用一条 INSERT 完成。

4.3 查询与遍历:SQLSelect、SQLNextRow、SQLGetValue

查询历史和做报表是另一个高频场景。力控脚本里用SQLSelect返回一个数据集,然后通过游标函数逐行读取。以下代码演示如何把表里最近十条记录的值累加并显示平均值:

// 力控脚本:查询最近10条记录的平均值 int dsID; string sqlStr; float sumVal; int count; string tmpVal; sqlStr = "SELECT TagValue FROM HistoryData WHERE TagName='PT_101' ORDER BY ID DESC"; dsID = SQLSelect(connID, sqlStr); if(dsID < 0) return; count = 0; sumVal = 0; while(SQLNextRow(dsID) == 1 && count < 10) { SQLGetValue(dsID, "TagValue", tmpVal); sumVal = sumVal + StrToFloat(tmpVal); count++; } SQLClose(dsID); if(count > 0) { Text_Avg.Value = sumVal / count; }

SQLNextRow的返回值:1 表示取到了下一行,0 表示结束,负值表示出错。SQLGetValue把字段值取到字符串变量里,再做类型转换。这就意味着如果原始值是浮点数,读取出来后是字符串,你必须保证StrToFloat能解析科学计数法。在写入时把数值格式统一成固定小数位,能减少这类解析问题。

特别注意:SQLSelect拿到的结果集相当于一个快照,在SQLClose之前,原表可能处于锁定状态。如果脚本里先查询又立刻执行写入,容易报"数据表已锁定"。最好的做法是查询完立刻SQLClose,把结果拷到力控变量里,再执行后续写入。

4.4 事务与批量写入:如果版本支持

有些力控版本支持SQLTransaction相关函数,可以把多条 INSERT 放到一个事务里,最后由Commit统一提交。单条插入时无所谓,但批量写入或写入历史报警时,事务能避免半条数据落库的情况。如果现场数据量突然暴涨,用事务批量提交可以大幅缩短占用数据库的时间。不过事务函数在不同版本中名称不一,我在项目里一般先查询当前版本的手册,确认SQLBeginTrans是否存在,不能用"我以为"去硬写,否则编译能过但运行时找不到函数。

5. 力控连 Access 常见坑:从驱动报错到数据源消失,逐一排查

5.1 提示"请先安装 access 数据库 64 位系统驱动程序":驱动装了也白装

现象:在 64 位 Windows 上安装 Access Database Engine 后,力控运行仍弹窗提示请先安装 access 数据库 64 位系统驱动程序,甚至出现"64 位引擎不支持 DBC 数据,只支持 ACCESS 数据"这类字眼。测了几次都连不上,看起来像驱动没装。

原因:力控运行进程以 32 位模式工作,它只会到 32 位 ODBC 注册表节点找驱动。你通过默认控制面板打开的 64 位 ODBC 管理器创建了 DSN,这个 DSN 不会出现在 32 位节点的列表里。力控找不到 DSN,就把底层错误误报成"没有驱动"。

解决:先确定力控进程位数。将 ODBC 管理器关闭,打开C:\Windows\SysWOW64\odbcad32.exe,重新创建系统 DSN。同时检查 ACE 驱动的安装方式,如果安装时选择了"仅桌面数据库",部分 ODBC 条目不会注册。重装时选择完整安装,并在 ODBC 管理器里确认驱动列表出现Microsoft Access Driver (*.mdb, *.accdb)。这一步做完,大多数"请先安装驱动"的误报都会消失。

5.2 在 64 位管理器里能看到 DSN,力控却看不到

现象:控制面板的 ODBC 管理器里能看到ForceData,但力控脚本SQLConnect报错。打开 32 位 ODBC 管理器,发现列表是空的,或者找不到刚建的 DSN。

原因:用户很可能在 64 位 ODBC 管理器里创建了用户 DSN,或者创建了系统 DSN,但入口不对。用户 DSN 只对创建它的 Windows 用户可见,力控若以不同账户运行就看不到;另外 64 位和 32 位 DSN 分属不同注册表节点,互不共享。

解决:统一在 32 位入口创建"系统 DSN"。创建后回到 ODBC 管理器刷新一次,确认在 32 位管理器列表中能看到ForceData。这样不管力控以哪个用户运行,只要它是 32 位进程,就能通过系统 DSN 找到这个数据源。如果现场有多套环境,建议把 DSN 名称统一固化到工程文档里,避免开发用ForceData2、生产用ForceData这种不一致带来的隐性断层。

5.3 SQLInsert 返回负值,错误信息只有 SQL 语句

现象:连接正常,但写库时SQLInsert总是失败,力控消息框只显示拼接好的 SQL 语句,没有具体错误码。把 SQL 拿到 Access 里执行,有时能成功,有时同样失败。

原因:力控拼接的 SQL 与 Access 语法存在细节差异,最常见的是时间分隔符写错。很多工程师会把时间字符串用单引号包围,Access 有时在区域设置里不认,需要用#包围。另外,字段名如果用了 Access 保留字(如Value、Date),不加方括号也会导致解析失败。

解决:在拼接 SQL 时对时间字段用#,对字段名统一用方括号包裹,比如[TagName],这是 Access 标准的转义方式。把失败时的 SQL 语句复制到 Access 的 SQL 视图里执行,让 Access 给出具体错误信息。然后按提示修改力控脚本里的拼接格式。调试阶段先写死一条记录验证,确认通过再恢复动态变量。

5.4 写入时提示文件正在使用或数据库锁定

现象:运行期间偶尔写库报错,提示"文件正在使用"或"不能锁定数据库"。重启力控后又恢复,但运行一段时间再次报错。

原因:Access 文件被其他进程独占。常见嫌疑有:别人打开了 Access 设计视图没有关闭;上一次力控异常退出,SQLDisconnect没执行,ODBC 连接未释放;数据库文件放在网络共享路径,另一台电脑的半开连接占用锁文件。

解决:确认没有其他 Access 实例;力控窗体关闭事件里确保SQLDisconnect被调用;数据库文件只放本机,不走共享。如果已经出现锁文件,在任务管理器里结束MSACCESS.EXE进程,删除 Access 文件同目录下的.ldb锁文件,再重新连接。长期方案是设置定时器检测连接并自动重连,同时把写库操作包在一个独立函数里,每次写前检查连接状态,避免在断线状态下反复往里撞。

5.5 Access 文件体积快速膨胀,查询越来越慢

现象:运行一个月后,Access 文件从几 MB 涨到几百 MB,报表查询明显卡顿。力控写入变慢,有时还会超时。

原因:Access 表没有索引,写入记录过多,文件碎片增加。力控脚本如果每秒都执行INSERT,一个月会产生两百多万行记录,查询全表扫描必然慢。

解决:给CreateTime和TagName建普通索引,查询条件里带上时间范围;清理超过 60 天数据的 SQL 语句在每天凌晨执行一次;用 Access 的"压缩和修复数据库"功能释放空间。下面这条语句可以在 Access 的 SQL 视图或力控脚本里执行:

CREATE INDEX idx_TagTime ON HistoryData (TagName, CreateTime);

建立组合索引后,按标签和时间范围的查询会快很多。更根本的做法是降低写入频率,分钟级聚合落库。这个坑我每次做方案都会强调,等数据量上去了再改架构会非常被动。

6. 验证与进阶:从"能写进去"到"写得快、查得稳"

6.1 验证落库:用聚合查询代替肉眼翻表

写完脚本连上 Access,先执行一条带条件的计数查询。例如统计今天某标签的记录数,只要数量和力控的运行时长对得上,就说明链路是通的。我习惯把这段验证脚本放在开发环境的调试按钮下,确认无误再删掉。不要依赖用 Access 打开表翻到最后一页,那既慢又不能说明写入连续性。

6.2 进阶:分钟级聚合落库与自动清理

把写入频率从秒级降到分钟级是 Access 方案能否长期运行的分水岭。在力控定时器里每 60 秒执行一次INSERT,写入这分钟内的均值、最小值、最大值。数据量一下就能降两个数量级。同时配合每天凌晨的DELETE * FROM HistoryData WHERE CreateTime < DateAdd('d', -60, Now()),数据库始终保持在几十 MB 级别。这个思路也适合后来迁移到 SQL Server 的过渡。

6.3 文件备份:整库复制比导出更可靠

Access 单文件备份,直接关闭力控后复制.mdb文件到另一块硬盘即可。建议保留最近 7 天每日备份。如果现场要求备份时力控不停机,可以先在力控脚本里把连接断开,再用 Windows 计划任务调用copy命令,拷贝完成后重新连接。不过这个流程有窗口期,务必在值班日志里注明。

这里有一个我留下的教训:刚上手时我图省事,把秒级数据全塞进 Access,结果两周后报表查询就超时。后来我增加了聚合和清理,才真正稳定下来。所以现在每到一个新现场,我都会先问一句"这东西要持续跑几个月",再决定 Access 表怎么建、多久清一次。这个方向最值得投入的反而不是 SQL 技巧,而是数据生命周期设计。希望这套步骤能帮你把力控与 Access 数据库的链路一次跑通,也希望你能绕开我踩过的这些坑,项目交付顺顺利利。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进