LAST() 函数用户手册
函数定义
LAST(expr)
功能说明
LAST()
函数统计表/超级表中某列的值最后写入的非 NULL 值,即返回时间戳最大的非 NULL 值。
版本要求
- 最低版本: v3.0.0.0
返回值
- 数据类型: 同应用的字段
- 返回内容: 时间戳最大的非 NULL 值及其对应的时间戳
参数说明
参数 | 类型 | 说明 | 取值范围 |
---|---|---|---|
expr | 任意类型 | 要统计的字段表达式或 * | 所有字段类型 |
适用数据类型
- 所有字段类型: 支持所有数据类型,包括数值类型、字符串类型、时间戳类型等
适用范围
- 表类型: 表和超级表
- 查询支持: 支持聚合查询、窗口查询
- 多结果函数: 支持多列返回
使用说明
- NULL 值处理: 忽略 NULL 值,只返回非 NULL 值
- 时间戳关联: 返回值对应时间戳最大的记录
- 复合主键: 对于存在复合主键的表,若最大时间戳的数据有多条,则返回复合主键最大的数据
- 全 NULL 处理:
- 如果某列全部为 NULL 值,则该列返回结果也是 NULL
- 如果所有列全部为 NULL 值,则不返回结果
- LAST(*): 支持查询所有列的最后一个非 NULL 值
- 随机性: 在用于超级表时,时间戳完全一样且同为最大的数据行可能有多个,会从中随机返回一条,不保证多次运行所挑选的数据行必然一致
基本用法示例
单列查询
-- 获取电流的最后一个非 NULL 值
SELECT LAST(current) FROM meters;-- 获取电压的最后一个非 NULL 值
SELECT LAST(voltage) FROM meters;-- 获取相位的最后一个非 NULL 值
SELECT LAST(phase) FROM meters;
多列查询
-- 获取多个字段的最后一个非 NULL 值
SELECT LAST(current), LAST(voltage), LAST(phase) FROM meters;-- 使用 LAST(*) 查询所有列的最后一个非 NULL 值
SELECT LAST(*) FROM meters;
NULL 值处理
-- LAST 函数自动忽略 NULL 值
SELECT LAST(current) FROM meters;
-- 只返回非 NULL 的最后值
智能电表场景应用示例
基于智能电表数据库结构:
-- 数据库和表结构
USE test;
-- meters 超级表包含 ts, current, voltage, phase 字段和 location, groupid 标签
场景1:设备最新状态监控
-- 查找每个电表的最新运行数据
SELECT tbname,LAST(current) as latest_current,LAST(voltage) as latest_voltage,LAST(phase) as latest_phase
FROM meters
GROUP BY tbname;
场景2:按区域查找最新数据
-- 查找每个区域最新的用电记录
SELECT location,LAST(current) as latest_current,LAST(voltage) as latest_voltage,LAST(phase) as latest_phase
FROM meters
GROUP BY location;
场景3:设备组最新状态分析
-- 分析不同设备组的最新运行情况
SELECT groupid,location,LAST(*)
FROM meters
GROUP BY groupid, location
ORDER BY groupid;
场景4:数据完整性检查
-- 检查各电表最新数据的完整性
SELECT tbname,CASE WHEN LAST(current) IS NULL THEN '电流数据缺失'WHEN LAST(voltage) IS NULL THEN '电压数据缺失'WHEN LAST(phase) IS NULL THEN '相位数据缺失'ELSE '数据完整'END as data_status,LAST(current) as latest_current,LAST(voltage) as latest_voltage,LAST(phase) as latest_phase
FROM meters
GROUP BY tbname;
场景5:实时监控面板
-- 获取各电表的实时监控数据
SELECT tbname,location,groupid,LAST(current) as realtime_current,LAST(voltage) as realtime_voltage,LAST(phase) as realtime_phase
FROM meters
GROUP BY tbname, location, groupid
HAVING LAST(current) IS NOT NULL;-- 按区域统计设备状态
SELECT location,COUNT(*) as meter_count,MAX(LAST(current)) as max_current,MIN(LAST(current)) as min_current,MAX(LAST(voltage)) as max_voltage,MIN(LAST(voltage)) as min_voltage
FROM meters
GROUP BY location;
场景6:异常设备识别
-- 识别最近异常的设备
SELECT tbname,location,LAST(current) as latest_current,LAST(voltage) as latest_voltage,CASE WHEN LAST(current) > 25.0 THEN '电流过载'WHEN LAST(voltage) < 200 OR LAST(voltage) > 240 THEN '电压异常'WHEN LAST(phase) < 0 OR LAST(phase) > 360 THEN '相位异常'ELSE '正常'END as device_status
FROM meters
GROUP BY tbname, location
ORDER BY latest_current DESC;
场景7:设备运行时长统计
-- 计算设备从首次记录到最后记录的运行时长
SELECT tbname,location,FIRST(ts) as start_time,LAST(ts) as end_time,LAST(current) as final_current
FROM meters
GROUP BY tbname, location;
场景8:负载变化趋势分析
-- 分析电表负载从初始到最终的变化
SELECT location,FIRST(current) as initial_current,LAST(current) as final_current,LAST(current) - FIRST(current) as current_change,CASE WHEN LAST(current) > FIRST(current) THEN '负载增加'WHEN LAST(current) < FIRST(current) THEN '负载减少'ELSE '负载稳定'END as load_trend
FROM meters
GROUP BY location
HAVING FIRST(current) IS NOT NULL AND LAST(current) IS NOT NULL;
场景9:电能质量最新状态评估(分步查询)
-- 第一步:获取每个电表的最新数据
SELECT tbname,groupid,location,LAST(current) as latest_current,LAST(voltage) as latest_voltage,LAST(phase) as latest_phase
FROM meters
GROUP BY tbname, groupid, location;
场景10:设备维护提醒
-- 根据最后记录时间和数据值进行维护提醒
SELECT tbname,location,LAST(ts) as last_record_time,LAST(current) as last_current_reading,CASE WHEN LAST(ts) < NOW() - 1d THEN '设备可能离线,需要检查'WHEN LAST(current) > 20.0 THEN '电流过高,需要维护'WHEN LAST(voltage) < 200 THEN '电压异常,需要检查'ELSE '设备运行正常'END as maintenance_status
FROM meters
GROUP BY tbname, location
ORDER BY last_record_time ASC;
LAST(*) 的特殊用法
查询所有列的最后值
-- 查询超级表所有列的最后非 NULL 值
SELECT LAST(*) FROM meters;-- 按电表分组查询每个电表的最后记录
SELECT LAST(*) FROM meters GROUP BY tbname;
标签列返回控制
在 TDengine 中,LAST(*)
的行为受 multiResultFunctionStarReturnTags
参数控制:
- 设置为 0(默认): 只返回超级表的普通列
- 设置为 1: 返回超级表的普通列和标签列
-- 设置参数以包含标签列
SET multiResultFunctionStarReturnTags=1;
SELECT LAST(*) FROM meters;-- 恢复默认设置
SET multiResultFunctionStarReturnTags=0;
LAST 函数缓存优化
缓存机制说明
TDengine 通过数据库的 CACHEMODEL
参数来优化 LAST 函数的查询性能。当启用相应的缓存模式后,系统会在内存中缓存子表的最近数据,从而大幅提升 LAST 相关查询的响应速度。
CACHEMODEL 参数详解
CACHEMODEL
表示是否在内存中缓存子表的最近数据,有以下四种模式:
模式 | 说明 | 适用场景 |
---|---|---|
none | 不缓存(默认值) | 内存资源紧张或不常用 LAST 查询 |
last_row | 缓存子表最近一行数据 | 频繁使用 LAST_ROW 函数的场景 |
last_value | 缓存子表每一列的最近非 NULL 值 | 频繁使用 LAST 函数的场景 |
both | 同时开启 last_row 和 last_value 缓存 | 同时需要 LAST 和 LAST_ROW 高性能的场景 |
重要说明: last_value
模式将显著改善无特殊影响(WHERE、ORDER BY、GROUP BY、INTERVAL)下的 LAST 函数的性能表现。
启用 LAST 函数缓存
创建数据库时启用缓存
-- 创建数据库时启用 LAST 函数缓存
CREATE DATABASE test_db CACHEMODEL 'last_value';-- 创建数据库时同时启用 LAST 和 LAST_ROW 缓存
CREATE DATABASE test_db CACHEMODEL 'both';-- 完整的创建示例,包含缓存配置
CREATE DATABASE smart_meter CACHEMODEL 'last_value' CACHESIZE 16 BUFFER 256 PRECISION 'ms';
修改现有数据库启用缓存
-- 为现有数据库启用 LAST 函数缓存
ALTER DATABASE test CACHEMODEL 'last_value';-- 为现有数据库同时启用 LAST 和 LAST_ROW 缓存
ALTER DATABASE test CACHEMODEL 'both';-- 禁用缓存
ALTER DATABASE test CACHEMODEL 'none';
配置缓存大小
-- 设置每个 vnode 用于缓存的内存大小(MB)
ALTER DATABASE test CACHESIZE 32; -- 设置为 32MB-- 同时修改缓存模式和大小
ALTER DATABASE test CACHEMODEL 'both' CACHESIZE 64;
查看缓存配置状态
-- 查看数据库的缓存配置
SELECT NAME, CACHE_MODEL, CACHE_SIZE
FROM INFORMATION_SCHEMA.INS_DATABASES
WHERE NAME='test';-- 查看详细的数据库配置
SHOW CREATE DATABASE test \G;-- 查看所有数据库的缓存配置
SELECT NAME, CACHE_MODEL, CACHE_SIZE
FROM INFORMATION_SCHEMA.INS_DATABASES;
缓存使用效果示例
启用缓存后的高性能查询
-- 启用 last_value 缓存后的高效查询
-- 获取所有电表的最新非 NULL 状态(毫秒级响应)
SELECT tbname, LAST(*) FROM meters GROUP BY tbname;-- 设备在线状态检查(毫秒级响应)
SELECT tbname,location,LAST(ts) as last_seen,CASE WHEN LAST(ts) > NOW() - 5m THEN '在线'ELSE '离线'END as status
FROM meters
GROUP BY tbname, location;
缓存监控与诊断
-- 查看 vgroup 的缓存使用情况
SHOW test.vgroups;-- 检查缓存负载
SELECT * FROM INFORMATION_SCHEMA.INS_DATABASES WHERE NAME='test';
缓存优化最佳实践
1. 缓存模式选择建议
-- 场景1:主要使用 LAST 函数的监控系统
CREATE DATABASE monitoring_db CACHEMODEL 'last_value' CACHESIZE 32;-- 场景2:同时需要 LAST 和 LAST_ROW 的实时系统
CREATE DATABASE realtime_db CACHEMODEL 'both' CACHESIZE 64;-- 场景3:内存敏感的环境
CREATE DATABASE minimal_db CACHEMODEL 'none';
2. 缓存大小调优
根据文档说明,可以通过以下方式判断和调整缓存大小:
-- 1. 查看当前 cachesize(单位:MB)
SELECT NAME, CACHE_SIZE FROM INFORMATION_SCHEMA.INS_DATABASES WHERE NAME='test';-- 2. 查看 cacheload(单位:Byte)
SHOW test.vgroups;-- 3. 根据负载情况调整
-- 如果 cacheload 非常接近 cachesize,则 cachesize 可能过小
-- 如果 cacheload 明显小于 cachesize 则 cachesize 是够用的-- 小规模部署(< 1000 设备)
ALTER DATABASE test CACHESIZE 8;-- 中等规模部署(1000-10000 设备)
ALTER DATABASE test CACHESIZE 32;-- 大规模部署(> 10000 设备)
ALTER DATABASE test CACHESIZE 128;
3. 监控缓存效果
-- 创建缓存性能监控查询
SELECT NAME as db_name,CACHE_MODEL,CACHE_SIZE,BUFFER
FROM INFORMATION_SCHEMA.INS_DATABASES
WHERE NAME = 'test';-- 查看 vgroup 缓存负载
SHOW test.vgroups;
缓存注意事项与限制
- 内存消耗: 启用缓存会增加内存使用,CACHESIZE 范围为 [1, 65536] MB
- 数据一致性: 缓存数据与磁盘数据保持强一致性,无需担心数据不一致
- 适用场景: 特别适合频繁查询最新数据的实时监控场景
- 模式切换警告: CACHEMODEL 值来回切换有可能导致 last/last_row 的查询结果不准确,建议保持稳定配置(推荐保持打开)
- 缓存预热: 新启用缓存后,第一次查询可能稍慢,后续查询会显著加速
- 查询限制: last_value 缓存主要优化无特殊影响(WHERE、ORDER BY、GROUP BY、INTERVAL)下的 LAST 函数查询
与其他函数的对比
LAST vs FIRST
-- 对比首个值和最后值
SELECT location,FIRST(current) as first_current,LAST(current) as last_current,LAST(current) - FIRST(current) as current_change,FIRST(ts) as first_time,LAST(ts) as last_time
FROM meters
GROUP BY location;
LAST vs MAX
-- LAST 返回时间最晚的值,MAX 返回数值最大的值
SELECT location,LAST(current) as latest_current, -- 时间最晚的电流值MAX(current) as maximum_current -- 数值最大的电流值
FROM meters
GROUP BY location;
LAST vs LAST_ROW
-- LAST 和 LAST_ROW 的区别
SELECT location,LAST(current) as last_non_null_current, -- 最后的非NULL电流值LAST_ROW(current) as last_row_current -- 最后一行的电流值(可能为NULL)
FROM meters
GROUP BY location;
TDengine 函数嵌套限制说明
不支持的嵌套方式
-- 以下写法都会报错:
-- AVG(LAST(current)) -- 聚合函数嵌套选择函数
-- SUM(FIRST(voltage)) -- 聚合函数嵌套选择函数
-- COUNT(TOP(current, 1)) -- 聚合函数嵌套选择函数
-- MAX(LAST(phase)) -- 聚合函数嵌套选择函数
推荐的替代方案
- 分步查询:先用 LAST 获取数据,再在外层或应用层计算聚合
- 使用单层聚合:直接使用 MAX、MIN、COUNT 等函数
- 子查询:如果支持,使用子查询分离不同层次的聚合
正确的查询模式
-- 模式1:单层查询
SELECT location,LAST(current) as latest_current,MAX(current) as max_current,MIN(current) as min_current,COUNT(*) as record_count
FROM meters
GROUP BY location;-- 模式2:分层查询(推荐)
-- 第一层:获取每个设备的最新数据
SELECT tbname,location, LAST(current) as latest_current
FROM meters
GROUP BY tbname, location;
-- 第二层:在应用层计算区域统计
性能优化建议
1. 缓存优化
-- 为高频 LAST 查询启用缓存
ALTER DATABASE test CACHEMODEL 'last_value' CACHESIZE 64;
2. 查询优化
-- 使用合适的时间范围过滤
SELECT LAST(current) FROM meters
WHERE ts >= NOW() - 1d
GROUP BY location;-- 避免不必要的复杂条件
SELECT LAST(*) FROM meters GROUP BY tbname;
3. 索引优化
-- 确保时间戳字段有适当的索引(TDengine 自动处理)
-- 合理设计标签字段以优化分组查询
常见问题与解决方案
1. 查询性能问题
问题: LAST 查询响应慢
解决方案:
-- 启用 last_value 缓存
ALTER DATABASE your_db CACHEMODEL 'last_value';
-- 调整缓存大小
ALTER DATABASE your_db CACHESIZE 32;
2. 内存使用过高
问题: 缓存占用内存过多
解决方案:
-- 先检查当前缓存使用情况
SHOW your_db.vgroups;-- 减少缓存大小
ALTER DATABASE your_db CACHESIZE 16;
-- 或关闭缓存
ALTER DATABASE your_db CACHEMODEL 'none';
3. 结果不一致
问题: 频繁切换 CACHEMODEL 导致结果不准确
解决方案: 保持稳定的缓存配置,避免频繁切换
4. 聚合函数嵌套错误
问题: AVG(LAST(current))
报错
解决方案: 使用分步查询或单层聚合函数
注意事项
- 时间戳依赖: LAST 函数依赖于时间戳排序,确保时间戳字段的准确性
- NULL值忽略: 函数自动忽略 NULL 值,只返回非 NULL 值
- 复合主键: 对于有复合主键的表,相同时间戳的记录会按主键排序
- 随机性: 超级表查询时,相同最大时间戳的多条记录会随机选择一条
- 缓存配置: 启用缓存会增加内存消耗,需要根据系统资源合理配置
- 结果完整性:
- 单列全为 NULL 时,该列结果为 NULL
- 所有列全为 NULL 时,不返回任何结果
- 配置稳定性: 建议保持 CACHEMODEL 配置稳定,避免频繁更改
- 函数嵌套限制: 不支持在聚合函数内嵌套 LAST 函数
相关函数
FIRST()
: 返回最先(时间戳最小)的非 NULL 值LAST_ROW()
: 返回最后一条记录(可能包含 NULL 值)MAX()
: 返回数值最大的值MIN()
: 返回数值最小的值TOP()
: 返回最大的 k 个值BOTTOM()
: 返回最小的 k 个值
关于 TDengine
TDengine 专为物联网IoT平台、工业大数据平台设计。其中,TDengine TSDB 是一款高性能、分布式的时序数据库(Time Series Database),同时它还带有内建的缓存、流式计算、数据订阅等系统功能;TDengine IDMP 是一款AI原生工业数据管理平台,它通过树状层次结构建立数据目录,对数据进行标准化、情景化,并通过 AI 提供实时分析、可视化、事件管理与报警等功能。