4. 历史数据查询语言与API

历史库的价值不在于“存了多少点”,而在于“能不能按需把点取出来”。电力监控系统里,一条10kV线路的有功功率可能每秒采集一次,一天就是86400个原始点;若直接全量拉取,既慢又难用。因此,历史数据查询的核心是四件事:用类SQL语言描述要什么、用时间范围限定取哪一段、用插值把不规则时间戳对齐到统一网格、用聚合函数把密集点压缩成有意义的统计量。理解这四层,才能写出既正确又高效的历史查询。

一、SQL-like查询:把“测点”当作表来查

多数历史库(PI、eDNA、InfluxDB、OpenTSDB及国产实时库)都提供类SQL接口。它和关系库最大的区别是:表名通常对应测点或测点集合,时间列是隐含的主键。典型结构如下:

SELECT value, quality
FROM   Line01.ActivePower
WHERE  time BETWEEN '2024-06-01 00:00:00' AND '2024-06-01 23:59:59'
  AND  quality = 'GOOD'
ORDER BY time ASC

其中 Line01.ActivePower 是测点路径(类似表名),time 是时间过滤条件,quality 用于剔除坏点。需要特别注意的是:时间范围过滤是历史查询的第一道性能闸门。没有时间条件的查询在PI等系统中会被拒绝或返回超时,因为历史库按时间分块存储,只有限定区间才能命中对应归档文件。工程上建议:查询跨度不超过24小时时用原始精度;超过7天时先做1分钟或15分钟聚合,再取回结果。

二、插值查询:让不同测点对齐到同一时间轴

实际系统中,线路有功功率、电流、电压的采集时刻往往不完全一致,直接按原始时间戳拼接会出现“同一行里三个值来自三个不同时刻”的问题。插值查询解决的就是这个对齐问题。常见插值方式有三类:

  • 前值保持(Previous/Step):取查询时刻之前最近的一个有效值,适合开关状态、档位等离散量。
  • 线性插值(Linear):在相邻两点间按时间比例计算,适合功率、温度等连续量。
  • 时间加权平均(Time-weighted Average):按每个值持续的时间长度加权,适合计算一段时间的平均功率。

例如要得到每5分钟一个点的24小时有功功率曲线,可写成:

SELECT INTERP(value, '5m', 'LINEAR') AS p_avg
FROM   Line01.ActivePower
WHERE  time BETWEEN '2024-06-01 00:00:00' AND '2024-06-01 23:59:59'
  AND  quality = 'GOOD'

这里 '5m' 是插值周期,'LINEAR' 是插值算法。结果会输出288个等间隔点(24×60÷5),每个点都是该5分钟窗口内的线性插值结果。若改用 'TIME_WEIGHTED',则得到的是时间加权平均值,更贴近电能计量口径。

三、聚合函数:把密集点压缩成统计量

当查询跨度大、原始点太多时,聚合函数是必选项。历史库常用聚合包括:AVG、MAX、MIN、SUM、COUNT、STDDEV、RANGE(最大值减最小值)。它们通常与时间分桶配合使用:

SELECT BUCKET(time, '15m') AS ts,
       AVG(value) AS p_avg,
       MAX(value) AS p_max,
       MIN(value) AS p_min
FROM   Line01.ActivePower
WHERE  time BETWEEN '2024-06-01 00:00:00' AND '2024-06-01 23:59:59'
GROUP BY BUCKET(time, '15m')

这条查询把一天切成96个15分钟桶,每个桶输出平均、最大、最小有功功率。对于负荷分析,MAX用于判断峰值是否越限,AVG用于计算负荷率,RANGE用于评估波动幅度。注意:聚合前必须确认坏点已被过滤,否则一个坏点可能把最大值拉高到不合理水平。

四、常用历史库API:PI SDK、REST、OPC HDA

不同系统提供的访问方式不同,选型时主要看集成环境和实时性要求:

API类型典型场景特点
PI SDK / AF SDKPI System内部应用、C#/Java客户端功能最全,支持批量读取、插值、聚合,需安装客户端库
REST / Web APIWeb前端、跨平台、轻量集成基于HTTP+JSON,易用但大批量查询性能弱于SDK
OPC HDA多厂商历史库互操作、SCADA集成标准化接口,支持ReadRaw、ReadProcessed、ReadAtTime

