Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南

Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南

引言

在工业物联网场景中,时序数据的存储与处理常面临“数据孤岛”困境——生产设备产生的原始数据需经过清洗、聚合、转换等多步处理,才能转化为可分析的业务指标。Apache IoTDB的查询写回(INTO子句)正是破解这一痛点的“数据炼金术”。通过SELECT INTO语句,能将查询结果直接写入新序列,实现“查询-转换-存储”的闭环,相当于在数据库内部构建轻量级ETL管道。

在这里插入图片描述

Apache IoTDB 时序数据库【系列篇章】

No.文章地址(点击进入)
1Apache IoTDB(1):时序数据库介绍与单机版安装部署指南
2Apache IoTDB(2):时序数据库 IoTDB 集群安装部署的技术优势与适用场景分析
3Apache IoTDB(3):时序数据库 IoTDB Docker部署从单机到集群的全场景部署与实践指南
4Apache IoTDB(4):深度解析时序数据库 IoTDB 在Kubernetes 集群中的部署与实践指南
5Apache IoTDB(5):深度解析时序数据库 IoTDB 中 AINode 工具的部署与实践
6Apache IoTDB(6):深入解析数据库管理操作——增删改查与异构数据库实战指南
7Apache IoTDB(7):设备模板管理——工业物联网元数据标准化的破局之道
8Apache IoTDB(8):时间序列管理——从创建到分析的实战指南
9Apache IoTDB(9):数据库操作——数据写入从CLI到集群部署的六种实战
10Apache IoTDB(10):数据库操作——从查询到优化的全链路实践指南
11Apache IoTDB(11):分段聚合深度解析——从原理到实战的完整指南
12Apache IoTDB(12):深度解析时序数据聚合的GROUP BY与HAVING子句
13Apache IoTDB(13):数据处理的双刃剑——FILL空值填充与LIMIT/SLIMIT分页查询实战指南
14Apache IoTDB(14):IoTDB结果集排序与查询对齐模式——ORDER BY与ALIGN BY DEVICE使用

一、语法定义

SELECT INTO 语句用于将查询结果写入一系列指定的时间序列中。

使用场景

实现 IoTDB 内部 ETL:对原始数据进行 ETL 处理后写入新序列。
查询结果存储:将查询结果进行持久化存储,起到类似物化视图的作用。
非对齐序列转对齐序列:对齐序列从0.13版本开始支持,可以通过该功能将非对齐序列的数据写入新的对齐序列中。

二、使用示例

2.1 语法

selectIntoStatement : SELECT resultColumn [, resultColumn]...INTO intoItem [, intoItem]...FROM prefixPath [, prefixPath]...[WHERE whereCondition][GROUPBY groupByTimeClause, groupByLevelClause][FILL {PREVIOUS | LINEAR | constant}][LIMIT rowLimit OFFSET rowOffset][ALIGN BY DEVICE]; intoItem : [ALIGNED] intoDevicePath '(' intoMeasurementName [',' intoMeasurementName]*')';

2.2 INTO 子句

INTO 子句由若干个 intoItem 构成。

每个 intoItem 由一个目标设备路径和一个包含若干目标物理量名的列表组成(与 INSERT 语句中的 INTO 子句写法类似)。

其中每个目标物理量名与目标设备路径组成一个目标序列,一个 intoItem 包含若干目标序列。例如:root.sg_copy.d1(s1, s2) 指定了两条目标序列 root.sg_copy.d1.s1 和 root.sg_copy.d1.s2。

INTO 子句指定的目标序列要能够与查询结果集的列一一对应。

规则:

按时间对齐(默认):全部 intoItem 包含的目标序列数量要与查询结果集的列数(除时间列外)一致,且按照表头从左到右的顺序一一对应。

按设备对齐(使用 ALIGN BY DEVICE):全部 intoItem 中指定的目标设备数和查询的设备数(即 FROM 子句中路径模式匹配的设备数)一致,且按照结果集设备的输出顺序一一对应。
为每个目标设备指定的目标物理量数量要与查询结果集的列数(除时间和设备列外)一致,且按照表头从左到右的顺序一一对应。

2.3 例子

1.按时间对齐

