☰
Intouch与SQL Server 2012历史库配置及Excel报表闭环
2026/11/4 12:33:04 网站建设 项目流程

简介:这份文档面向使用 WonderWare Intouch 2014R2 构建 SCADA 系统的工程人员与初学者,聚焦 HMI 与 SQL Server 2012 数据库集成及 Excel 报表落地。内容围绕数据库登录方式与权限配置、Intouch 标记字典与条件脚本编写、SQL 访问管理器变量绑定、SQLConnect/SQLDisconnect 连接管理、SQLInsert 定时写入,以及 Excel VBA 报表模板引用与数据提取展开,并附数据库文件恢复思路。整包仅 1 个 doc 文件,约 2.48MB,便于按章节查阅与现场对照。已有 533 人学习下载,适合需要打通 Intouch 数据存储、历史数据报表呈现与故障恢复流程的读者作为操作参考。

1. 装 Intouch 2014 R2 之前:先确认 SQL Server 2012 这套底座能不能立住

工控机上装 Intouch 2014 R2,配 SQL Server 2012 做历史库,再用 Excel 报表把班组的参数日报拉出来,这套组合在不少 SCADA 项目里是标准动作。真正上手才发现,坑不在 Intouch 的画面绘制,而在 SQLConnect 那一行:连接串里 Data Source 写错一个字符,绑定列表照常配置、脚本照常编译通过,数据就是不进库。等 Excel 报表打开一片空白,回头排查已经过去两天。

这套环境适合手里有一台能装 SQL Server 2012 的工控机、需要把 Intouch 实时数据落库并按班次出报表的人;也适合接手别人现场、只看到一堆 .mdf 和 .ldf 文件要恢复的运维。下面按我实际装过的顺序,从 SQL Server 2012 实例一直写到 Excel 报表刷新。

2. SQL Server 2012 实例配置:实例名、混合验证与建库建表

这一章要交付的是「一个 Intouch 能连上、Excel 能读、以后能恢复」的数据库底座。装 SQL Server 2012 本身不难,难的是装完之后那几个默认值——默认只开 Windows 身份验证、默认不启用 TCP/IP、默认把库建在 C 盘——三个默认值凑在一起,正好让 Intouch 的 SQL 访问管理器连不上。我一般把这几项在安装阶段就一次定死,省得后面回头改注册表再重启服务。

2.1 实例名与排序规则:装之前先定死

版本先选清楚。SQL Server 2012 Express 单库有 10GB 上限,做几天班组的参数日报还够用,一旦要存几个月的秒级数据就会顶到天花板,那时候再换版本要停机迁移,成本比一开始装标准版高得多。工控机上如果不确定数据量,我的做法是先按标准版规划,实在只有 Express 介质,就把写入频率从秒级降到分钟级,并且在建库时把文件增长量调大,减少碎片。

实例类型建议用默认实例 MSSQLSERVER,不要图省事装成命名实例。原因是 Intouch 的 SQL 访问管理器走的是 OLEDB,连接串里Data Source一段写的是数据库所在计算机名;默认实例只写主机名就行,命名实例要写成主机名\实例名,反斜杠在字符串里、在 Excel VBA 里都容易被转义或截断,是后面排查的高发点。如果现场已经有命名实例不能改,那就把实例名记死,后面每一处连接串都照抄,不要一处写主机名、一处写完整实例名。

排序规则选 Chinese_PRC_CI_AS,这是中文环境的常规选项,大小写不敏感、重音敏感,后面按日期字符串查询、按计算机名过滤都不会出岔子。装的时候在「数据库引擎配置」页把「身份验证模式」直接选成「混合模式」,同时把 SQL Server 管理员密码设好;如果安装时漏了,后面在 SSMS 里改也行,但必须重启服务才生效。

安装项建议值说明
版本标准版优先,Express 仅限小数据量Express 单库 10GB 上限
实例类型默认实例 MSSQLSERVER连接串只写主机名
排序规则Chinese_PRC_CI_AS中文环境常规选择
身份验证模式SQL Server 和 Windows 身份验证模式装完再改需重启服务
TCP/IP 协议启用,端口 1433OLEDB 连接的前置条件
SQL Server Browser自动启动命名实例必须,默认实例也建议开
数据文件目录E:\DataBase与后续数据库恢复目录保持一致