以PI Web API为例,读取24小时插值曲线的请求大致如下:

GET /piwebapi/streams/{webId}/interpolated
    ?startTime=2024-06-01T00:00:00Z
    &endTime=2024-06-01T23:59:59Z
    &interval=5m
    &filterExpression=quality='GOOD'

OPC HDA则通过 ReadProcessed 方法传入聚合类型和周期,由服务器端完成计算。无论哪种API,核心参数都是:测点标识、起止时间、采样周期、聚合/插值方式、质量过滤。这五个参数决定了返回数据的粒度和可信度。

历史数据查询四层处理链:从原始点到可用曲线 ① 原始归档点 Line01.ActivePower 1s采集 · 86400点/天 含时间戳+值+质量码 时间分块存储 ② 时间范围过滤 WHERE time BETWEEN '00:00:00' AND '23:59:59' AND quality='GOOD' 命中归档块 · 剔除坏点 ③ 插值对齐 INTERP(value,'5m','LINEAR') 前值保持 / 线性 / 时间加权 输出288个等间隔点 统一时间网格 ④ 聚合统计 AVG / MAX / MIN BUCKET(time,'15m') 96个统计桶 负荷率/峰值判断 三种常用历史库API接入方式 PI SDK / AF SDK C#/Java客户端 · 功能最全 批量读取 · 插值 · 聚合 REST / Web API HTTP + JSON · 跨平台 /streams/{webId}/interpolated OPC HDA ReadRaw / ReadProcessed 多厂商互操作 · 标准化 查询五要素(决定返回数据的粒度与可信度) 测点标识 Line01.ActivePower 起止时间 00:00 ~ 23:59 采样周期 5m / 15m / 1h 插值/聚合 LINEAR / AVG 质量过滤 quality='GOOD'

五、实战:编写查询获取某线路24小时有功功率曲线

假设某10kV线路测点为 Line01.ActivePower,单位kW,采集周期1s,需要获取2024年6月1日全天24小时、每5分钟一个点的有功功率曲线,用于绘制趋势图并计算日负荷率。完整实现分三步:

第一步:确定查询参数。测点标识 Line01.ActivePower;起止时间 2024-06-01 00:00:00 至 2024-06-01 23:59:59;采样周期5分钟;插值方式线性;质量过滤 GOOD。预期返回288个点。

第二步:编写查询。若使用PI Web API,请求如下:

GET /piwebapi/streams/{Line01_ActivePower_WebId}/interpolated
    ?startTime=2024-06-01T00:00:00Z
    &endTime=2024-06-01T23:59:59Z
    &interval=5m
    &filterExpression=quality='GOOD'

若使用OPC HDA,则调用 ReadProcessed,参数为:ItemID=Line01.ActivePower、AggregateType=INTERPOLATIVE、ResampleInterval=300000(毫秒)、StartTime/EndTime同上。

第三步:结果判读。返回的288个点应满足:时间戳严格等间隔5分钟;值域在合理范围内(例如该线路额定功率5000kW,则曲线应在0~5000kW之间,若出现负值需检查是否反向潮流或接线极性);质量码全部为GOOD。若某点缺失,插值查询会返回前值或线性补点,但应在结果中标记为“插值点”以便区分。日负荷率可用 AVG/MAX 计算,若平均功率为3200kW、最大功率为4800kW,则日负荷率约为66.7%,属于正常波动范围。

实践提示:查询跨度超过24小时时,建议先用15分钟聚合再取回,避免一次拉取数万个原始点导致接口超时。若曲线出现台阶状突变,优先检查插值方式是否误用了“前值保持”;若曲线整体偏低,检查是否遗漏了质量过滤导致坏点被平均。

掌握这四层查询能力后,历史库就不再是“只能看不能算”的黑盒。无论是画趋势、算负荷率,还是做越限统计,都可以用一条结构清晰的查询完成。关键在于:时间范围先收窄,插值方式选对,聚合粒度匹配分析目标,质量过滤不可省略。