IoTDB>select s1, s2 into root.sg_copy.d1(t1), root.sg_copy.d2(t1, t2), root.sg_copy.d1(t2)from root.sg.d1, root.sg.d2;+--------------+-------------------+--------+| source column| target timeseries| written|+--------------+-------------------+--------+| root.sg.d1.s1| root.sg_copy.d1.t1|8000|+--------------+-------------------+--------+| root.sg.d2.s1| root.sg_copy.d2.t1|10000|+--------------+-------------------+--------+| root.sg.d1.s2| root.sg_copy.d2.t2|12000|+--------------+-------------------+--------+| root.sg.d2.s2| root.sg_copy.d1.t2|10000|+--------------+-------------------+--------+ Total line number =4 It costs 0.725s 

该语句将 root.sg database 下四条序列的查询结果写入到 root.sg_copy database 下指定的四条序列中。

其中的 root.sg_copy.d2(t1, t2) 也可以写做 root.sg_copy.d2(t1), root.sg_copy.d2(t2)。

INTO 子句的写法非常灵活,只要满足组合出的目标序列没有重复,且与查询结果列一一对应即可。

source column 列表示查询结果的列名
target timeseries 表示对应列写入的目标序列
written 表示预期写入的数据量

2.按时间对齐

IoTDB>selectcount(s1 + s2), last_value(s2)into root.agg.count(s1_add_s2), root.agg.last_value(s2)from root.sg.d1 groupby([0,100),10ms);+--------------------------------------+-------------------------+--------+| source column| target timeseries| written|+--------------------------------------+-------------------------+--------+|count(root.sg.d1.s1 + root.sg.d1.s2)| root.agg.count.s1_add_s2|10|+--------------------------------------+-------------------------+--------+| last_value(root.sg.d1.s2)| root.agg.last_value.s2|10|+--------------------------------------+-------------------------+--------+ Total line number =2 It costs 0.375s 

该语句将聚合查询的结果存储到指定序列中

3.按设备对齐

IoTDB>select s1, s2 into root.sg_copy.d1(t1, t2), root.sg_copy.d2(t1, t2)from root.sg.d1, root.sg.d2 align by device;+--------------+--------------+-------------------+--------+| source device| source column| target timeseries| written|+--------------+--------------+-------------------+--------+| root.sg.d1| s1| root.sg_copy.d1.t1|8000|+--------------+--------------+-------------------+--------+| root.sg.d1| s2| root.sg_copy.d1.t2|11000|+--------------+--------------+-------------------+--------+| root.sg.d2| s1| root.sg_copy.d2.t1|12000|+--------------+--------------+-------------------+--------+| root.sg.d2| s2| root.sg_copy.d2.t2|9000|+--------------+--------------+-------------------+--------+ Total line number =4 It costs 0.625s 

该语句同样是将 root.sg database 下四条序列的查询结果写入到 root.sg_copy database 下指定的四条序列中。但在按设备对齐中,intoItem 的数量必须和查询的设备数量一致,每个查询设备对应一个 intoItem。

按设备对齐查询时,CLI 展示的结果集多出一列 source device 列表示查询的设备。

4.按设备对齐

IoTDB>select s1 + s2 into root.expr.add(d1s1_d1s2), root.expr.add(d2s1_d2s2)from root.sg.d1, root.sg.d2 align by device;+--------------+--------------+------------------------+--------+| source device| source column| target timeseries| written|+--------------+--------------+------------------------+--------+| root.sg.d1| s1 + s2| root.expr.add.d1s1_d1s2|10000|+--------------+--------------+------------------------+--------+| root.sg.d2| s1 + s2| root.expr.add.d2s1_d2s2|10000|+--------------+--------------+------------------------+--------+ Total line number =2 It costs 0.532s 

该语句将表达式计算的结果存储到指定序列中

三、变量占位符

特别的可以使用变量占位符描述目标序列与查询序列之间的对应规律,简化语句书写。目前支持两种变量占位符。

  1. 后缀复制符 :::复制查询设备后缀(或物理量),表示从该层开始一直到设备的最后一层(或物理量),目标设备的节点名(或物理量名)与查询的设备对应的节点名(或物理量名)相同。
  2. 单层节点匹配符 i :表示目标序列当前层节点名与查询序列的第 i 层节点名相同。比如,对于路径 r o o t . s g 1. d 1. s 1 而言, {i}:表示目标序列当前层节点名与查询序列的第i层节点名相同。比如,对于路径root.sg1.d1.s1而言, i:表示目标序列当前层节点名与查询序列的第i层节点名相同。比如,对于路径root.sg1.d1.s1而言,{1}表示sg1, 2 表示 d 1 , {2}表示d1, 2表示d1,{3}表示s1。
    在使用变量占位符时,intoItem与查询结果集列的对应关系不能存在歧义