装完第一件事不是急着建库,而是确认网络协议真的开了。这一步在图形界面里点「启用」之后,服务不重启是不生效的,很多人在这里翻车:明明勾了 TCP/IP,OLEDB 还是连不上。

# 在数据库服务器上执行,确认实例服务状态与监听端口 Get-Service -Name 'MSSQLSERVER' | Select-Object Name, Status, StartType # 从错误日志里确认实例实际监听的端口,默认实例通常是 1433 Select-String -Path 'C:\Program Files\Microsoft SQL Server\MSSQL11.MSSQLSERVER\MSSQL\Log\ERRORLOG' ` -Pattern 'Server is listening on'

第一段命令看的是服务有没有起来、启动类型是不是自动;第二段命令直接读错误日志,比在配置管理器里翻协议可靠得多,日志里出现Server is listening on [ 'any' <ipv4> 1433]就说明端口正常。如果只看到共享内存和命名管道、没有 TCP 那一行,回到 SQL Server 配置管理器把MSSQLSERVER 的协议下的 TCP/IP 启用,然后重启SQL Server (MSSQLSERVER)服务再查一次。

2.2 开启 SQL Server 和 Windows 身份验证模式并新建登录名

装完是「仅 Windows 身份验证」的话,Intouch 那条连接串里的User ID/Password根本没有用,SQLConnect 会一直返回 0,而 Intouch 不会给你任何报错弹窗——这是最费时间的一种失败。改法有图形和脚本两条路,图形路径是:SSMS 里右键实例 → 属性 → 左侧选「安全性」→ 右侧「服务器身份验证」选「SQL Server 和 Windows 身份验证模式」→ 确定 → 重启服务。

我习惯用注册表脚本改,因为可以写进部署记录,换台机器照着执行一遍就行。

-- 以 Windows 身份验证登录 SSMS 后,在新查询窗口执行 -- LoginMode:1 = 仅 Windows 身份验证,2 = SQL Server 和 Windows 身份验证模式 EXEC xp_instance_regwrite N'HKEY_LOCAL_MACHINE', N'Software\Microsoft\MSSQLServer\MSSQLServer', N'LoginMode', REG_DWORD, 2; GO

执行完必须重启 SQL Server 服务,注册表改动才会被引擎读进去。重启后可以用下面这句确认当前模式,返回 2 就对了:

SELECT SERVERPROPERTY('IsIntegratedSecurityOnly') AS [仅Windows验证]; -- 返回 0 表示混合模式已生效,返回 1 表示仍然是仅 Windows 验证

接下来是新建登录名。图形路径是「对象资源管理器」→「安全性」→ 右键「登录名」→「新建登录名」,在「常规」页填登录名、选「SQL Server 身份验证」、输两遍密码、取消「用户在下次登录时必须更改密码」;「服务器角色」页按需勾选。这里有个取舍:不少人图省事直接给sysadmin或dbcreator,现场能跑,但一个只负责写数的报表账号拿到建库删库权限,属于自己给自己埋雷。我的做法是登录名只给public,到具体数据库里再映射用户、给db_datareader和db_datawriter,够 Intouch 写、也够 Excel 读。

-- 1) 新建 SQL 登录名,关闭密码策略以便现场部署(生产环境建议保留策略) CREATE LOGIN [scada_rw] WITH PASSWORD = N'Sc@da#2012rw', CHECK_POLICY = OFF, DEFAULT_DATABASE = [SCADA_BB]; GO -- 2) 只给目标库的读写角色,不给服务器级角色 USE [SCADA_BB]; GO CREATE USER [scada_rw] FOR LOGIN [scada_rw]; GO ALTER ROLE [db_datareader] ADD MEMBER [scada_rw]; ALTER ROLE [db_datawriter] ADD MEMBER [scada_rw]; GO

