TDengine 中 InterP 函数用户手册
TDengine INTERP 函数用户使用手册
1. 概述
1.1 功能介绍
INTERP 函数是 TDengine 中用于数据插值的重要函数,主要用于在时间序列数据中进行数据插值和填充。该函数可以根据指定的时间点和插值方法,计算出对应时间点的数据值。
1.2 应用场景
在工业物联网、智能电表、传感器监控等场景中,经常遇到以下问题:
- 数据采集不均匀,存在时间间隔
- 需要在特定时间点获取数据值
- 数据缺失需要进行合理的估算
- 需要将不同采样频率的数据统一到相同的时间刻度
2. 函数语法
INTERP(expr [, ignore_null_values]) [RANGE(timestamp1, timestamp2) EVERY(interval) [FILL(fill_mode)]]
2.1 参数说明
- expr: 要进行插值的表达式,通常是列名
- ignore_null_values: 可选参数,值为 0 或 1
0
: 不忽略 NULL 值(默认)1
: 忽略 NULL 值
- RANGE: 指定插值的时间范围
timestamp1
: 起始时间timestamp2
: 结束时间- 支持时间范围扩展:
RANGE('2023-01-01 00:00:00', 10s)
表示在时间点前后 10s 范围内查找数据
- EVERY: 指定插值的时间间隔
interval
: 时间间隔,如 1a(毫秒)、1s(秒)、1m(分)、1h(小时)、1d(天)、1w(周)
- FILL: 指定填充模式
NULL
: 使用 NULL 填充(默认)VALUE
: 使用指定值填充PREV
: 使用前一个值填充NEXT
: 使用后一个值填充LINEAR
: 线性插值NONE
: 不填充NEAR
: 使用距离当前时间点最近的数据进行插值(v3.3.4.9+)
2.2 返回数据类型
返回数据类型与字段类型相同。
2.3 适用数据类型
主要适用于数值类型数据。
2.4 适用范围
适用于表和超级表查询。
2.5 重要使用说明
- 专用语法: INTERP 函数使用专用的 interp 子句语法,当 SQL 语句中存在 interp 子句时,只能查询 INTERP 函数,不能与其他函数一起查询
- 语法限制: interp 子句与窗口子句(window_clause)、分组子句(group_by_clause)不能同时使用
- 必要组合: INTERP 函数需要与 RANGE、EVERY 和 FILL 子句一起使用;流计算不支持使用 RANGE,但需要与 EVERY 和 FILL 关键字一起使用
- 时间范围: RANGE(timestamp1, timestamp2) 需满足 timestamp1 <= timestamp2
- 单点插值: 可以在 RANGE 字段中只指定唯一的时间戳对单个时间点进行插值,此时 EVERY 字段可以省略
- 超级表处理: 作用于超级表时,会将该超级表下的所有子表数据按照主键列排序后进行插值计算,也可以搭配 PARTITION BY tbname 使用
- FILL 行为: 在 FILL PREV/NEXT/NEAR 时,当截面存在数据时不会进行 FILL,即便当前值为 NULL
- 复合主键: 对于带复合主键的表,若存在相同时间戳的数据,则只有对应的复合主键最小的数据参与运算
2.6 特殊伪列支持
- _irowts: 返回插值点所对应的时间戳(v3.0.2.0+)
- _isfilled: 显示返回结果是否为原始记录或插值算法产生的数据(v3.0.3.0+)
- _irowts_origin: 仅在 FILL PREV/NEXT/NEAR 模式时可用,返回 interp 函数所使用的原始数据的时间戳列(v3.3.4.9+)
3. 主要使用场景
3.1 智能电表数据插值
场景描述
智能电表数据采集可能存在网络中断或设备故障导致的数据缺失,需要进行数据插值以保证数据的连续性。
示例代码
-- 场景1:电压数据线性插值
-- 在过去1小时内,每分钟插值一次电压数据
SELECT ts,INTERP(电压)
FROM tdasset.vt_em-6
RANGE('2024-01-01 10:00:00', '2024-01-01 11:00:00')
EVERY(1m)
FILL(LINEAR);-- 场景2:功率数据前值填充,忽略NULL值
-- 每30秒插值一次功率数据,使用前一个值填充,忽略NULL值
SELECT ts,INTERP(功率, 1)
FROM tdasset.vt_em-6
RANGE('2024-01-01 10:00:00', '2024-01-01 12:00:00')
EVERY(30s)
FILL(PREV);-- 场景3:单点插值
-- 在特定时间点进行插值,不需要EVERY子句
SELECT ts,INTERP(电压)
FROM tdasset.vt_em-6
RANGE('2024-01-01 10:30:00')
FILL(LINEAR);-- 场景4:使用伪列获取插值信息
-- 获取插值结果并显示是否为插值数据
SELECT ts,INTERP(电压) AS 电压值,_isfilled AS 是否插值,_irowts AS 插值时间戳
FROM tdasset.vt_em-6
RANGE('2024-01-01 10:00:00', '2024-01-01 11:00:00')
EVERY(1m)
FILL(LINEAR);
3.2 传感器数据同步
场景描述
不同传感器的采样频率可能不同,需要将它们统一到相同的时间刻度进行对比分析。
示例代码
-- 场景3:多相电流数据同步
-- 将三相电流数据统一到每10秒一个采样点
SELECT ts,INTERP(电流_A相) AS 电流_A,INTERP(电流_B相) AS 电流_B,INTERP(电流_C相) AS 电流_C
FROM tdasset.vt_em-7
RANGE('2024-01-01 09:00:00', '2024-01-01 10:00:00')
EVERY(10s)
FILL(LINEAR);-- 场景4:温度和湿度数据对齐
-- 将温度和湿度数据对齐到相同的时间点
SELECT ts,INTERP(温度) AS 温度值,INTERP(湿度) AS 湿度值
FROM sensor_data
RANGE('2024-01-01 08:00:00', '2024-01-01 16:00:00')
EVERY(5m)
FILL(LINEAR);
3.3 数据质量检测
场景描述
通过插值可以检测数据的异常值和缺失情况,用于数据质量评估。
示例代码
-- 场景5:电压异常检测
-- 通过插值检测电压数据的连续性
SELECT ts,INTERP(电压) AS 插值电压,ABS(电压 - INTERP(电压)) AS 偏差值
FROM tdasset.vt_em-6
RANGE('2024-01-01 00:00:00', '2024-01-01 23:59:59')
EVERY(1m)
FILL(LINEAR)
WHERE ABS(电压 - INTERP(电压)) > 10;-- 场景6:功率数据完整性检查
-- 检查功率数据的缺失情况
SELECT ts,INTERP(功率) AS 插值功率,CASE WHEN INTERP(功率) IS NULL THEN '数据缺失'ELSE '数据正常'END AS 数据状态
FROM tdasset.vt_em-6
RANGE('2024-01-01 00:00:00', '2024-01-02 00:00:00')
EVERY(1m)
FILL(NULL);
3.4 报表生成
场景描述
生成定时报表时,需要在固定的时间点获取数据值,即使原始数据不在这些时间点。
示例代码
-- 场景7:小时报表数据生成
-- 生成每小时整点的电表数据报表
SELECT ts AS 报表时间,INTERP(电压) AS 小时电压,INTERP(电流) AS 小时电流,INTERP(功率) AS 小时功率
FROM tdasset.vt_em-6
RANGE('2024-01-01 00:00:00', '2024-01-01 23:59:59')
EVERY(1h)
FILL(LINEAR);-- 场景8:日报表数据汇总
-- 生成每日8点的电表数据快照
SELECT ts AS 日期,INTERP(电压) AS 日电压,INTERP(累计用电量) AS 日用电量
FROM tdasset.vt_em-6
RANGE('2024-01-01 08:00:00', '2024-01-31 08:00:00')
EVERY(1d)
FILL(PREV);
3.5 实时监控告警
场景描述
在实时监控系统中,需要在特定时间点检查设备状态,触发告警。
示例代码
-- 场景9:实时电压监控
-- 每分钟检查电压是否超过阈值
SELECT ts,INTERP(电压) AS 当前电压,CASE WHEN INTERP(电压) > 250 THEN '电压过高告警'WHEN INTERP(电压) < 200 THEN '电压过低告警'ELSE '电压正常'END AS 告警状态
FROM tdasset.vt_em-6
RANGE(NOW() - INTERVAL 1 HOUR, NOW())
EVERY(1m)
FILL(LINEAR);-- 场景10:设备连接状态检查
-- 每30秒检查设备是否有数据上报
SELECT ts,INTERP(电流) AS 电流值,CASE WHEN INTERP(电流) IS NULL THEN '设备离线'ELSE '设备在线'END AS 连接状态
FROM tdasset.vt_em-6
RANGE(NOW() - INTERVAL 10 MINUTES, NOW())
EVERY(30s)
FILL(NULL);
4. 填充模式详解
4.1 LINEAR(线性插值)
适用于连续变化的数据,如温度、电压等。
-- 线性插值示例
SELECT ts,INTERP(温度) AS 插值温度
FROM sensor_data
RANGE('2024-01-01 10:00:00', '2024-01-01 11:00:00')
EVERY(1m)
FILL(LINEAR);
4.2 PREV(前值填充)
适用于状态类数据,如开关状态、设备模式等。
-- 前值填充示例
SELECT ts,INTERP(设备状态) AS 当前状态
FROM device_status
RANGE('2024-01-01 08:00:00', '2024-01-01 18:00:00')
EVERY(10m)
FILL(PREV);
4.3 NEXT(后值填充)
适用于预测性数据填充。
-- 后值填充示例
SELECT ts,INTERP(预测功率) AS 预测值
FROM power_forecast
RANGE('2024-01-01 12:00:00', '2024-01-01 13:00:00')
EVERY(15m)
FILL(NEXT);
4.4 VALUE(指定值填充)
适用于有默认值的场景。
-- 指定值填充示例
SELECT ts,INTERP(报警级别) AS 报警级别
FROM alarm_log
RANGE('2024-01-01 00:00:00', '2024-01-01 23:59:59')
EVERY(1h)
FILL(VALUE, 0);
4.5 NEAR(最近值填充)
适用于需要使用距离当前时间点最近的数据进行插值的场景(v3.3.4.9+)。
-- 最近值填充示例
SELECT ts,INTERP(电压) AS 电压值
FROM tdasset.vt_em-6
RANGE('2024-01-01 10:00:00', '2024-01-01 11:00:00')
EVERY(1m)
FILL(NEAR);-- 时间范围扩展示例
-- 在指定时间点前后10秒范围内查找数据进行插值
SELECT ts,INTERP(电压) AS 电压值
FROM tdasset.vt_em-6
RANGE('2024-01-01 10:30:00', 10s)
FILL(PREV, 220);
4.6 ignore_null_values 参数使用
控制是否忽略 NULL 值的参数,对数据插值结果有重要影响。
-- 不忽略NULL值(默认行为)
SELECT ts,INTERP(电压, 0) AS 电压值
FROM tdasset.vt_em-6
RANGE('2024-01-01 10:00:00', '2024-01-01 11:00:00')
EVERY(1m)
FILL(LINEAR);-- 忽略NULL值
SELECT ts,INTERP(电压, 1) AS 电压值
FROM tdasset.vt_em-6
RANGE('2024-01-01 10:00:00', '2024-01-01 11:00:00')
EVERY(1m)
FILL(LINEAR);-- 对比NULL值处理的差异
SELECT ts,INTERP(电压, 0) AS 包含NULL,INTERP(电压, 1) AS 忽略NULL
FROM tdasset.vt_em-6
RANGE('2024-01-01 10:00:00', '2024-01-01 11:00:00')
EVERY(1m)
FILL(LINEAR);
5. 高级特性与应用
5.1 伪列的使用
TDengine 提供了多个伪列来增强 INTERP 函数的功能。
-- 使用_isfilled判断数据是否为插值结果
SELECT ts,INTERP(电压) AS 电压值,_isfilled AS 是否插值,CASE WHEN _isfilled = 1 THEN '插值数据'ELSE '原始数据'END AS 数据类型
FROM tdasset.vt_em-6
RANGE('2024-01-01 10:00:00', '2024-01-01 11:00:00')
EVERY(1m)
FILL(LINEAR);-- 使用_irowts获取插值时间戳
SELECT ts,INTERP(电压) AS 电压值,_irowts AS 插值时间戳,_isfilled AS 是否插值
FROM tdasset.vt_em-6
RANGE('2024-01-01 10:00:00', '2024-01-01 11:00:00')
EVERY(1m)
FILL(LINEAR);-- 使用_irowts_origin获取原始数据时间戳(仅PREV/NEXT/NEAR模式)
SELECT ts,INTERP(电压) AS 电压值,_irowts_origin AS 原始数据时间戳,_isfilled AS 是否插值
FROM tdasset.vt_em-6
RANGE('2024-01-01 10:00:00', '2024-01-01 11:00:00')
EVERY(1m)
FILL(PREV);
5.2 超级表的处理
-- 超级表插值(所有子表数据按主键排序)
SELECT ts,INTERP(电压) AS 电压值,tbname
FROM tdasset.meters
RANGE('2024-01-01 10:00:00', '2024-01-01 11:00:00')
EVERY(1m)
FILL(LINEAR);-- 使用PARTITION BY tbname强制规约到单个时间线
SELECT ts,INTERP(电压) AS 电压值,tbname
FROM tdasset.meters
RANGE('2024-01-01 10:00:00', '2024-01-01 11:00:00')
EVERY(1m)
FILL(LINEAR)
PARTITION BY tbname;
5.3 时间范围扩展功能
-- 时间范围扩展:在指定时间点前后查找数据
-- 在2024-01-01 10:30:00前后10秒范围内查找数据
SELECT ts,INTERP(电压) AS 电压值
FROM tdasset.vt_em-6
RANGE('2024-01-01 10:30:00', 10s)
FILL(PREV, 220);-- 使用NEXT模式在时间点后查找数据
SELECT ts,INTERP(电压) AS 电压值
FROM tdasset.vt_em-6
RANGE('2024-01-01 10:30:00', 30s)
FILL(NEXT, 220);-- 使用NEAR模式在时间点前后查找最近数据
SELECT ts,INTERP(电压) AS 电压值
FROM tdasset.vt_em-6
RANGE('2024-01-01 10:30:00', 15s)
FILL(NEAR, 220);
6. 在流计算中的应用
6.1 流计算中的 INTERP 使用
在流计算中,INTERP 不支持使用 RANGE,但需要与 EVERY 和 FILL 关键字一起使用。
-- 流计算中使用 INTERP 进行数据补齐
CREATE STREAM IF NOT EXISTS voltage_interp_stream
INTERVAL(1m)
FROM tdasset.vt_em-6
INTO voltage_interp_result
AS SELECT _twstart AS ts,INTERP(电压) AS 平均电压
FROM tdasset.vt_em-6
WHERE ts >= _twstart AND ts <= _twend
EVERY(10s)
FILL(LINEAR);-- 流计算中忽略NULL值的插值
CREATE STREAM IF NOT EXISTS current_interp_stream
INTERVAL(1m)
FROM tdasset.vt_em-6
INTO current_interp_result
AS SELECT _twstart AS ts,INTERP(电流, 1) AS 电流值
FROM tdasset.vt_em-6
WHERE ts >= _twstart AND ts <= _twend
EVERY(30s)
FILL(PREV);
6.2 告警场景应用
-- 基于插值数据的告警流
CREATE STREAM IF NOT EXISTS voltage_alarm_stream
INTERVAL(1m)
FROM tdasset.vt_em-6
INTO voltage_alarm_result
AS SELECT _twstart AS ts,CASE WHEN INTERP(电压, 1) > 250 THEN '高压告警'WHEN INTERP(电压, 1) < 200 THEN '低压告警'ELSE '正常'END AS 告警状态,INTERP(电压, 1) AS 电压值
FROM tdasset.vt_em-6
WHERE ts >= _twstart AND ts <= _twend
EVERY(30s)
FILL(LINEAR);-- 基于插值数据的功率监控流
CREATE STREAM IF NOT EXISTS power_monitor_stream
INTERVAL(5m)
FROM tdasset.vt_em-6
INTO power_monitor_result
AS SELECT _twstart AS ts,INTERP(功率, 1) AS 当前功率,CASE WHEN INTERP(功率, 1) > 5000 THEN '功率过高'WHEN INTERP(功率, 1) < 100 THEN '功率异常'ELSE '正常'END AS 功率状态
FROM tdasset.vt_em-6
WHERE ts >= _twstart AND ts <= _twend
EVERY(1m)
FILL(PREV);
7. 性能优化建议
7.1 时间范围优化
-- 避免过大的时间范围
-- 推荐:限制在合理的时间范围内
SELECT ts, INTERP(电压)
FROM tdasset.vt_em-6
RANGE('2024-01-01 10:00:00', '2024-01-01 11:00:00')
EVERY(1m)
FILL(LINEAR);-- 不推荐:时间范围过大
-- SELECT ts, INTERP(电压)
-- FROM tdasset.vt_em-6
-- RANGE('2024-01-01 00:00:00', '2024-12-31 23:59:59')
-- EVERY(1s)
-- FILL(LINEAR);
7.2 插值间隔优化
-- 根据数据特点选择合适的插值间隔
-- 对于电压数据,1分钟间隔通常足够
SELECT ts, INTERP(电压)
FROM tdasset.vt_em-6
RANGE('2024-01-01 10:00:00', '2024-01-01 11:00:00')
EVERY(1m)
FILL(LINEAR);
7.3 ignore_null_values 参数优化
-- 当数据中包含较多NULL值时,使用ignore_null_values=1可以提高插值效果
SELECT ts, INTERP(电压, 1)
FROM tdasset.vt_em-6
RANGE('2024-01-01 10:00:00', '2024-01-01 11:00:00')
EVERY(1m)
FILL(LINEAR);
8. 注意事项
8.1 数据类型限制
- INTERP 函数主要适用于数值类型数据
- 对于字符串类型,建议使用 PREV 或 NEXT 填充模式
8.2 NULL 值处理
- 当选择 FILL(NULL) 时,缺失的数据点会返回 NULL
- 使用 ignore_null_values=1 可以在插值计算中忽略 NULL 值
- 在进行后续计算时需要考虑 NULL 值的影响
8.3 性能考虑
- 插值计算会消耗额外的计算资源
- 在大数据量场景下,建议合理设置时间范围和插值间隔
- 使用伪列会增加额外的计算开销
8.4 语法限制
- INTERP 函数不能与其他函数一起查询
- 不能与窗口子句、分组子句同时使用
- 在流计算中不支持 RANGE 子句
8.5 版本兼容性
- _irowts 伪列需要 v3.0.2.0 及以上版本
- _isfilled 伪列需要 v3.0.3.0 及以上版本
- NEAR 填充模式和时间范围扩展需要 v3.3.4.9 及以上版本
- _irowts_origin 伪列需要 v3.3.4.9 及以上版本
9. 常见问题解答
Q1: INTERP 函数与 FILL 子句的区别?
A1: INTERP 函数专门用于在指定时间点进行插值,而 FILL 子句主要用于在聚合查询中填充空值。INTERP 提供了更精确的时间点插值控制和更多的填充选项。
Q2: 如何选择合适的填充模式?
A2:
- 连续变化的数据(如温度、电压):使用 LINEAR
- 状态类数据(如开关状态):使用 PREV
- 需要最近数据的场景:使用 NEAR
- 有明确默认值的场景:使用 VALUE
Q3: ignore_null_values 参数什么时候使用?
A3: 当数据中包含较多 NULL 值,并且希望在插值计算中忽略这些 NULL 值时使用。设置为 1 可以提高插值的连续性和准确性。
Q4: INTERP 函数对性能的影响?
A4: INTERP 函数会增加计算负担,特别是在大时间范围和高频插值的情况下。建议根据实际需求合理设置参数,避免过度密集的插值计算。
Q5: 能否在子查询中使用 INTERP?
A5: 可以,但需要注意子查询的时间范围设置,确保能够获取到足够的数据进行插值计算。
Q6: 时间范围扩展功能如何使用?
A6: 使用 RANGE(‘时间点’, 时间范围) 的格式,如 RANGE(‘2024-01-01 10:00:00’, 10s) 表示在指定时间点前后 10 秒范围内查找数据。此功能仅支持 PREV/NEXT/NEAR 填充模式。
Q7: 伪列的作用是什么?
A7:
- _isfilled: 判断返回的数据是原始数据还是插值数据
- _irowts: 获取插值点对应的时间戳
- _irowts_origin: 获取插值时使用的原始数据时间戳(仅限PREV/NEXT/NEAR模式)
10. 实践建议
- 合理设置时间范围:避免过大的时间跨度,影响查询性能
- 选择合适的插值间隔:根据数据特点和业务需求选择
- 正确使用 ignore_null_values:在数据包含大量 NULL 值时启用
- 选择正确的填充模式:根据数据类型和业务语义选择
- 合理使用伪列:在需要了解插值详情时使用,但要注意性能开销
- 结合索引优化:确保时间列有适当的索引
- 监控查询性能:在生产环境中监控 INTERP 查询的性能表现
- 版本管理:注意不同版本的功能差异,选择合适的版本使用相应功能
通过合理使用 INTERP 函数及其高级特性,可以有效解决时间序列数据中的插值问题,提高数据分析的准确性和完整性。
关于 TDengine
TDengine 是一款专为物联网、工业互联网等场景设计并优化的大数据平台,其核心模块是高性能、集群开源、云原生、极简的时序数据库。它能安全高效地将大量设备每天产生的高达 TB 甚至 PB 级的数据进行汇聚、存储、分析和分发,并提供 AI 智能体对数据进行预测与异常检测,提供实时的商业洞察。