TDengine 选择函数 Last() 用户手册

LAST() 函数用户手册

函数定义

sql 复制代码
LAST(expr)

功能说明

LAST() 函数统计表/超级表中某列的值最后写入的非 NULL 值,即返回时间戳最大的非 NULL 值。

版本要求

  • 最低版本: v3.0.0.0

返回值

  • 数据类型: 同应用的字段
  • 返回内容: 时间戳最大的非 NULL 值及其对应的时间戳

参数说明

参数 类型 说明 取值范围
expr 任意类型 要统计的字段表达式或 * 所有字段类型

适用数据类型

  • 所有字段类型: 支持所有数据类型,包括数值类型、字符串类型、时间戳类型等

适用范围

  • 表类型: 表和超级表
  • 查询支持: 支持聚合查询、窗口查询
  • 多结果函数: 支持多列返回

使用说明

  1. NULL 值处理: 忽略 NULL 值,只返回非 NULL 值
  2. 时间戳关联: 返回值对应时间戳最大的记录
  3. 复合主键: 对于存在复合主键的表,若最大时间戳的数据有多条,则返回复合主键最大的数据
  4. 全 NULL 处理 :
    • 如果某列全部为 NULL 值,则该列返回结果也是 NULL
    • 如果所有列全部为 NULL 值,则不返回结果
  5. LAST(*): 支持查询所有列的最后一个非 NULL 值
  6. 随机性: 在用于超级表时,时间戳完全一样且同为最大的数据行可能有多个,会从中随机返回一条,不保证多次运行所挑选的数据行必然一致

基本用法示例

单列查询

sql 复制代码
-- 获取电流的最后一个非 NULL 值
SELECT LAST(current) FROM meters;

-- 获取电压的最后一个非 NULL 值
SELECT LAST(voltage) FROM meters;

-- 获取相位的最后一个非 NULL 值
SELECT LAST(phase) FROM meters;

多列查询

sql 复制代码
-- 获取多个字段的最后一个非 NULL 值
SELECT LAST(current), LAST(voltage), LAST(phase) FROM meters;

-- 使用 LAST(*) 查询所有列的最后一个非 NULL 值
SELECT LAST(*) FROM meters;

NULL 值处理

sql 复制代码
-- LAST 函数自动忽略 NULL 值
SELECT LAST(current) FROM meters;
-- 只返回非 NULL 的最后值

智能电表场景应用示例

基于智能电表数据库结构:

sql 复制代码
-- 数据库和表结构
USE test;
-- meters 超级表包含 ts, current, voltage, phase 字段和 location, groupid 标签

场景1:设备最新状态监控

sql 复制代码
-- 查找每个电表的最新运行数据
SELECT 
    tbname,
    LAST(current) as latest_current,
    LAST(voltage) as latest_voltage,
    LAST(phase) as latest_phase
FROM meters 
GROUP BY tbname;

场景2:按区域查找最新数据

sql 复制代码
-- 查找每个区域最新的用电记录
SELECT 
    location,
    LAST(current) as latest_current,
    LAST(voltage) as latest_voltage,
    LAST(phase) as latest_phase
FROM meters 
GROUP BY location;

场景3:设备组最新状态分析

sql 复制代码
-- 分析不同设备组的最新运行情况
SELECT 
    groupid,
    location,
    LAST(*) 
FROM meters 
GROUP BY groupid, location
ORDER BY groupid;

场景4:数据完整性检查

sql 复制代码
-- 检查各电表最新数据的完整性
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:实时监控面板

sql 复制代码
-- 获取各电表的实时监控数据
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:异常设备识别

sql 复制代码
-- 识别最近异常的设备
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:设备运行时长统计

sql 复制代码
-- 计算设备从首次记录到最后记录的运行时长
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:负载变化趋势分析

sql 复制代码
-- 分析电表负载从初始到最终的变化
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:电能质量最新状态评估(分步查询)

sql 复制代码
-- 第一步:获取每个电表的最新数据
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:设备维护提醒

sql 复制代码
-- 根据最后记录时间和数据值进行维护提醒
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(*) 的特殊用法

查询所有列的最后值

sql 复制代码
-- 查询超级表所有列的最后非 NULL 值
SELECT LAST(*) FROM meters;

-- 按电表分组查询每个电表的最后记录
SELECT LAST(*) FROM meters GROUP BY tbname;

