Background
  The table model already provides aggregate functions such as sum, avg, rate, 
increase, irate, and delta, and table functions such as tumble, hop, session, 
lowpass, highpass, m4, fft, and xcorr. These cover counters, windowed 
aggregation, and filtering, but they do not
  cover three common requirements: the area under a curve over time, the rate 
of change between adjacent samples of a signed field, and a bounded uniform 
sample of a table. The tree model has comparable capability through its 
derivative and sample UDTFs; the table
  model currently has no built-in equivalent. Typical scenarios are computing 
energy or volume from a signed power or flow signal, comparing the rate of 
change of two sensors in a dashboard, and drawing a fixed-size sample from a 
large table without loading all rows.
  Problem Description
  Users who need these quantities must either write a UDF or move the data out 
of the database. For signed signals, rate() and increase() are not applicable 
because they assume a monotonic counter. A plain LIMIT is not a uniform sample 
and does not preserve the
  per-partition semantics used elsewhere in the table model. The three 
functions below address these cases and follow the existing conventions: 
explicit PARTITION BY, required ORDER BY where order matters, and per-partition 
isolation.
  Proposed Implementation
  The implementation is in PR #19227: 
https://github.com/apache/iotdb/pull/19227. The behavior of the three functions 
is as follows.
  - integral(value [, unit [, time]]) is an aggregate function. It applies the 
trapezoidal rule to adjacent distinct timestamps within each group. Null values 
are skipped, fewer than two effective points yield 0, negative values are 
allowed, and only numeric input types
    are accepted. The optional unit defaults to 1s and accepts a duration 
literal or a positive integer number of ticks. The result is the signed area 
divided by unit, so integral(x, 10s) is one tenth of integral(x, 1s). The 
optional third argument names the actual
    time column. The function works with GROUP BY, date_bin windows, and 
COLUMNS regular expressions. When the planner proves that input is 
time-ascending, OrderedIntegralAccumulator computes the result in a single 
pass; otherwise NaiveIntegralAccumulator buffers and
    sorts the samples. Grouped variants exist for both, and partial and final 
aggregation are supported through IntegralFunctionIntermediateStateCodec.
  - derivative is a table function: DERIVATIVE(DATA => table PARTITION BY ... 
ORDER BY time, unit => 1s). For each adjacent pair of points it emits (v2 - v1) 
* unit / (t2 - t1), using the time of the second point as the output time. The 
output contains the partition
    columns, the time column, and one DOUBLE column per numeric input column, 
named derivative(column). ORDER BY is mandatory and must contain exactly one 
timestamp column. Time must be monotonic within each partition; rows with the 
same timestamp collapse and the later
    row wins, and a null value nulls only that output column for that pair.
  - sample is a table function: SAMPLE(DATA => table PARTITION BY ... ORDER BY 
time, N => k). It performs reservoir sampling over whole rows, per partition, 
with k between 1 and 65536. All columns are preserved, partition columns come 
first, and the sampled rows are
    emitted in input order so that ORDER BY still determines their relative 
order.
  Impact and Scope
  The change is limited to the table model. It adds built-in functions and does 
not change the tree model, existing aggregate functions, or existing table 
functions. No external UDF deployment is required. derivative streams one 
partition at a time, sample buffers at
  most N rows per partition, and integral buffers samples only when the input 
order cannot be proven. Integration tests cover basic aggregation and multiple 
groups, date_bin windows, nulls and insufficient points, negative values, 
COLUMNS expansion, and invalid
  arguments for integral, and empty tables, partitions, duplicate timestamps, 
null timestamps, all numeric types, and invalid arguments for sample and 
derivative.
  Known Limitation and Open Question
  In a plan that is split across data regions, integral() currently carries 
every raw sample in its intermediate state, whose size is bounded by 
max_tsblock_size_in_bytes. A single group with more than about 8,191 points in 
one data region therefore fails with error
  701. A constant-size mergeable state, carrying the first point, the last 
point, and the partial area, would avoid this. I would like to hear whether 
reviewers prefer to address this in PR #19227 or in a follow-up. Comments on 
the function semantics are also welcome.
  ---
  
  背景
  表模型已经提供 sum、avg、rate、increase、irate、delta 等聚合函数,以及 
tumble、hop、session、lowpass、highpass、m4、fft、xcorr
  
等表函数。它们覆盖了计数器、窗口聚合和过滤,但没有覆盖三类常见需求:一段时间内曲线下的面积、有符号字段相邻采样点之间的变化率,以及从一张表中取出大小固定且均匀的样本。树模型通过
 derivative 和 sample UDTF
  
已经具备相近能力;表模型目前没有内置的对应实现。典型场景包括:从有符号的功率或流量信号计算电量或流量体积;在仪表盘中比较两个传感器的变化率;在不加载全部行的情况下从大表抽取固定大小的样本。
  问题描述
  需要上述结果的用户,要么自己写 UDF,要么把数据搬到数据库之外处理。对于有符号信号,rate() 和 increase() 
不适用,因为它们假设输入是单调递增的计数器。普通的 LIMIT 
不是均匀采样,也不保留表模型其他位置使用的按分区语义。下面三个函数覆盖这些情况,并沿用现有约定:显式
  PARTITION BY、顺序相关的场景必须写 ORDER BY、以及按分区隔离。
  实现方案
  实现位于 PR #19227:https://github.com/apache/iotdb/pull/19227。三个函数的行为如下。
  - integral(value [, unit [, time]]) 
是聚合函数。它在每个分组内,对相邻且时间戳不同的点应用梯形法则。空值会被跳过,有效点少于两个时结果为 0,允许负值,只接受数值类型的输入。可选的 unit 
默认是 1s,接受时长字面量或正整数个 tick。结果是带符号面积除以 unit,因此
    integral(x, 10s) 是 integral(x, 1s) 的十分之一。可选的第三个参数用于指定实际的时间列。该函数支持 GROUP 
BY、date_bin 窗口和 COLUMNS 正则。当计划器能证明输入按时间升序时,OrderedIntegralAccumulator 单遍计算;否则 
NaiveIntegralAccumulator
    缓存样本后排序。两种实现都有对应的分组版本,partial 和 final 聚合通过 
IntegralFunctionIntermediateStateCodec 支持。
  - derivative 是表函数:DERIVATIVE(DATA => table PARTITION BY ... ORDER BY time, 
unit => 1s)。对每一对相邻点,它输出 (v2 - v1) * unit / (t2 - 
t1),输出时间取这对点中第二个点的时间。输出包含分区列、时间列,以及每个数值输入列对应的一列 DOUBLE,列名为
    derivative(列名)。ORDER BY 
必填,且必须恰好包含一个时间戳列。每个分区内的时间必须单调;时间戳相同的行会合并,后出现的行生效;某一侧为空值时,只会让该对点对应的这一列输出为空。
  - sample 是表函数:SAMPLE(DATA => table PARTITION BY ... ORDER BY time, N => 
k)。它按分区对整行做蓄水池采样,k 的取值是 1 到 65536。所有列都会保留,分区列排在最前面,采样行按输入顺序输出,因此 ORDER BY 
仍然决定它们的相对顺序。


Best wishes,
Yaobin Chen

Reply via email to