CHECK_POLICY = OFF是为了让现场部署时密码不受复杂度策略限制,代价是安全性下降,正式投运前建议改回ON并换成复杂密码。DEFAULT_DATABASE指向目标库,这样登录进去默认就在SCADA_BB里,Intouch 连接串里的Initial Catalog即使写漏也有兜底。如果现场已经有合适的用户名,就不用新建,直接在「登录名」属性里改密码和服务器角色即可,改完记得在 Intouch 的连接串里同步。

2.3 建库建表和列类型:和 Intouch 绑定列表对齐

建库这一步要把文件路径一次定到 E 盘,别用默认的 C 盘路径。原因很实际:后面数据库恢复、附加 .mdf 的操作默认都往E:\DataBase找,路径不一致时要多走一遍「定位数据库文件」,现场一着急就容易选错文件。建库脚本里把初始大小和增长量写死,能避免频繁自动增长带来的日志碎片。

-- 建库:数据文件和日志文件都放 E:\DataBase,目录需提前创建 CREATE DATABASE [SCADA_BB] ON PRIMARY ( NAME = N'SCADA_BB', FILENAME = N'E:\DataBase\SCADA_BB.mdf', SIZE = 512MB, FILEGROWTH = 256MB ) LOG ON ( NAME = N'SCADA_BB_log', FILENAME = N'E:\DataBase\SCADA_BB_log.ldf', SIZE = 256MB, FILEGROWTH = 128MB ); GO

SIZE是初始大小,FILEGROWTH是每次不够时扩多少。写死增长量比按百分比增长可控,尤其是日志文件按百分比涨起来会很快吃掉磁盘。建库之前先确认E:\DataBase目录存在,SQL Server 服务账户对它有写权限,否则建库语句直接报操作系统错误 5(拒绝访问)。

表结构要和 Intouch 的绑定列表一一对齐,这是整条链路里最容易出错的地方。原文里的做法是让 Intouch 变量名与数据库列名对应、变量数据类型与列的数据类型对应,我把它整理成一张对照表,建表时照着填:

Intouch 标记类型SQL Server 列类型典型用途
内存消息(Message)nvarchar(32) / nvarchar(64)日期时间字符串、计算机名
内存实型(Real)float温度、压力、流量等工艺参数
内存整形(Integer)int计数、状态码、连接号
内存离散(Discrete)bit开关量、报警状态

建表语句按这个对照关系落地,主键用自增列,日期列单独建索引,报表按时间段查询时不会全表扫。

USE [SCADA_BB]; GO CREATE TABLE [dbo].[TDays]( [RecID] [int] IDENTITY(1,1) NOT NULL, -- 自增主键,报表排序用 [DateTimeS] [nvarchar](32) NULL, -- 对应内存消息 GetDateTimeS [NodeName] [nvarchar](64) NULL, -- 对应内存消息 NodeName [Tag001] [float] NULL, -- 对应内存实型工艺参数 [RunFlag] [bit] NULL, -- 对应内存离散状态位 CONSTRAINT [PK_TDays] PRIMARY KEY CLUSTERED ([RecID] ASC) ); GO CREATE INDEX [IX_TDays_DateTimeS] ON [dbo].[TDays]([DateTimeS]); GO

IDENTITY(1,1)让每条记录自动编号,避免 Intouch 侧还要维护一个序号变量;IX_TDays_DateTimeS是给 Excel 报表的按时间段查询准备的。表名TDays要和后面 Intouch 绑定列表名保持一致,因为SQLInsert的第一个字符串参数是表名、第二个是绑定列表名,两者写成一样最省心,也少一处改错的机会。

3. Intouch 2014 R2 侧配置:标记名、绑定列表与 SQLConnect 脚本时序

数据库这头立住之后,才轮到 Intouch 2014 R2。这一章要交付三样东西:四个支撑标记、一张和数据库列严格对齐的绑定列表、以及能定时把数据推进库里的条件脚本。顺序不能反——先建标记,再配绑定,最后写脚本,因为脚本里的变量名必须已经在标记名字典里存在,否则编译不报错、运行时静默失败,属于最难查的一类问题。

3.1 先把系统日期时间格式改成 yyyy-MM-dd