按时间对齐(默认):变量占位符只能描述序列与序列之间的对应关系,如果查询中包含聚合、表达式计算,此时查询结果中的列无法与某个序列对应,因此目标设备和目标物理量都不能使用变量占位符。

(1)目标设备不使用变量占位符 & 目标物理量列表使用变量占位符
限制:

1.每个 intoItem 中,物理量列表的长度必须为 1。
(如果长度可以大于1,例如 root.sg1.d1(::, s1),无法确定具体哪些列与::匹配)
2.intoItem 数量为 1,或与查询结果集列数一致。
(在每个目标物理量列表长度均为 1 的情况下,若 intoItem 只有 1 个,此时表示全部查询序列写入相同设备;若 intoItem 数量与查询序列一致,则表示为每个查询序列指定一个目标设备;若 intoItem 大于 1 小于查询序列数,此时无法与查询序列一一对应)

匹配方法: 每个查询序列指定目标设备,而目标物理量根据变量占位符生成。

例:

select s1, s2 into root.sg_copy.d1(::), root.sg_copy.d2(s1), root.sg_copy.d1(${3}), root.sg_copy.d2(::)from root.sg.d1, root.sg.d2;

等于下面的语句

select s1, s2 into root.sg_copy.d1(s1), root.sg_copy.d2(s1), root.sg_copy.d1(s2), root.sg_copy.d2(s2)from root.sg.d1, root.sg.d2;

(2)目标设备使用变量占位符 & 目标物理量列表不使用变量占位符
限制: 全部 intoItem 中目标物理量的数量与查询结果集列数一致。

匹配方式: 为每个查询序列指定了目标物理量,目标设备根据对应目标物理量所在 intoItem 的目标设备占位符生成。

例:

select d1.s1, d1.s2, d2.s3, d3.s4 into ::(s1_1, s2_2), root.sg.d2_2(s3_3), root.${2}_copy.::(s4)from root.sg;

(3)目标设备使用变量占位符 & 目标物理量列表使用变量占位符
限制: intoItem 只有一个且物理量列表的长度为 1。

匹配方式: 每个查询序列根据变量占位符可以得到一个目标序列。

例:

select*into root.sg_bk.::(::)from root.sg.**;

将 root.sg 下全部序列的查询结果写到 root.sg_bk,设备名后缀和物理量名保持不变

按设备对齐(使用 ALIGN BY DEVICE)
注:变量占位符只能描述序列与序列之间的对应关系,如果查询中包含聚合、表达式计算,此时查询结果中的列无法与某个物理量对应,因此目标物理量不能使用变量占位符。

(1)目标设备不使用变量占位符 & 目标物理量列表使用变量占位符
限制: 每个 intoItem 中,如果物理量列表使用了变量占位符,则列表的长度必须为 1。

匹配方法: 每个查询序列指定目标设备,而目标物理量根据变量占位符生成。

例:

select s1, s2, s3, s4 into root.backup_sg.d1(s1, s2, s3, s4), root.backup_sg.d2(::), root.sg.d3(backup_${4})from root.sg.d1, root.sg.d2, root.sg.d3 align by device;

(2)目标设备使用变量占位符 & 目标物理量列表不使用变量占位符
限制: intoItem 只有一个。(如果出现多个带占位符的 intoItem,我们将无法得知每个 intoItem 需要匹配哪几个源设备)

匹配方式: 每个查询设备根据变量占位符得到一个目标设备,每个设备下结果集各列写入的目标物理量由目标物理量列表指定。

例:

selectavg(s1),sum(s2)+sum(s3),count(s4)into root.agg_${2}.::(avg_s1, sum_s2_add_s3, count_s4)from root.** align by device;

(3)目标设备使用变量占位符 & 目标物理量列表使用变量占位符
限制: intoItem 只有一个且物理量列表的长度为 1。

匹配方式: 每个查询序列根据变量占位符可以得到一个目标序列。

例:

select*into ::(backup_${4})from root.sg.** align by device;