标签列返回控制

在 TDengine 中,LAST(*) 的行为受 multiResultFunctionStarReturnTags 参数控制:

  • 设置为 0(默认): 只返回超级表的普通列
  • 设置为 1: 返回超级表的普通列和标签列
sql 复制代码
-- 设置参数以包含标签列
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 函数缓存

创建数据库时启用缓存
sql 复制代码
-- 创建数据库时启用 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';
修改现有数据库启用缓存
sql 复制代码
-- 为现有数据库启用 LAST 函数缓存
ALTER DATABASE test CACHEMODEL 'last_value';

-- 为现有数据库同时启用 LAST 和 LAST_ROW 缓存
ALTER DATABASE test CACHEMODEL 'both';

-- 禁用缓存
ALTER DATABASE test CACHEMODEL 'none';
配置缓存大小
sql 复制代码
-- 设置每个 vnode 用于缓存的内存大小(MB)
ALTER DATABASE test CACHESIZE 32;  -- 设置为 32MB

-- 同时修改缓存模式和大小
ALTER DATABASE test CACHEMODEL 'both' CACHESIZE 64;
查看缓存配置状态
sql 复制代码
-- 查看数据库的缓存配置
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;

缓存使用效果示例

启用缓存后的高性能查询
sql 复制代码
-- 启用 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;
缓存监控与诊断
sql 复制代码
-- 查看 vgroup 的缓存使用情况
SHOW test.vgroups;

-- 检查缓存负载
SELECT * FROM INFORMATION_SCHEMA.INS_DATABASES WHERE NAME='test';

缓存优化最佳实践

1. 缓存模式选择建议
sql 复制代码
-- 场景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. 缓存大小调优

根据文档说明,可以通过以下方式判断和调整缓存大小:

sql 复制代码
-- 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. 监控缓存效果
sql 复制代码
-- 创建缓存性能监控查询
SELECT 
    NAME as db_name,
    CACHE_MODEL,
    CACHE_SIZE,
    BUFFER
FROM INFORMATION_SCHEMA.INS_DATABASES 
WHERE NAME = 'test';

-- 查看 vgroup 缓存负载
SHOW test.vgroups;

缓存注意事项与限制

  1. 内存消耗: 启用缓存会增加内存使用,CACHESIZE 范围为 [1, 65536] MB
  2. 数据一致性: 缓存数据与磁盘数据保持强一致性,无需担心数据不一致
  3. 适用场景: 特别适合频繁查询最新数据的实时监控场景
  4. 模式切换警告: CACHEMODEL 值来回切换有可能导致 last/last_row 的查询结果不准确,建议保持稳定配置(推荐保持打开)
  5. 缓存预热: 新启用缓存后,第一次查询可能稍慢,后续查询会显著加速
  6. 查询限制: last_value 缓存主要优化无特殊影响(WHERE、ORDER BY、GROUP BY、INTERVAL)下的 LAST 函数查询

与其他函数的对比

LAST vs FIRST

sql 复制代码
-- 对比首个值和最后值
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

sql 复制代码
-- LAST 返回时间最晚的值,MAX 返回数值最大的值
SELECT 
    location,
    LAST(current) as latest_current,    -- 时间最晚的电流值
    MAX(current) as maximum_current     -- 数值最大的电流值
FROM meters 
GROUP BY location;

LAST vs LAST_ROW

sql 复制代码
-- 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 函数嵌套限制说明

不支持的嵌套方式

sql 复制代码
-- 以下写法都会报错:
-- AVG(LAST(current))          -- 聚合函数嵌套选择函数
-- SUM(FIRST(voltage))         -- 聚合函数嵌套选择函数  
-- COUNT(TOP(current, 1))      -- 聚合函数嵌套选择函数
-- MAX(LAST(phase))            -- 聚合函数嵌套选择函数

推荐的替代方案

  1. 分步查询:先用 LAST 获取数据,再在外层或应用层计算聚合
  2. 使用单层聚合:直接使用 MAX、MIN、COUNT 等函数
  3. 子查询:如果支持,使用子查询分离不同层次的聚合

正确的查询模式

sql 复制代码
-- 模式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. 缓存优化

sql 复制代码
-- 为高频 LAST 查询启用缓存
ALTER DATABASE test CACHEMODEL 'last_value' CACHESIZE 64;