这一步在原文里只是一句「更改日期和时间格式」,但它决定了后面报表能不能按时间段查出来。Intouch 从系统取时间拼成字符串写库,如果系统短日期格式是yyyy/M/d或者MM-dd-yy,写进DateTimeS列的值就是这种格式,Excel 报表里按yyyy-mm-dd hh:nn:ss去比较字符串,一条都对不上,页面上就是空表——现象和数据没写进去一模一样,非常容易误判。

改法:控制面板 → 区域和语言 → 格式选项卡 → 短日期选yyyy-MM-dd,长日期选yyyy-MM-dd,时间选HH:mm:ss;再到「管理」→「更改系统区域设置」确认不是英文或其他区域。改完重启 Intouch 运行态,让它重新读系统格式。工控机如果做过Ghost 还原或者被人改过区域设置,这一项特别容易回到默认值,我一般在部署记录里单独列一条,每次重装都检查一遍。

3.2 标记名字典里必建的四个标记

在 Intouch 的「标记名字典」里新建四个标记,它们不参与画面显示,纯粹是给数据库链路做支撑。

标记名数据类型用途
Connid1内存整形保存 SQLConnect 返回的连接号,0 表示未连上
GetDateTimeS内存消息保存写入数据库的时间字符串
NodeName内存消息保存本机计算机名,用于区分哪台操作站写的数据
WSQLS内存离散写入防重标志,真表示本轮已写过,假表示未写

Connid1是整条链路的状态指示器,值非 0 才说明 SQL 连接建立成功;排查时在画面上放一个文本对象直接显示它,比翻日志快得多。NodeName用GetNodeName函数填充,长度给 32 就够,工控机名一般不会太长。WSQLS的初值设成假(0),后面条件脚本靠它判断本轮是否已经写过数据。

这四个标记建完之后,建议先做一次「全部重新编译」,Intouch 的标记名字典改动不会自动同步到已经打开的脚本编辑器,编译一次能提前暴露变量名拼写错误。

3.3 SQL 访问管理器绑定列表:列名和类型必须一一对上

绑定列表在 Intouch 左侧工具视图最下面的「SQL 访问管理器」里,选「绑定列表」。新建一个列表,名字就叫TDays,和数据库表名一致。列表里逐条添加要记录的变量:变量名必须和数据库表的列名完全一致(包括大小写,虽然Chinese_PRC_CI_AS不敏感,但保持一致的写法后面排查省事),变量类型必须和列类型对应上。

对应关系按上一章的对照表来:GetDateTimeS是内存消息,对应nvarchar(32);NodeName是内存消息,对应nvarchar(64);工艺参数是内存实型,对应float;状态位是内存离散,对应bit。这里最常见的翻车是长度不匹配——Intouch 内存消息如果设了 64 字符,而数据库列只给nvarchar(32),插入时会被截断或者直接报错,SQLInsert返回失败但 Intouch 不会弹窗。我的习惯是数据库列长度一律比 Intouch 标记长度多留一倍余量,宁可浪费几个字节。

还有一个细节:绑定列表里不要放RecID这类自增列,它是数据库自己生成的,放进去反而会让插入失败。

3.4 应用程序脚本里的 SQLConnect 与关闭时 SQLDisconnect

连接动作写在「脚本」视图的「应用程序」里的「启动时」脚本中,函数原型是SQLConnect(联接数据库号, SQL 联接字符串)。原文里的连接串是五个分号分隔的键值对,我把示例值换成占位符,现场按实际替换:

' Intouch 应用程序脚本 - 启动时 ' 参数1:Connid1,内存整形,接收连接号,0 表示连接失败 ' 参数2:OLEDB 连接串,五段用分号分隔,每段格式为 键=值 SQLConnect(Connid1, "provider=sqloledb;Data Source=SQLHOST01;Initial Catalog=SCADA_BB;UserID=scada_rw;Password=Sc@da#2012rw");

五段参数逐个说清楚:provider=sqloledb是固定值,表示走 SQL Server 的 OLEDB 提供程序,不要写成sqloledb.1之外的变体;Data Source是数据库所在计算机名,默认实例只写主机名,命名实例要写成主机名\实例名;Initial Catalog是数据库名,要和建库时的一致;UserID和Password是 2.2 节新建的 SQL 登录名和密码,不要用sa,sa一旦被改密码所有操作站全断。

