You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Firebird 2.5与Delphi 10.2/FireDAC的工业设备时序数据补全查询方案咨询

解决方案:用SQL生成完整时间-设备维度 + 左连接补全数据

完全可以通过SQL语句(或者封装成存储过程)实现这个需求,不需要客户端代码来补全,这样能节省内存和时间。核心思路是先构建出你需要的所有时间戳点 + 所有目标设备的组合,然后和原历史表做左连接,就能保证每个时间点每个设备都有一行记录,没有数据的字段自动为NULL。

1. 具体SQL实现(以你的示例场景为例)

这里用Firebird 2.5支持的递归CTE来生成连续的时间序列,再和设备列表做交叉连接,最后左连接原表数据:

WITH RECURSIVE time_series AS (
    -- 起始时间:2022-06-26 00:00:00
    SELECT CAST('2022-06-26' AS TIMESTAMP) AS interval_tstamp
    FROM RDB$DATABASE
    UNION ALL
    -- 每次增加3分钟,直到超过结束时间
    SELECT DATEADD(MINUTE, 3, interval_tstamp)
    FROM time_series
    WHERE interval_tstamp < CAST('2022-06-27' AS TIMESTAMP)
),
target_devices AS (
    -- 目标设备列表:11、12
    SELECT 11 AS avcid FROM RDB$DATABASE
    UNION ALL
    SELECT 12 AS avcid FROM RDB$DATABASE
)
-- 交叉连接得到所有时间-设备组合,再左连接历史数据
SELECT 
    ts.interval_tstamp AS tstamp,
    td.avcid,
    ah.value1, ah.value2 -- 这里可以指定你需要的具体字段,替换成实际列名
FROM time_series ts
CROSS JOIN target_devices td
LEFT JOIN avc_history ah 
    ON ah.avcid = td.avcid 
    AND ah.tstamp = ts.interval_tstamp
ORDER BY ts.interval_tstamp, td.avcid;

代码解释:

  • time_series:递归生成从起始时间到结束时间,每3分钟一个的时间戳序列,确保每个时间间隔点都存在。如果需要其他间隔,只需要修改DATEADD里的分钟数即可。
  • target_devices:定义你要查询的设备ID列表,如果有专门的设备表(比如devices),可以直接替换成SELECT avcid FROM devices WHERE avcid IN (11,12),更灵活。
  • CROSS JOIN:把时间序列和设备列表做笛卡尔积,得到所有需要的时间-设备组合(这就是你需要的完整维度,保证每个时间点每个设备都有一条基础记录)。
  • LEFT JOIN:关联原历史表,匹配到数据就返回对应字段,匹配不到则返回NULL,完美满足你的需求。

2. 封装成存储过程(复用场景)

如果需要多次复用这个逻辑,可以封装成Firebird存储过程,传入起始时间、结束时间、间隔分钟数、设备列表参数,返回结果集。示例如下:

CREATE PROCEDURE GetAvcHistoryWithFill(
    StartTstamp TIMESTAMP,
    EndTstamp TIMESTAMP,
    IntervalMinutes INTEGER,
    DeviceIds VARCHAR(100) -- 比如传入'11,12'
)
RETURNS (
    Tstamp TIMESTAMP,
    Avcid INTEGER,
    -- 这里添加你需要的其他字段,根据实际表结构调整
    Value1 DOUBLE PRECISION,
    Value2 INTEGER
)
AS
DECLARE VARIABLE CurrentTstamp TIMESTAMP;
DECLARE VARIABLE DeviceId INTEGER;
DECLARE VARIABLE IdPos INTEGER;
DECLARE VARIABLE NextCommaPos INTEGER;
BEGIN
    -- 初始化时间序列起始点
    CurrentTstamp = StartTstamp;
    WHILE CurrentTstamp < EndTstamp DO
    BEGIN
        -- 遍历传入的设备ID列表
        IdPos = 1;
        WHILE IdPos <= CHAR_LENGTH(DeviceIds) DO
        BEGIN
            -- 截取单个设备ID
            NextCommaPos = POSITION(',' IN DeviceIds FROM IdPos);
            IF NextCommaPos = 0 THEN
                NextCommaPos = CHAR_LENGTH(DeviceIds) + 1;
            DeviceId = CAST(SUBSTRING(DeviceIds FROM IdPos FOR NextCommaPos - IdPos) AS INTEGER);
            
            -- 尝试从历史表获取对应数据
            SELECT ah.Value1, ah.Value2
            INTO :Value1, :Value2
            FROM avc_history ah
            WHERE ah.avcid = :DeviceId AND ah.tstamp = :CurrentTstamp;
            
            -- 返回记录(如果没有匹配数据,Value1/Value2会自动为NULL)
            Tstamp = CurrentTstamp;
            Avcid = DeviceId;
            SUSPEND;
            
            -- 移动到下一个设备ID
            IdPos = NextCommaPos + 1;
        END;
        
        -- 移动到下一个时间间隔点
        CurrentTstamp = DATEADD(MINUTE, :IntervalMinutes, CurrentTstamp);
    END
END;

调用存储过程的方式:

EXECUTE PROCEDURE GetAvcHistoryWithFill('2022-06-26', '2022-06-27', 3, '11,12');

3. FireDAC相关说明

FireDAC本身没有专门的功能来生成这种补全后的数据集,但它完全支持执行上述复杂SQL和存储过程,并且可以通过TFDQuery组件来获取返回的结果集,直接绑定到网格或报表控件上。你只需要把SQL语句赋值给TFDQuery.SQL属性,执行Open方法即可,FireDAC会帮你处理所有的数据返回逻辑,不需要额外的代码处理。

内容的提问来源于stack exchange,提问作者SteveS

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 01:37:48