2. 查询优化

sql 复制代码
-- 使用合适的时间范围过滤
SELECT LAST(current) FROM meters 
WHERE ts >= NOW() - 1d
GROUP BY location;

-- 避免不必要的复杂条件
SELECT LAST(*) FROM meters GROUP BY tbname;

3. 索引优化

sql 复制代码
-- 确保时间戳字段有适当的索引(TDengine 自动处理)
-- 合理设计标签字段以优化分组查询

常见问题与解决方案

1. 查询性能问题

问题 : LAST 查询响应慢
解决方案:

sql 复制代码
-- 启用 last_value 缓存
ALTER DATABASE your_db CACHEMODEL 'last_value';
-- 调整缓存大小
ALTER DATABASE your_db CACHESIZE 32;

2. 内存使用过高

问题 : 缓存占用内存过多
解决方案:

sql 复制代码
-- 先检查当前缓存使用情况
SHOW your_db.vgroups;

-- 减少缓存大小
ALTER DATABASE your_db CACHESIZE 16;
-- 或关闭缓存
ALTER DATABASE your_db CACHEMODEL 'none';

3. 结果不一致

问题 : 频繁切换 CACHEMODEL 导致结果不准确
解决方案: 保持稳定的缓存配置,避免频繁切换

4. 聚合函数嵌套错误

问题 : AVG(LAST(current)) 报错
解决方案: 使用分步查询或单层聚合函数

注意事项

  1. 时间戳依赖: LAST 函数依赖于时间戳排序,确保时间戳字段的准确性
  2. NULL值忽略: 函数自动忽略 NULL 值,只返回非 NULL 值
  3. 复合主键: 对于有复合主键的表,相同时间戳的记录会按主键排序
  4. 随机性: 超级表查询时,相同最大时间戳的多条记录会随机选择一条
  5. 缓存配置: 启用缓存会增加内存消耗,需要根据系统资源合理配置
  6. 结果完整性 :
    • 单列全为 NULL 时,该列结果为 NULL
    • 所有列全为 NULL 时,不返回任何结果
  7. 配置稳定性: 建议保持 CACHEMODEL 配置稳定,避免频繁更改
  8. 函数嵌套限制: 不支持在聚合函数内嵌套 LAST 函数

相关函数

  • FIRST(): 返回最先(时间戳最小)的非 NULL 值
  • LAST_ROW(): 返回最后一条记录(可能包含 NULL 值)
  • MAX(): 返回数值最大的值
  • MIN(): 返回数值最小的值
  • TOP(): 返回最大的 k 个值
  • BOTTOM(): 返回最小的 k 个值

关于 TDengine

TDengine 专为物联网IoT平台、工业大数据平台设计。其中,TDengine TSDB 是一款高性能、分布式的时序数据库(Time Series Database),同时它还带有内建的缓存、流式计算、数据订阅等系统功能;TDengine IDMP 是一款AI原生工业数据管理平台,它通过树状层次结构建立数据目录,对数据进行标准化、情景化,并通过 AI 提供实时分析、可视化、事件管理与报警等功能。

相关推荐
little_xianzhong5 小时前
关于对逾期提醒的定时任务~改进完善
java·数据库·spring boot·spring·mybatis
Sally璐璐5 小时前
Go正则表达式实战指南
数据库·mysql·golang
小猪咪piggy6 小时前
【JavaEE】(23) 综合练习--博客系统
java·数据库·java-ee
bikong76 小时前
一种高效绘制余晖波形的方法Qt/C++
数据库·c++·qt
一叶飘零_sweeeet6 小时前
从 0 到 1 攻克订单表分表分库:亿级流量下的数据库架构实战指南
java·数据库·mysql·数据库架构·分库分表
xianyinsuifeng6 小时前
Oracle 10g → Oracle 19c 升级后问题解决方案(Pro*C 项目)
c语言·数据库·oracle
明达智控技术6 小时前
MR30分布式I/O在面机装备中的应用
分布式·物联网·自动化
TDengine (老段)6 小时前
TDengine 选择函数 First 用户手册
大数据·数据库·物联网·时序数据库·iot·tdengine·涛思数据
dreams_dream7 小时前
企业级 Django 日志配置示例
数据库·django·sqlite