连接串里最容易写错的是Data Source:写成 IP 有时候能通、有时候不通,取决于是否启用了 TCP/IP 和是否配了别名;写错一个字符则完全不报错。我的做法是连接串统一用计算机名,并且在部署记录里把这个名字单独列一行,换机器时只改这一处。

关闭连接写在「关闭时」脚本里,只有一行:

' Intouch 应用程序脚本 - 关闭时 ' 不写这一行,运行态退出后连接不会立刻释放,重启后可能撞上连接数上限 SQLDisconnect(Connid1);

这一行看着可有可无,实际影响很大:Intouch 运行态被强制结束、或者操作员反复开关画面时,没有SQLDisconnect的连接会挂在数据库侧,时间长了Connid1拿不到新连接号,表现就是「昨天还好好的,今天突然不写数了」。

3.5 条件脚本:$Second==5 触发与 WSQLS 防重

写入动作放在「脚本」视图的「条件」里,新建一个条件脚本,条件写$Second==5,条件类型选「为真时」。含义是每秒钟判断一次,秒数等于 5 时执行一次,也就是每分钟执行一次。这个触发方式比用定时器简单,缺点是 Intouch 运行态如果正好在那一秒卡顿,这一分钟就跳过了,对报表类数据可以接受。

原文的脚本逻辑是:取本机计算机名 → 判断是不是指定操作站 → 判断分钟数是否大于等于 58 → 判断是否已写过 → 执行SQLInsert→ 置位/复位标志。我按这个逻辑给出修正后的写法,并把它拆开说明:

' 条件脚本:$Second==5,条件类型:为真时 ' 131 为 GetNodeName 的取节点名参数,结果放入 NodeName 标记 GetNodeName(NodeName, 131); IF NodeName == "OP_01" THEN IF $Minute >= 58 THEN IF WSQLS == 0 THEN ' 时间字符串按系统格式拼装,写入前先赋值给 GetDateTimeS GetDateTimeS = $DateString + " " + $TimeString; ' 参数1:连接号;参数2:数据库表名;参数3:绑定列表名 SQLInsert(Connid1, "TDays", "TDays"); WSQLS = 1; ' 置位,表示本小时已写过,防止重复插入 ENDIF; ELSE WSQLS = 0; ' 离开 58 分窗口后复位,为下一小时做准备 ENDIF; ENDIF;

和原文相比,关键改动在最后那个ELSE WSQLS = 0;。原文把置位和复位写在同一个条件块里,插完立刻复位,防重标志等于没起作用,只要那一分钟内秒数再走到 5(实际不会,但换个触发条件就会),就会重复插入一条。复位动作放到「分钟数小于 58」的分支里,语义才对:进入 58 分之后的窗口写一次,出了窗口再清零。

IF NodeName == "OP_01"这一行的字符串必须和实际计算机名完全一致。原文注释里写的是OP_01,代码里写的是OP_OP1,这种不一致在现场非常常见,后果是脚本永远不执行、数据库一条记录都没有,而脚本本身编译通过、没有任何报错。我的习惯是先用一个临时文本对象把NodeName显示在画面上,肉眼确认实际值,再回来改脚本里的字符串。如果现场有多台操作站都要写数,就把这个判断去掉,或者改成IF NodeName <> "" THEN,靠数据库里的NodeName列区分来源。

4. Excel 报表模板:控制面板页、参数日报页与 VBA 引用

报表这一端要交付的是一个能双击打开、点一下按钮就从 SQL Server 2012 拉数据、按班次出参数日报的 Excel 文件。原文给的结构是两张工作表:「控制面板」放查询条件和按钮,「参数日报」放数据区和 VBA。这个结构比我见过的一些单表方案好维护,因为查询参数和数据区分开了,换班次只要改控制面板上的两个单元格,不用动代码。

4.1 Excel 宏安全与信任中心设置