将 root.sg 下每条序列的查询结果写到相同设备下,物理量名前加backup_。

指定目标序列为对齐序列
通过 ALIGNED 关键词可以指定写入的目标设备为对齐写入,每个 intoItem 可以独立设置。

例:

select s1, s2 into root.sg_copy.d1(t1, t2), aligned root.sg_copy.d2(t1, t2)from root.sg.d1, root.sg.d2 align by device;

该语句指定了 root.sg_copy.d1 是非对齐设备,root.sg_copy.d2是对齐设备。

不支持使用的查询子句
SLIMIT、SOFFSET:查询出来的列不确定,功能不清晰,因此不支持
LAST查询、GROUP BY TAGS、DISABLE ALIGN:表结构和写入结构不一致,因此不支持

需注意:

对于一般的聚合查询,时间戳是无意义的,约定使用 0 来存储
当目标序列存在时,需要保证源序列和目标时间序列的数据类型兼容
当目标序列不存在时,系统将自动创建目标序列(包括 database)
当查询的序列不存在或查询的序列不存在数据,则不会自动创建目标序列

四、内部ETL实现

对原始数据进行 ETL 处理后写入新序列。

IOTDB >SELECT preprocess_udf(s1, s2)INTO ::(preprocessed_s1, preprocessed_s2)FROM root.sg.* ALIGN BY DEIVCE;+--------------+-------------------+---------------------------+--------+| source device| source column| target timeseries| written|+--------------+-------------------+---------------------------+--------+| root.sg.d1| preprocess_udf(s1)| root.sg.d1.preprocessed_s1|8000|+--------------+-------------------+---------------------------+--------+| root.sg.d1| preprocess_udf(s2)| root.sg.d1.preprocessed_s2|10000|+--------------+-------------------+---------------------------+--------+| root.sg.d2| preprocess_udf(s1)| root.sg.d2.preprocessed_s1|11000|+--------------+-------------------+---------------------------+--------+| root.sg.d2| preprocess_udf(s2)| root.sg.d2.preprocessed_s2|9000|+--------------+-------------------+---------------------------+--------+

以上语句使用自定义函数对数据进行预处理,将预处理后的结果持久化存储到新序列中。

查询结果存储

将查询结果进行持久化存储,起到类似物化视图的作用

IOTDB >SELECTcount(s1), last_value(s1)INTO root.sg.agg_${2}(count_s1, last_value_s1)FROM root.sg1.d1 GROUPBY([0,10000),10ms);+--------------------------+-----------------------------+--------+| source column| target timeseries| written|+--------------------------+-----------------------------+--------+|count(root.sg.d1.s1)| root.sg.agg_d1.count_s1|1000|+--------------------------+-----------------------------+--------+| last_value(root.sg.d1.s2)| root.sg.agg_d1.last_value_s2|1000|+--------------------------+-----------------------------+--------+ Total line number =2 It costs 0.115s 

以上语句将降采样查询的结果持久化存储到新序列中。

非对齐序列转对齐序列

对齐序列从 0.13 版本开始支持,可以通过该功能将非对齐序列的数据写入新的对齐序列中。

注意: 建议配合使用 LIMIT & OFFSET 子句或 WHERE 子句(时间过滤条件)对数据进行分批,防止单次操作的数据量过大。

IOTDB >SELECT s1, s2 INTO ALIGNED root.sg1.aligned_d(s1, s2)FROM root.sg1.non_aligned_d WHEREtime>=0andtime<10000;+--------------------------+----------------------+--------+| source column| target timeseries| written|+--------------------------+----------------------+--------+| root.sg1.non_aligned_d.s1| root.sg1.aligned_d.s1|10000|+--------------------------+----------------------+--------+| root.sg1.non_aligned_d.s2| root.sg1.aligned_d.s2|10000|+--------------------------+----------------------+--------+ Total line number =2 It costs 0.375s 

以上语句将一组非对齐的序列的数据迁移到一组对齐序列

五、用户权限与配置参数

相关用户权限

用户必须有下列权限才能正常执行查询写回语句:

所有 SELECT 子句中源序列的 WRITE_SCHEMA 权限
所有 INTO 子句中目标序列 WRITE_DATA 权限

相关配置参数

select_into_insert_tablet_plan_row_limit

在这里插入图片描述

总结

