简介:正在使用组态王进行历史数据库查询的工程师,常需要处理日期类型字段的筛选问题。这份精品文档专门讲解了KVADODBGrid控件的应用方法,从添加两个日期时间控件、编写CLOSEUP事件转换日期格式,到拼接Where查询条件、执行FetchData取数,均给出了可直接套用的命令语言示例。文档针对月份或日期小于10时需补零的细节,提供了完整的分支判断写法,也演示了如何使用{d'...'}语法实现时间段查询,并通过Editable属性与SQLUpdate()函数完成查询后的数据编辑与回写。资源仅有1个doc文件,压缩包约35KB,内容高度聚焦,适合作为组态王二次开发人员的快速参考。目前已有183人学习浏览,尤其适合需要快速实现组态王与数据库联动的自动化工程师、上位机开发人员。
1. 组态王 KVADODBGrid 控件:日期字段查询难住不少人
做上位机的人应该都摸过组态王里那个 KVADODBGrid 控件,表格绑定数据库、显示记录集,看着挺方便。可一旦条件里带上日期字段,经常是查不出数据、报语法错、或者显示一堆莫名其妙的记录。我见过不少人在这个控件上翻车,查日期差点把数据库翻了底朝天。这份文档就是专门讲清楚「在组态王中用 KVADODBGrid 控件查询数据库日期类型字段」这件事的,适合正在做组态画面数据查询、搞过报表和趋势回放的工程师。文档里把控件属性、SQL 写法、日期格式处理和常见坑都盘了一遍,照着做基本能直接落地,不用再去试错。
2. 环境与准备工作:先把数据库和控件状态搞对
2.1 数据库表结构设计与日期字段选型
KVADODBGrid 本身只是一个展示和查询的壳,真正决定日期查询能不能成的是底层数据库表设计。我习惯先建一张测试表,字段不需要多,但日期字段必须单独拎出来设计。
常见做法是用 SQL Server 或 Access,组态王对这两种支持最稳。测试表结构大致如下:
CREATE TABLE HistoryData ( ID INT PRIMARY KEY, TagName NVARCHAR(50), RecordTime DATETIME, Value FLOAT )这段建表 SQL 里有几个关键点:RecordTime用的是DATETIME,它同时包含日期和时间;查询的时候如果只按日期筛,容易把当天凌晨的 00:00:00 边界问题带出来。组态王的 KVADODBGrid 对字段名大小写不敏感,但字段类型最好别用VARCHAR存日期,否则后面查询条件里的比较运算会变成字符串比较,结果完全不可控。我在某水处理项目里就见过把日期存成文本的表,查询时大于小于全乱套,这种坑最好在设计阶段就避开。
如果你用的是 Access,字段类型选择“日期/时间”,内部存储和 SQL Server 的 DATETIME 逻辑一致。需要注意 Access 对日期的分隔符默认是#,这和 SQL Server 的单引号写法不同,文档里也有提到这个差异。用组态王做上位机的项目,底层库最常见就是这两种,先把表结构定下来,后面的控件连接才省心。
2.2 KVADODBGrid 控件插入与属性配置
新建组态王画面后,在工具箱里找到 KVADODBGrid 控件拖到画面上。这个控件类似一个网格表格,双击进入属性设置页。需要重点配置几个属性,我整理了一张表:
| 属性 | 建议值 | 作用 |
|---|---|---|
| DataSourceName | 系统ODBC数据源名 | 控件通过它连接数据库 |
| ConnectionString | 留空或按需覆盖 | 有些版本可以直接写连接串,优先级高于数据源 |
| RecordSource | SELECT 语句 | 控件显示的记录集来源 |
| Font | 宋体 9号 | 保证中文显示正常 |
| ReadOnly | True | 避免误操作修改数据 |
实际连库时,我一般不用 ConnectionString 直接写,而是先在系统 ODBC 里配置好数据源,控件里只填 DataSourceName。这样做的好处是换数据库机器时不用改画面脚本,只改 ODBC 配置。不过要注意,DataSourceName在某些组态王版本里显示为DataSource或者英文属性名,不同版本控件属性名略有差异。
RecordSource 可以直接在属性里写死一个查询语句,例如:
SELECT ID, TagName, RecordTime, Value FROM HistoryData这样画面上就能看到表里的全部数据,证明控件和数据库之间链路是通的。但这不是最终目的,实际项目里需要根据用户在画面输入的日期范围动态生成 SQL,这就引出了下一章的查询实现。
3. 日期查询实现:SQL 拼接、参数传递与边界控制
3.1 组态王脚本变量与日期时间转换
组态王的脚本语言类似 C 语言,但函数库和通用语言差别不小。要在脚本里拼出带日期条件的 SQL,得先把用户界面输入的日期时间转成字符串。这里有一个很关键的问题:组态王脚本中通过控件输入框拿到的日期,通常是个字符串变量,但格式不一定是YYYY-MM-DD,可能是YYYY/MM/DD,也可能是带时分秒的。
我一般这样做:画面上放两个输入框,一个起始日期StartDate,一个结束日期EndDate,再把它们转成标准格式拼进 SQL。组态王脚本里字符串拼接用+运算符,数字转字符串用StrFromInt、StrFromFloat之类的转换函数(具体函数名以你用的组态王版本为准,大致是这种命名)。下面是一段比较典型的脚本逻辑:
string sSql; string sStart, sEnd; sStart = "2024-01-01 00:00:00"; sEnd = "2024-01-31 23:59:59"; sSql = "SELECT ID, TagName, RecordTime, Value FROM HistoryData WHERE RecordTime >= '" + sStart + "' AND RecordTime <= '" + sEnd + "' ORDER BY RecordTime";这段代码里我把日期条件直接写进了 SQL 字符串。注意RecordTime >= '2024-01-01 00:00:00',日期值被单引号包裹,这是数据库识别的标准写法。如果你只传日期2024-01-01而不带时间,数据库会把后面的时分秒默认成00:00:00,查询结果的最后一条边界就有问题。
实际项目中用户输入往往是控件返回的字符串变量,比如画面上的编辑框变量是StartDateInput。那脚本里就直接引用:
string sSql; string sStart, sEnd; sStart = StartDateInput; sEnd = EndDateInput; sSql = "SELECT ... WHERE RecordTime >= '" + sStart + "' AND RecordTime <= '" + sEnd + "'";这种拼接方式最简单,但对用户输入的格式有要求。如果用户在输入框里填了2024/01/01,数据库在执行时会隐式转换,大多数情况下也能识别。如果填的是2024年1月1日,那 SQL 执行就会报错,因为不是合法的日期字面量。这个问题可以在脚本里做一次替换,把/替换成-,把中文去掉,格式统一后再拼 SQL。
3.2 参数化查询:避免 SQL 注入与格式混乱
直接拼接字符串是最容易上手的写法,但存在两个隐患:一是用户输入特殊字符时可能拼出错误 SQL;二是日期格式不统一时执行报错或查不到数据。更稳妥的做法是用参数化查询,KVADODBGrid 控件支持通过 ADO 参数对象传递查询条件,脚本里这样操作:
object objRS; object objParam; objRS = KVADODBGrid1.GetRecordset(); objParam = objRS.CreateParameter("pStart", adDBTimeStamp, adParamInput, 8, sStart); objRS.Parameters.Append(objParam); objRS.QueryParameters("pStart") = sStart; objRS.Source = sSql; objRS.Refresh();这段逻辑里,先获取控件的记录集对象,再创建名为pStart的参数,参数类型是adDBTimeStamp(对应数据库的日期时间类型),最后把参数值赋给 SQL 里的占位符。SQL 语句里要写成:
SELECT ... FROM HistoryData WHERE RecordTime >= ? AND RecordTime <= ?问号就是参数占位符,执行时会被pStart、pEnd的值替代。
不过这段脚本在实际工程里比较多变,因为 KVADODBGrid 在不同组态王版本里的对象模型不一定完全一样,有些版本GetRecordset()取到的对象不支持QueryParameters属性,这时就得用最原始的Execute方式去执行。参数化查询的优点是格式统一、对中文和特殊符号的容错好,缺点是脚本复杂度高、版本兼容性差。所以我通常的推荐是:在组态王里做日期查询,优先用字符串拼接 + 严格格式校验,比参数化更可控。
3.3 常用日期查询写法对比
为了照顾不同使用习惯,我把文档里出现的几种常用查询写法整理成对比表:
| 写法 | SQL 示例 | 适用场景 | 注意点 |
|---|---|---|---|
| 等值查询 | WHERE RecordTime = '2024-01-01' | 精确查某天数据 | 当天所有时分秒记录都可能被漏掉,不推荐 |
| 范围查询 | WHERE RecordTime >= '2024-01-01' AND RecordTime < '2024-01-02' | 查某一天完整数据 | 左闭右开,能包含当天全部记录 |
| 带时间为边界 | WHERE RecordTime BETWEEN '2024-01-01 00:00:00' AND '2024-01-31 23:59:59' | 查某月数据 | 结束时间必须带 23:59:59 |
| 月份模糊 | WHERE CONVERT(VARCHAR(7), RecordTime, 120) = '2024-01' | 跨数据库兼容月查询 | 索引失效,数据量大时不建议 |
我在实际项目中用得最多的是第三种,即结束时间强制写成23:59:59。经常有人只写2024-01-31,数据库默认成2024-01-31 00:00:00,导致当天数据全部查不到。这个细节文档里专门提到过,属于反复出现的典型问题。
4. 日期格式处理:三套方案应对不同数据源
4.1 数据库端日期格式化
日期字段在数据库里存的是标准时间类型,但组态王的表格控件显示出来却是2024/1/1 0:00这种格式,看着不专业,也不利于操作员读取。处理方式可以不改控件外观,直接在 SQL 里格式化输出。
SELECT ID, TagName, CONVERT(VARCHAR(19), RecordTime, 120) AS RecordTimeText, Value FROM HistoryData这段 SQL 的CONVERT(VARCHAR(19), RecordTime, 120)会把时间转成YYYY-MM-DD HH:MM:SS格式,样式代码120是 ODBC 标准格式,在 SQL Server 下稳定可靠。用这种方式后,KVADODBGrid 表格里显示的日期就是友好的中文习惯格式,而且不影响 WHERE 条件里的原始字段查询。
如果你用的是 Access,格式化函数要换一种:
SELECT ID, TagName, FORMAT(RecordTime, 'yyyy-mm-dd hh:nn:ss') AS RecordTimeText, Value FROM HistoryDataAccess 的FORMAT函数里分钟是nn,不是mm,写mm会显示成月份。这也是文档里明确提醒过的一个点,挺多人在这个细节上翻车。格式化只影响显示,不会改变底层数据,查询条件仍然用原始 RecordTime 字段比较,性能上不会受太大影响。
4.2 组态王脚本端格式化
有时候不方便改 SQL,或者用户想显示成自定义格式,可以让组态王脚本在拿到查询结果后自行处理。KVADODBGrid 控件提供了按列访问数据的能力,脚本可以遍历记录集,把日期列重新格式化:
string sValue; sValue = KVADODBGrid1.GetCellText(2, nRow); // sValue 形如 2024-01-01 12:30:00这种方式灵活,但有个明显的代价:如果表格有几百行,遍历会明显拖慢画面刷新速度。组态王的脚本执行效率本来就不高,建议只在记录条数少(少于 100 条)的场景用,数据量大时尽量走 SQL 端格式化。
4.3 输入端的格式规范化
用户输入日期是查询体验的起点,如果这一步格式不统一,后面 SQL 写得再严谨也白搭。我习惯在画面脚本中做一次输入校验,判断长度和分隔符,不合规就直接弹窗提示,而不是把错误的日期字符串传给 SQL。
string sInput; int nLen; sInput = StartDateInput; nLen = StrLen(sInput); if (nLen != 10 && nLen != 19) { ShowError("日期格式应为 YYYY-MM-DD 或 YYYY-MM-DD HH:MM:SS"); }这只是一个粗略的长度判断,实际项目里可能还要校验中间的分隔符是不是-,年份是不是四位等。输入校验看起来多写几行脚本,但能挡住一大批“查不出数据”的低级问题。文档里对这部分也做了说明,强调输入格式规范化是日期查询稳定性的第一道防线。
5. 避坑指南:KVADODBGrid 日期查询常见问题排查
5.1 查询结果为空,但 SQL 在数据库工具里能查出数据
现象:同样的 SQL 语句放到数据库查询工具里执行有结果,但组态王画面上 KVADODBGrid 表格一片空白。
原因:最常见的是日期字符串两端的单引号在脚本拼接时丢了。组态王脚本字符串中有时候会默认过滤某些字符,或者你拼接时写成了双引号。数据库工具里双引号也能查,但组件执行时走的是 OLEDB 引擎,对引号类型更严格。
解决:把 SQL 里日期条件的引号统一改成单引号,并且在脚本中用\转义或者用变量拆分拼接。我习惯把整个 SQL 先赋给一个字符串变量,再传给控件,这样可以在脚本里随时打印出来排查。组态王有日志输出函数,把拼接后的 SQL 写到日志文件里,一眼就能看出引号问题。
5.2 结束日期当天数据查不到
现象:查询某月数据,只查到 1 号到 30 号,31 号的记录一条都没有。
原因:结束日期写的是2024-01-31,数据库默认转成2024-01-31 00:00:00,而当天记录时间都在 00:00:00 之后,范围条件限定时它们全部被排除。
解决:结束日期拼接时加上23:59:59后缀,或者把结束条件改成< '2024-02-01',也就是用第二天的零点作为开区间边界。这两种写法都能包含完整的一天。从那以后我写日期范围查询,结束条件一律用左闭右开写法,再没出过漏数据的问题。
5.3 查询后表格不刷新,显示的还是旧数据
现象:第一次查询有结果,换一个日期范围再查,表格内容不变。
原因:KVADODBGrid 控件的记录集没有重新加载。给控件赋值新的 RecordSource 字符串后,必须触发一次加载动作。在属性窗口里改 SQL 会自动刷新,但脚本运行时改 SQL 不会自动重查。
解决:脚本里给 RecordSource 赋值后,调用控件的Refresh()方法或者重新执行Query()。有时候Refresh()不生效,可以先把DataSourceName置空再赋回原值,强制控件重建连接。这种“置空再赋值”的写法属于组态王脚本的玄学技巧,但在多个版本上验证过确实有效。
5.4 日期格式混入中文或斜杠,报语法错误
现象:SQL 语句执行报错,提示日期语法不正确,或者查询结果包含大量不需要的行。
原因:用户输入框里填了2024年1月1日、2024/1/1这种非标准格式,数据库无法解析。
解决:在脚本中对输入变量做预处理,把年、月、日、/全部替换为-。替换函数用组态王的StrReplace或者循环遍历字符处理,替换完再做一次格式校验。如果项目里同时存在多种输入方式,最好统一用一个转换函数,避免每个画面各写一套导致格式漂移。
5.5 日期字段包含毫秒,精确查询对不上
现象:数据库里明明有一条2024-01-01 12:00:00.123的记录,用= '2024-01-01 12:00:00'查不出来。
原因:SQL Server 的 DATETIME 类型实际存储精度到毫秒,显示时默认不显示小数部分。直接用等值比较时,字符串里的时间会被转成2024-01-01 12:00:00.000,与实际的.123不匹配。
解决:日期记录带毫秒时,查询不准用等值,必须改成范围查询。比如>= '2024-01-01 12:00:00' AND < '2024-01-01 12:00:01'。这个场景在设备高频采集数据时特别常见,因为采集时间间隔可能只有几百毫秒,两个记录时间显示相同但存储值不同。
6. 进阶用法:动态日期范围、运行时验证与性能排查
6.1 动态时间范围:默认查最近一小时
实际运行中,很多组态画面需要默认展示最近一段时间的数据,比如设备故障前后十分钟的记录。可以用组态王系统时间函数动态计算起止时间。
string sStart, sEnd; sEnd = TimeToStr(GetServerTime(0), "yyyy-mm-dd hh:nn:ss"); sStart = TimeToStr(GetServerTime(0) - 3600, "yyyy-mm-dd hh:nn:ss");组态王的时间函数返回值是基于秒的整数,减去 3600 就是一小时前。TimeToStr的格式串里分钟是nn,这一点和 Access 的FORMAT一致,我在第一次用的时候写成了mm,结果显示出来全是 00 分钟,排查了半天才发现是格式串的问题。
动态时间的意义在于操作员不需要手动输入起止时间,画面打开就能看到最近数据,这对日常巡检和事故追忆都很有用。
6.2 运行时验证:把 SQL 打到日志文件里
排查日期查询问题最有效的手段是把实际执行的 SQL 打到日志文件。组态王脚本自带日志函数,在调试阶段给每个查询操作加一行输出,比在画面上弹窗更直观,而且不影响运行。
LogMessage("D:\kvlog\query.log", "SQL=" + sSql, 1);这句脚本会把拼接好的 SQL 追加到指定目录的文本文件里。如果查询异常,直接打开日志看 SQL 长什么样,是引号丢了、日期格式不对、还是表名拼错,一目了然。我建议任何新做的查询画面都把这个日志开关留在代码里,平时注释掉,排查时放开。这比对着画面猜原因高效得多。
那以后凡是涉及 KVADODBGrid 日期查询的画面,我都会先跑一遍最小化验证流程:单表、单条件、已知数据,确认 SQL 正确后再往上加条件。这套流程救过我好几次,希望帮到你。
本文还有配套的精品资源,点击获取