模板拿到手第一件事不是打开看,而是先配信任中心,否则一开文件宏就被禁用,按钮点了没反应,很容易误判成模板坏了。路径是:文件 → 选项 → 信任中心 → 信任中心设置 → 宏设置,选「启用所有宏」并勾选「信任对 VBA 工程对象模型的访问」;再到「受信任位置」把模板所在目录加进去。

这台机器如果同时装了 Intouch 2014 R2,需要注意 Excel 和 Intouch 的位数要匹配——Intouch 的 SQL 访问管理器是 32 位组件,如果 Excel 装的是 64 位版本,跨组件调用偶尔会出问题。我的做法是报表机器上统一装 32 位 Office,和工控软件保持一致,省掉一类说不清的兼容问题。

4.2 模板结构:控制面板页与参数日报页

「控制面板」页放三类东西:查询起始时间、查询结束时间、一个「刷新报表」按钮。起始和结束时间用两个单元格,格式设成yyyy-mm-dd hh:mm:ss,并且把单元格格式显式设成文本或者自定义格式,避免 Excel 自动把日期转成序列号后又按本地格式显示回去——这一项和 3.1 节的系统时间格式是联动的,两边不一致,查出来的数据就会偏移或者为空。

「参数日报」页第一行是表头,列名和数据库TDays表的列一一对应:时间、计算机名、工艺参数、状态位。数据区从第二行开始,VBA 每次刷新先清空再写入。表头下面不要合并单元格,ADO 记录集往单元格里写的时候,合并区域会导致写入位置错乱。

4.3 VBA 引用:至少要勾的三项

在「参数日报」标签上右键 →「查看代码」,进入 VBA 编辑器,菜单「工具」→「引用」,这个窗口里至少要勾三项:Visual Basic for Applications(默认已勾)、Microsoft Excel 对象库(默认已勾)、以及一个数据访问库。第三项是重点,原文写的是「至少钩选以下三项」但没展开,现场按机器上实际有的版本选:优先找Microsoft ActiveX Data Objects 6.1 Library,没有就退到2.8或2.5,三者选一个即可,不要同时勾多个版本,否则会出现「用户定义类型未定义」的编译错误。

除了 ADO,如果模板里用了Scripting.Dictionary或者文件系统对象,还要补勾Microsoft Scripting Runtime。判断方法很直接:按 F5 编译一次,报哪一行「用户定义类型未定义」,就是缺哪个引用,报的变量类型名基本能对应上库名。

4.4 取数脚本与刷新节奏

取数用 ADO 连接 SQL Server 2012,连接串和 Intouch 那边保持同样的五段结构,这样现场只维护一套参数。下面这段放在「参数日报」的代码模块里:

' 参数日报页:按控制面板上的时间段从 SQL Server 2012 取数 Sub RefreshDailyReport() Dim conn As Object, rs As Object Dim connStr As String, sqlStr As String Dim t1 As String, t2 As String Dim wsData As Worksheet, wsPanel As Worksheet Dim r As Long Set wsData = ThisWorkbook.Worksheets("参数日报") Set wsPanel = ThisWorkbook.Worksheets("控制面板") ' 时间参数统一格式化为 yyyy-mm-dd hh:nn:ss,与数据库中的字符串格式对齐 t1 = Format(wsPanel.Range("B2").Value, "yyyy-mm-dd hh:nn:ss") t2 = Format(wsPanel.Range("B3").Value, "yyyy-mm-dd hh:nn:ss") connStr = "Provider=SQLOLEDB;Data Source=SQLHOST01;Initial Catalog=SCADA_BB;" & _ "User ID=scada_rw;Password=Sc@da#2012rw;" sqlStr = "SELECT DateTimeS, NodeName, Tag001, RunFlag FROM dbo.TDays " & _ "WHERE DateTimeS BETWEEN '" & t1 & "' AND '" & t2 & "' " & _ "ORDER BY RecID" Set conn = CreateObject("ADODB.Connection") conn.Open connStr Set rs = conn.Execute(sqlStr) ' 清空旧数据,从第 2 行开始写,保留表头 wsData.Range("A2:D10000").ClearContents r = 2 Do While Not rs.EOF wsData.Cells(r, 1).Value = rs.Fields("DateTimeS").Value wsData.Cells(r, 2).Value = rs.Fields("NodeName").Value wsData.Cells(r, 3).Value = rs.Fields("Tag001").Value wsData.Cells(r, 4).Value = rs.Fields("RunFlag").Value rs.MoveNext r = r + 1 Loop rs.Close conn.Close Set rs = Nothing Set conn = Nothing End Sub