Apache IoTDB的INTO子句通过“查询即存储”的创新设计,实现了时序数据处理的性能突破、功能突破、易用性突破:通过类SQL语法降低使用门槛,配合变量占位符实现动态查询在工业物联网、智能电网、车联网等场景中,INTO子句已成为时序数据处理的核心之一。通过本文的深度解析,不仅能掌握INTO子句的语法细节,更能理解其背后的设计哲学——让数据库自身成为数据处理的核心引擎。

Read more

AI 直接生成前端代码:我的软件原型设计流,从此告别重复画图

AI 直接生成前端代码:我的软件原型设计流,从此告别重复画图

近年来,AI 辅助开发越来越成熟,尤其是在快速原型设计方面。今天分享一下我如何借助 Cursor、Trace solo、ChatGPT、Qoder 等 AI 工具,高效完成软件原型的自动绘制与代码生成。 📌 核心流程三步走 1️⃣ 用 AI 输出需求文档(非技术描述) 首先,我会让 AI 根据产品思路或功能描述,生成一份清晰、无技术细节的需求文档。这一步不写代码,只聚焦逻辑与用户流程。 2️⃣ AI 生成 HTML 原型代码 基于上一步的需求文档,直接让 AI 生成对应的 HTML 代码,快速搭建出可交互的前端原型。支持实时预览,直观看到界面效果。 3️⃣ 反复微调,直至满意 生成的原型往往需要多次调整。通过自然语言描述修改方向,AI 可快速迭代代码,直至达到想要的交互与视觉效果。

By Ne0inhk
WebAgent详解+实战:用开源AI智能体搞定产品与竞品市场调研

WebAgent详解+实战:用开源AI智能体搞定产品与竞品市场调研

在市场调研场景中,产品及竞品分析往往需要投入大量人力,手动浏览网页、提取信息、整理数据,不仅效率低下,还容易出现信息遗漏、误差等问题。WebAgent作为通义实验室开源的端到端自主网页智能体,凭借强大的中文语义理解、多步骤推理和结构化输出能力,可完全本地部署且永久免费,能高效替代人工完成网页信息采集、竞品数据提取、产品信息汇总等调研工作。本文将从WebAgent核心介绍、部署要点入手,聚焦产品与竞争对手调研场景,一步步实现实战示例,让无论是开发者还是市场从业者,都能快速上手,用AI提升调研效率,摆脱重复劳动。 一、初识WebAgent:阿里开源的网页智能体“神器” 1.1 什么是WebAgent? WebAgent是阿里巴巴通义实验室开源的自主网页智能体框架,核心定位是“模拟人类浏览网页的完整流程”,能理解自然语言指令、规划浏览路径、执行网页操作(点击、翻页、搜索等)、提取关键信息并结构化输出,无需人工干预即可完成复杂的网页相关任务。 与国外的AgentQL相比,WebAgent最大的优势的是完全开源免费、支持本地部署、中文语义优化,无需调用云端API,数据可完全保存在内网,

By Ne0inhk

前端转型AI的“第一公里”:如何建立正确的AI心智模型?

前端转型AI的“第一公里”:如何建立正确的AI心智模型? 在过去的一年里,我见证了太多前端同行的焦虑与迷茫。AI浪潮袭来,很多人匆忙上阵,学会了调用OpenAI的API,甚至跑通了LangChain的Demo,但在实际落地时却频频踩坑。 我们习惯了确定性的世界:输入1 + 1,输出必然是2;写了display: flex,布局必然改变。然而,AI开发是一个概率性的世界:同样的Prompt,两次调用可能得到截然不同的结果。这种底层逻辑的冲突,是前端转型AI最大的“拦路虎”。 很多前端工程师把大模型仅仅当成一个“智能API接口”,试图用传统的硬编码逻辑去控制它,结果往往是Prompt越写越长,系统却越来越不稳定。这并非技术能力不足,而是心智模型尚未完成迁移。 从“函数思维”到“上下文思维” 传统前端开发的核心是“函数思维”:我们定义输入、处理逻辑和输出,追求的是精准控制。但在AI应用开发中,这种思维必须升级为“上下文思维”。 大模型本质上是一个“概率预测机”。它不像函数那样执行指令,而是像人一样理解语境。前端开发者转型AI的第一步,不是去学Python深度学习框架,而是学会如

By Ne0inhk