Format(..., "yyyy-mm-dd hh:nn:ss")是关键,它保证不管控制面板上显示成什么样,传到 SQL 里的字符串格式都是固定的,和DateTimeS列存的格式对得上。BETWEEN两端都包含,如果班次是 08:00 到 20:00,结束时间写20:00:00会把整点那条也带上,需要精确的话改成>= t1 AND < t2。

ClearContents的范围写 10000 行是经验值,按现场数据量调整;不要用UsedRange.Clear,它会把表头一起清掉。连接用完必须Close,否则 Excel 进程退出后连接还挂着,下次刷新拿不到新连接。

刷新节奏上,不建议在 Excel 里做定时器自动刷。报表是给人看的,操作员点按钮刷新就够了;真要做自动刷新,用Application.OnTime设 5 分钟一次,但必须在Workbook_BeforeClose里OnTime取消调度,否则关掉文件后 Excel 进程会一直驻留。原文里 Intouch 是每 5 秒判断一次、每 5 分钟量级写一次库,报表侧跟着这个节奏走,5 分钟刷一次完全够。

5. 避坑与排查:从连接串到数据库附加的五条血泪记录

这一章是我这几年在这套环境上实际踩过的、以及帮别人查过的记录,按「现象 → 原因 → 解决」整理。每条都出现过不止一次,新手照着自己对一遍,能省掉大量瞎猜的时间。

5.1 现象:SQLConnect 返回值一直是 0,绑定列表配好了也不写数

原因排查下来有四种:SQL Server 只开了 Windows 身份验证,连接串里的用户名密码无效;TCP/IP 协议没启用,或者启用了没重启服务;Data Source写成了 IP 但服务器没监听该地址;登录名没有目标库的db_datawriter角色。四种原因的现象完全一样,Intouch 都不报错。

解决要按顺序查:先在 SSMS 里执行SELECT SERVERPROPERTY('IsIntegratedSecurityOnly'),返回 0 才说明混合模式生效;再用sqlcmd -S 主机名 -U scada_rw -P 密码 -d SCADA_BB -Q "SELECT 1"从 Intouch 那台机器上试连,能通说明网络和账号没问题,不通就回到协议和防火墙;最后确认登录名在目标库里有写权限。这三步走完还没通,基本就是连接串字符串本身写错了,逐字符对比。

5.2 现象:脚本编译通过、运行态也跑着,数据库一张表都没多

原因:条件脚本里的计算机名判断和实际计算机名不一致。原文示例里注释写OP_01、代码写OP_OP1,这种笔误在现场极其常见;另一个变体是工控机改过名字,脚本没跟着改。

解决:建一个临时文本对象显示NodeName标记的实时值,肉眼抄下来,逐字符改进脚本里的字符串;或者干脆把判断放宽成IF NodeName <> "" THEN,用数据库里的NodeName列区分来源。这个坑的隐蔽之处在于脚本没有任何错误提示,条件判断为假就静默跳过。

5.3 现象:SQLInsert 执行了,数据库里有记录但字段是空的或者被截断

原因:绑定列表的变量类型或长度和数据库列不匹配。内存消息标记长度 64、列给nvarchar(32),超长部分被截断;内存实型对应到int列,小数部分丢失;内存离散对应到nvarchar列,写入的是字符串"0"/"1"而不是布尔值。

解决:按第 2 章的对照表把每一列重新核一遍,数据库列长度一律给 Intouch 标记长度的两倍;不确定的就先在测试库上插入一条,用SELECT * FROM TDays看实际落库内容。这类问题不会报错,只能靠对数据。

5.4 现象:Intouch 里数据明明在变化,Excel 报表查出来是空的

原因:DateTimeS存进去的格式和报表查询用的格式不一致。系统短日期是yyyy/M/d,报表 VBA 里Format成yyyy-mm-dd,字符串比较一条都匹配不上;另一种情况是控制面板上的时间单元格被 Excel 自动识别成日期序列号,Format之后偏移了几个小时。

解决:两边统一到yyyy-mm-dd hh:nn:ss。系统区域设置里把短日期改成yyyy-MM-dd(见 3.1 节),报表侧单元格格式设成文本,VBA 里统一Format;验证方法是直接在 SSMS 里跑同样条件的SELECT,SSMS 能查出来而 Excel 查不出来,问题一定在格式转换上。

5.5 现象:附加 .mdf 文件时报错,或者附加成功但数据库显示「只读」

原因有三种:只拷了 .mdf 没拷 .ldf,SQL Server 找不到日志文件;E:\DataBase目录对 SQL Server 服务账户没有完全控制权限,附加时报操作系统错误 5;.mdf 来自更高版本的 SQL Server,2012 无法附加。

解决:.mdf和_log.ldf两个文件必须一起拷贝到E:\DataBase,路径和原机器保持一致;给 SQL Server 服务账户(默认NT Service\MSSQLSERVER)对该目录「完全控制」;版本不匹配只能回到源机器导出脚本和数据,不能硬附。附加操作路径是:SSMS 里右键「数据库」→「附加」→「添加」→ 定位到E:\DataBase→ 选.mdf→ 确定,附加完成后立刻做一次DBCC CHECKDB确认文件完整。

6. 验证跑通:从 $Second 触发到报表刷新的闭环检查

装完之后不要急着交付,按下面这个顺序走一遍,每一步都有明确的通过标准。我把这套检查固化成了一张清单,每次部署完照着勾,比凭感觉「看着差不多」可靠得多。

第一步,数据库侧确认表结构。在 SSMS 里执行SELECT TOP 10 * FROM dbo.TDays ORDER BY RecID DESC,能查出列名、类型符合预期即可,此时表里应该是空的。

第二步,确认连接建立。在 Intouch 画面上放一个文本对象显示Connid1,切到运行态,值非 0 说明SQLConnect成功;值是 0 就回到 5.1 节排查。这一步通过之前,后面所有验证都没有意义。

第三步,手动触发写入。把条件脚本的触发条件临时从$Second==5改成$Second==5 AND $Minute==任意当前分钟,或者直接把$Minute >= 58改成$Minute >= 0,让它在当前分钟就执行一次。改完重新编译、切运行态,等一个触发周期,然后回 SSMS 再查一次TDays,应该多出一条记录,DateTimeS是yyyy-mm-dd hh:nn:ss格式,NodeName是实际计算机名。

第四步,验证防重。保持上面的放宽条件不动,观察两分钟,TDays里不应该每分钟都多一条——WSQLS置位后应该挡住重复写入。如果每分钟都在涨,说明 3.5 节的复位分支写错了。

第五步,验证报表。打开 Excel 模板,控制面板上把起止时间设成刚才那条记录的时间范围,点刷新,参数日报页应该出现数据。查不到就按 5.4 节对格式。

验证项通过标准失败先查
表结构列名、类型与对照表一致建表脚本
SQLConnectConnid1 非 0混合验证、TCP/IP、连接串
单条写入TDays 新增一条、格式正确计算机名判断、绑定列表类型
防重一个窗口内只写一条WSQLS 置位/复位分支
报表取数参数日报页有数据时间格式、VBA 引用
数据库恢复附加后 DBCC CHECKDB 无错文件权限、mdf/ldf 是否成对

第六步,恢复演练。把E:\DataBase下的.mdf和_log.ldf拷一份到测试机,按 5.5 节的路径附加一次,能附加成功并且SELECT COUNT(*)和源库一致,才算这套环境的备份链路是通的。这一步很多人省略,真出事的时候才发现日志文件没拷。

从那以后我每次部署完这套环境,都会强制走一遍第三步和第六步——手动插一条、再恢复一次。这两步花不了十分钟,但能挡掉后面百分之八十的「数据没进库」和「恢复不起来」。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询