| import{_ as n,c as a,b as e,o as l}from"./app-BJ81rGGe.js";const t={};function i(o,s){return l(),a("div",null,s[0]||(s[0]=[e(`<h1 id="用户自定义函数" tabindex="-1"><a class="header-anchor" href="#用户自定义函数"><span>用户自定义函数</span></a></h1><h2 id="_1-udf介绍" tabindex="-1"><a class="header-anchor" href="#_1-udf介绍"><span>1. UDF介绍</span></a></h2><p>UDF(User Defined Function)即用户自定义函数,IoTDB 提供多种内置的时序处理函数,也支持扩展自定义函数来满足更多的计算需求。</p><p>IoTDB 表模型中支持两种类型的 UDF ,如下表所示。</p><table><thead><tr><th>UDF 类型</th><th>函数类型</th><th>描述</th></tr></thead><tbody><tr><td><code>UDSF(User-defined Scalar Function)</code></td><td>标量函数</td><td>输入 k 列 1 行数据,输出1 列 1 行数据(一对一)。</td></tr><tr><td><code>UDAF(User-defined Aggregate Function)</code></td><td>聚合函数</td><td>输入k 列 m 行数据,输出1 列 1 行数据(多对一)。</td></tr></tbody></table><ul><li><code>UDSF</code> 可用于标量函数出现的任何子句和表达式中,如select子句、where子句等。 <ul><li><code>select udsf1(s1) from table1 where udsf2(s1)>0</code></li></ul></li><li><code>UDAF</code> 可用于聚合函数出现的任何子句和表达式中,如select子句、having子句等; <ul><li><code>select udaf1(s1), device_id from table1 group by device_id having udaf2(s1)>0 </code></li></ul></li></ul><h2 id="_2-udf-管理" tabindex="-1"><a class="header-anchor" href="#_2-udf-管理"><span>2. UDF 管理</span></a></h2><h3 id="_2-1-udf-注册" tabindex="-1"><a class="header-anchor" href="#_2-1-udf-注册"><span>2.1 UDF 注册</span></a></h3><ol><li><p>准备 UDF 实现的 JAR 包,其中包含 UDF 实现类,如<code>org.apache.iotdb.udf.ScalarFunctionExample</code>。</p><p>Jar 包的放置有两种方式:</p></li></ol><ul><li>本地:需要将 JAR 包放置到集群<strong>所有节点</strong>的 <code>ext/udf</code>目录下。</li><li>远端:需要将 JAR 包上传到 URI 服务器上并确保 IoTDB 实例能够访问该 URI 服务器(注册成功后IoTDB 会下载 JAR 包并同步到整个集群)。</li></ul><ol start="2"><li>使用以下 SQL 语句注册 UDF</li></ol><div class="language-sql line-numbers-mode" data-highlighter="shiki" data-ext="sql" data-title="sql" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code><span class="line"><span style="color:#C678DD;">CREATE</span><span style="color:#C678DD;"> FUNCTION</span><span style="color:#56B6C2;"> <</span><span style="color:#ABB2BF;">UDF-</span><span style="color:#C678DD;">NAME</span><span style="color:#56B6C2;">></span><span style="color:#C678DD;"> AS</span><span style="color:#56B6C2;"> <</span><span style="color:#ABB2BF;">UDF-CLASS-FULL-PATHNAME</span><span style="color:#56B6C2;">></span><span style="color:#ABB2BF;"> (</span><span style="color:#C678DD;">USING</span><span style="color:#ABB2BF;"> URI URI-STRING)</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><ul><li>示例</li></ul><div class="language-sql line-numbers-mode" data-highlighter="shiki" data-ext="sql" data-title="sql" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code><span class="line"><span style="color:#7F848E;font-style:italic;">-- 本地</span></span> |
| <span class="line"><span style="color:#C678DD;">CREATE</span><span style="color:#C678DD;"> FUNCTION</span><span style="color:#61AFEF;"> contain_null</span><span style="color:#C678DD;"> AS</span><span style="color:#98C379;"> 'org.apache.iotdb.udf.ScalarFunctionExample'</span><span style="color:#ABB2BF;">;</span></span> |
| <span class="line"></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;">-- 远端</span></span> |
| <span class="line"><span style="color:#C678DD;">CREATE</span><span style="color:#C678DD;"> FUNCTION</span><span style="color:#61AFEF;"> contain_null</span><span style="color:#C678DD;"> AS</span><span style="color:#98C379;"> 'org.apache.iotdb.udf.ScalarFunctionExample'</span><span style="color:#C678DD;"> USING</span><span style="color:#ABB2BF;"> URI </span><span style="color:#98C379;">'http://jar/example.jar'</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p><strong>注意:</strong></p><ol><li>UDF 在装载过程中无需启停服务器。</li><li>UDF 名称大小写不敏感,不能与 IoTDB 内置函数重名。</li><li>表模型和树模型的 UDF 空间相互独立。</li><li>避免在不同的 JAR 包中创建全类名相同但功能逻辑不同的 UDF 类。如果存在,系统在执行 UDF 时会随机加载其中一个,造成执行行为不一致。</li></ol><h3 id="_2-2-udf-卸载" tabindex="-1"><a class="header-anchor" href="#_2-2-udf-卸载"><span>2.2 UDF 卸载</span></a></h3><p>SQL 语法如下:</p><div class="language-sql line-numbers-mode" data-highlighter="shiki" data-ext="sql" data-title="sql" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code><span class="line"><span style="color:#C678DD;">DROP</span><span style="color:#C678DD;"> FUNCTION</span><span style="color:#56B6C2;"> <</span><span style="color:#ABB2BF;">UDF-</span><span style="color:#C678DD;">NAME</span><span style="color:#56B6C2;">></span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><p>示例:卸载上述例子的 UDF:</p><div class="language-sql line-numbers-mode" data-highlighter="shiki" data-ext="sql" data-title="sql" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code><span class="line"><span style="color:#C678DD;">DROP</span><span style="color:#C678DD;"> FUNCTION</span><span style="color:#ABB2BF;"> contain_null</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><h3 id="_2-3-udf-查看" tabindex="-1"><a class="header-anchor" href="#_2-3-udf-查看"><span>2.3 UDF 查看</span></a></h3><ul><li>如果 State 为 UNAVAILABLE,可能是在注册或卸载过程中系统发生了错误,请查看系统日志进行排查,重新注册或卸载 UDF 直至成功即可。</li></ul><div class="language-sql line-numbers-mode" data-highlighter="shiki" data-ext="sql" data-title="sql" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code><span class="line"><span style="color:#ABB2BF;">SHOW FUNCTIONS</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div></div></div><h3 id="_2-4-udf-配置" tabindex="-1"><a class="header-anchor" href="#_2-4-udf-配置"><span>2.4 UDF 配置</span></a></h3><ul><li>可以在 <code>iotdb-system.properties</code> 中配置 UDF Jar 文件的存储目录:</li></ul><div class="language-properties line-numbers-mode" data-highlighter="shiki" data-ext="properties" data-title="properties" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code><span class="line"><span style="color:#7F848E;font-style:italic;"># UDF lib dir</span></span> |
| <span class="line"><span style="color:#C678DD;">udf_lib_dir</span><span style="color:#ABB2BF;">=</span><span style="color:#98C379;">ext/udf</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div></div></div><h2 id="_3-udf-开发" tabindex="-1"><a class="header-anchor" href="#_3-udf-开发"><span>3. UDF 开发</span></a></h2><h3 id="_3-1-udf-依赖" tabindex="-1"><a class="header-anchor" href="#_3-1-udf-依赖"><span>3.1 UDF 依赖</span></a></h3><p>可以从 <a href="http://search.maven.org/" target="_blank" rel="noopener noreferrer">Maven 库</a> 中搜索下面示例中的依赖,请注意选择和目标 IoTDB 服务器版本相同的依赖版本。</p><div class="language-xml line-numbers-mode" data-highlighter="shiki" data-ext="xml" data-title="xml" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code><span class="line"><span style="color:#ABB2BF;"><</span><span style="color:#E06C75;">dependency</span><span style="color:#ABB2BF;">></span></span> |
| <span class="line"><span style="color:#ABB2BF;"> <</span><span style="color:#E06C75;">groupId</span><span style="color:#ABB2BF;">>org.apache.iotdb</</span><span style="color:#E06C75;">groupId</span><span style="color:#ABB2BF;">></span></span> |
| <span class="line"><span style="color:#ABB2BF;"> <</span><span style="color:#E06C75;">artifactId</span><span style="color:#ABB2BF;">>udf-api</</span><span style="color:#E06C75;">artifactId</span><span style="color:#ABB2BF;">></span></span> |
| <span class="line"><span style="color:#ABB2BF;"> <</span><span style="color:#E06C75;">version</span><span style="color:#ABB2BF;">>2.0.0</</span><span style="color:#E06C75;">version</span><span style="color:#ABB2BF;">></span></span> |
| <span class="line"><span style="color:#ABB2BF;"> <</span><span style="color:#E06C75;">scope</span><span style="color:#ABB2BF;">>provided</</span><span style="color:#E06C75;">scope</span><span style="color:#ABB2BF;">></span></span> |
| <span class="line"><span style="color:#ABB2BF;"></</span><span style="color:#E06C75;">dependency</span><span style="color:#ABB2BF;">></span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><h3 id="_3-2-标量函数-udsf" tabindex="-1"><a class="header-anchor" href="#_3-2-标量函数-udsf"><span>3.2 标量函数(UDSF)</span></a></h3><p>编写一个 UDSF 需要实现<code>org.apache.iotdb.udf.api.relational.ScalarFunction</code>接口。</p><div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" data-title="java" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code><span class="line"><span style="color:#C678DD;">public</span><span style="color:#C678DD;"> interface</span><span style="color:#E5C07B;"> ScalarFunction</span><span style="color:#C678DD;"> extends</span><span style="color:#E5C07B;"> SQLFunction</span><span style="color:#ABB2BF;"> {</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> /**</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * In this method, the user need to do the following things:</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> *</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * <ul></span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * <li>Validate {@linkplain FunctionArguments}. Throw {@link UDFArgumentNotValidException} if</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * any parameter is not valid.</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * <li>Use {@linkplain FunctionArguments} to get input data types and infer output data type.</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * <li>Construct and return a {@linkplain ScalarFunctionAnalysis} object.</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </ul></span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> *</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@param</span><span style="color:#E06C75;font-style:italic;"> arguments</span><span style="color:#7F848E;font-style:italic;"> arguments used to validate</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@throws</span><span style="color:#E5C07B;font-style:italic;"> UDFArgumentNotValidException</span><span style="color:#7F848E;font-style:italic;"> if any parameter is not valid</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@return</span><span style="color:#7F848E;font-style:italic;"> the analysis result of the scalar function</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> */</span></span> |
| <span class="line"><span style="color:#E5C07B;"> ScalarFunctionAnalysis</span><span style="color:#61AFEF;"> analyze</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">FunctionArguments</span><span style="color:#E06C75;font-style:italic;"> arguments</span><span style="color:#ABB2BF;">)</span><span style="color:#C678DD;"> throws</span><span style="color:#E5C07B;"> UDFArgumentNotValidException</span><span style="color:#ABB2BF;">;</span></span> |
| <span class="line"></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> /**</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * This method is called after the ScalarFunction is instantiated and before the beginning of the</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * transformation process. This method is mainly used to initialize the resources used in</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * ScalarFunction.</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> *</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@param</span><span style="color:#E06C75;font-style:italic;"> arguments</span><span style="color:#7F848E;font-style:italic;"> used to parse the input arguments entered by the user</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@throws</span><span style="color:#E5C07B;font-style:italic;"> UDFException</span><span style="color:#7F848E;font-style:italic;"> the user can throw errors if necessary</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> */</span></span> |
| <span class="line"><span style="color:#C678DD;"> default</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> beforeStart</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">FunctionArguments</span><span style="color:#E06C75;font-style:italic;"> arguments</span><span style="color:#ABB2BF;">)</span><span style="color:#C678DD;"> throws</span><span style="color:#E5C07B;"> UDFException</span><span style="color:#ABB2BF;"> {</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> // do nothing</span></span> |
| <span class="line"><span style="color:#ABB2BF;"> }</span></span> |
| <span class="line"></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> /**</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * This method will be called to process the transformation. In a single UDF query, this method</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * may be called multiple times.</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> *</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@param</span><span style="color:#E06C75;font-style:italic;"> input</span><span style="color:#7F848E;font-style:italic;"> original input data row</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@throws</span><span style="color:#E5C07B;font-style:italic;"> UDFException</span><span style="color:#7F848E;font-style:italic;"> the user can throw errors if necessary</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> */</span></span> |
| <span class="line"><span style="color:#E5C07B;"> Object</span><span style="color:#61AFEF;"> evaluate</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">Record</span><span style="color:#E06C75;font-style:italic;"> input</span><span style="color:#ABB2BF;">)</span><span style="color:#C678DD;"> throws</span><span style="color:#E5C07B;"> UDFException</span><span style="color:#ABB2BF;">;</span></span> |
| <span class="line"></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> /** This method is mainly used to release the resources used in the ScalarFunction. */</span></span> |
| <span class="line"><span style="color:#C678DD;"> default</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> beforeDestroy</span><span style="color:#ABB2BF;">()</span><span style="color:#ABB2BF;"> {</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> // do nothing</span></span> |
| <span class="line"><span style="color:#ABB2BF;"> }</span></span> |
| <span class="line"><span style="color:#ABB2BF;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>接口说明:</p><table><thead><tr><th>接口定义</th><th>描述</th><th>是否必须</th></tr></thead><tbody><tr><td><code>ScalarFunctionAnalysis analyze(FunctionArguments arguments);</code></td><td>1. 校验<code>FunctionArguments</code>中的输入列数、数据类型、系统参数等是否合法,不合法则抛出异常。<br> 2. 根据<code>FunctionArguments</code>构造<code>ScalarFunctionAnalysis</code>,包括输出类型等信息。</td><td>是</td></tr><tr><td><code>void beforeStart(FunctionArguments arguments);</code></td><td>在 UDSF 处理输入数据前,调用用户自定义的初始化行为</td><td>否</td></tr><tr><td><code>Object evaluate(Record input) throws UDFException;</code></td><td>UDSF 处理逻辑,根据一行输入数据,返回一行输出数据。</td><td>是</td></tr><tr><td><code>void beforeDestroy();</code></td><td>UDSF 的结束方法,您可以在此方法中进行一些资源释放等的操作。此方法由框架调用。对于一个实例而言,生命周期中会且只会被调用一次,即在处理完最后一条记录之后被调用。</td><td>否</td></tr></tbody></table><p>目前 ScalarFunctionAnalysis 中的字段:</p><table><thead><tr><th>字段类型</th><th>字段名称</th><th>默认值</th></tr></thead><tbody><tr><td>Type</td><td>outputDataType</td><td>无</td></tr></tbody></table><p>示例:<a href="https://github.com/apache/iotdb/blob/master/example/udf/src/main/java/org/apache/iotdb/udf/ScalarFunctionExample.java" target="_blank" rel="noopener noreferrer">UDSF 的实现示例</a>,输入任意数据类型的任意多列,返回一个布尔值,代表该行输入是否包含 NULL 值。</p><h3 id="_3-3-聚合函数-udaf" tabindex="-1"><a class="header-anchor" href="#_3-3-聚合函数-udaf"><span>3.3 聚合函数(UDAF)</span></a></h3><p>一个完整的 UDAF 定义涉及到 <code>State</code> 和 <code>AggregateFunction</code> 两个类。</p><h4 id="_3-3-1-state-类" tabindex="-1"><a class="header-anchor" href="#_3-3-1-state-类"><span>3.3.1 State 类</span></a></h4><p>编写一个 State 类需要实现<code>org.apache.iotdb.udf.api.State</code>接口。</p><div class="language-c++ line-numbers-mode" data-highlighter="shiki" data-ext="c++" data-title="c++" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code><span class="line"><span style="color:#ABB2BF;">public interface State {</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> /** Reset your state object to its initial state. */</span></span> |
| <span class="line"><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> reset</span><span style="color:#ABB2BF;">();</span></span> |
| <span class="line"></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> /**</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * Serialize your state into byte array. The order of serialization must be consistent with</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * deserialization.</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> */</span></span> |
| <span class="line"><span style="color:#ABB2BF;"> byte[] </span><span style="color:#61AFEF;">serialize</span><span style="color:#ABB2BF;">();</span></span> |
| <span class="line"></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> /**</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * Deserialize byte array into your state. The order of deserialization must be consistent with</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * serialization.</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> */</span></span> |
| <span class="line"><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> deserialize</span><span style="color:#ABB2BF;">(byte[] bytes);</span></span> |
| <span class="line"></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> /** Destroy state. You may release previously binding resource in this method. */</span></span> |
| <span class="line"><span style="color:#C678DD;"> default</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> destroyState</span><span style="color:#ABB2BF;">() {}</span></span> |
| <span class="line"><span style="color:#ABB2BF;"> ;</span></span> |
| <span class="line"><span style="color:#ABB2BF;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>接口说明:</p><table><thead><tr><th>接口定义</th><th>描述</th><th>是否必须</th></tr></thead><tbody><tr><td><code>void reset() </code></td><td>将<code>State</code>对象重置为初始的状态,您需要像编写构造函数一样,在该方法内填入<code>State</code>类中各个字段的初始值。</td><td>是</td></tr><tr><td><code>byte[] serialize()</code></td><td>将<code>State</code>序列化为二进制数据。该方法用于 IoTDB 内部的<code>State</code>对象传递,注意序列化的顺序必须和下面的反序列化方法一致。</td><td>是</td></tr><tr><td><code>void deserialize(byte[] bytes)</code></td><td>将二进制数据反序列化为<code>State</code>。该方法用于 IoTDB 内部的<code>State</code>对象传递,注意反序列化的顺序必须和上面的序列化方法一致。</td><td>是</td></tr><tr><td><code>void destroyState()</code></td><td>进行资源释放等的操作。此方法由框架调用。对于一个实例而言,生命周期中会且只会被调用一次。</td><td>否</td></tr></tbody></table><h4 id="_3-3-2-aggregatefunction-类" tabindex="-1"><a class="header-anchor" href="#_3-3-2-aggregatefunction-类"><span>3.3.2 AggregateFunction 类</span></a></h4><p>编写一个 UDAF 需要实现 <code>org.apache.iotdb.udf.api.relational.AggregateFunction</code>接口。</p><div class="language-java line-numbers-mode" data-highlighter="shiki" data-ext="java" data-title="java" style="background-color:#282c34;color:#abb2bf;"><pre class="shiki one-dark-pro vp-code"><code><span class="line"><span style="color:#C678DD;">public</span><span style="color:#C678DD;"> interface</span><span style="color:#E5C07B;"> AggregateFunction</span><span style="color:#C678DD;"> extends</span><span style="color:#E5C07B;"> SQLFunction</span><span style="color:#ABB2BF;"> {</span></span> |
| <span class="line"></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> /**</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * In this method, the user need to do the following things:</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> *</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * <ul></span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * <li>Validate {@linkplain FunctionArguments}. Throw {@link UDFArgumentNotValidException} if</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * any parameter is not valid.</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * <li>Use {@linkplain FunctionArguments} to get input data types and infer output data type.</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * <li>Construct and return a {@linkplain AggregateFunctionAnalysis} object.</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </ul></span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> *</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@param</span><span style="color:#E06C75;font-style:italic;"> arguments</span><span style="color:#7F848E;font-style:italic;"> arguments used to validate</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@throws</span><span style="color:#E5C07B;font-style:italic;"> UDFArgumentNotValidException</span><span style="color:#7F848E;font-style:italic;"> if any parameter is not valid</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@return</span><span style="color:#7F848E;font-style:italic;"> the analysis result of the scalar function</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> */</span></span> |
| <span class="line"><span style="color:#E5C07B;"> AggregateFunctionAnalysis</span><span style="color:#61AFEF;"> analyze</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">FunctionArguments</span><span style="color:#E06C75;font-style:italic;"> arguments</span><span style="color:#ABB2BF;">)</span></span> |
| <span class="line"><span style="color:#C678DD;"> throws</span><span style="color:#E5C07B;"> UDFArgumentNotValidException</span><span style="color:#ABB2BF;">;</span></span> |
| <span class="line"></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> /**</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * This method is called after the AggregateFunction is instantiated and before the beginning of</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * the transformation process. This method is mainly used to initialize the resources used in</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * AggregateFunction.</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> *</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@param</span><span style="color:#E06C75;font-style:italic;"> arguments</span><span style="color:#7F848E;font-style:italic;"> used to parse the input arguments entered by the user</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@throws</span><span style="color:#E5C07B;font-style:italic;"> UDFException</span><span style="color:#7F848E;font-style:italic;"> the user can throw errors if necessary</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> */</span></span> |
| <span class="line"><span style="color:#C678DD;"> default</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> beforeStart</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">FunctionArguments</span><span style="color:#E06C75;font-style:italic;"> arguments</span><span style="color:#ABB2BF;">)</span><span style="color:#C678DD;"> throws</span><span style="color:#E5C07B;"> UDFException</span><span style="color:#ABB2BF;"> {</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> // do nothing</span></span> |
| <span class="line"><span style="color:#ABB2BF;"> }</span></span> |
| <span class="line"></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> /** Create and initialize state. You may bind some resource in this method. */</span></span> |
| <span class="line"><span style="color:#E5C07B;"> State</span><span style="color:#61AFEF;"> createState</span><span style="color:#ABB2BF;">();</span></span> |
| <span class="line"></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> /**</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * Update state with data columns.</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> *</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@param</span><span style="color:#E06C75;font-style:italic;"> state</span><span style="color:#7F848E;font-style:italic;"> state to be updated</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@param</span><span style="color:#E06C75;font-style:italic;"> input</span><span style="color:#7F848E;font-style:italic;"> original input data row</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> */</span></span> |
| <span class="line"><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> addInput</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">State</span><span style="color:#E06C75;font-style:italic;"> state</span><span style="color:#ABB2BF;">, </span><span style="color:#E5C07B;">Record</span><span style="color:#E06C75;font-style:italic;"> input</span><span style="color:#ABB2BF;">);</span></span> |
| <span class="line"></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> /**</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * Merge two state in execution engine.</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> *</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@param</span><span style="color:#E06C75;font-style:italic;"> state</span><span style="color:#7F848E;font-style:italic;"> current state</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@param</span><span style="color:#E06C75;font-style:italic;"> rhs</span><span style="color:#7F848E;font-style:italic;"> right-hand-side state to be merged</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> */</span></span> |
| <span class="line"><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> combineState</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">State</span><span style="color:#E06C75;font-style:italic;"> state</span><span style="color:#ABB2BF;">, </span><span style="color:#E5C07B;">State</span><span style="color:#E06C75;font-style:italic;"> rhs</span><span style="color:#ABB2BF;">);</span></span> |
| <span class="line"></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> /**</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * Calculate output value from final state</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> *</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@param</span><span style="color:#E06C75;font-style:italic;"> state</span><span style="color:#7F848E;font-style:italic;"> final state</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@param</span><span style="color:#E06C75;font-style:italic;"> resultValue</span><span style="color:#7F848E;font-style:italic;"> used to collect output data points</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> */</span></span> |
| <span class="line"><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> outputFinal</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">State</span><span style="color:#E06C75;font-style:italic;"> state</span><span style="color:#ABB2BF;">, </span><span style="color:#E5C07B;">ResultValue</span><span style="color:#E06C75;font-style:italic;"> resultValue</span><span style="color:#ABB2BF;">);</span></span> |
| <span class="line"></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> /**</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * Remove input data from state. This method is used to remove the data points that have been</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * added to the state. Once it is implemented, {@linkplain</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * AggregateFunctionAnalysis.Builder#removable(boolean)} should be set to true.</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> *</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@param</span><span style="color:#E06C75;font-style:italic;"> state</span><span style="color:#7F848E;font-style:italic;"> state to be updated</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> * </span><span style="color:#C678DD;font-style:italic;">@param</span><span style="color:#E06C75;font-style:italic;"> input</span><span style="color:#7F848E;font-style:italic;"> row to be removed</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> */</span></span> |
| <span class="line"><span style="color:#C678DD;"> default</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> remove</span><span style="color:#ABB2BF;">(</span><span style="color:#E5C07B;">State</span><span style="color:#E06C75;font-style:italic;"> state</span><span style="color:#ABB2BF;">, </span><span style="color:#E5C07B;">Record</span><span style="color:#E06C75;font-style:italic;"> input</span><span style="color:#ABB2BF;">)</span><span style="color:#ABB2BF;"> {</span></span> |
| <span class="line"><span style="color:#C678DD;"> throw</span><span style="color:#C678DD;"> new</span><span style="color:#61AFEF;"> UnsupportedOperationException</span><span style="color:#ABB2BF;">();</span></span> |
| <span class="line"><span style="color:#ABB2BF;"> }</span></span> |
| <span class="line"></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> /** This method is mainly used to release the resources used in the SQLFunction. */</span></span> |
| <span class="line"><span style="color:#C678DD;"> default</span><span style="color:#C678DD;"> void</span><span style="color:#61AFEF;"> beforeDestroy</span><span style="color:#ABB2BF;">()</span><span style="color:#ABB2BF;"> {</span></span> |
| <span class="line"><span style="color:#7F848E;font-style:italic;"> // do nothing</span></span> |
| <span class="line"><span style="color:#ABB2BF;"> }</span></span> |
| <span class="line"><span style="color:#ABB2BF;">}</span></span></code></pre><div class="line-numbers" aria-hidden="true" style="counter-reset:line-number 0;"><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div><div class="line-number"></div></div></div><p>接口说明:</p><table><thead><tr><th>接口定义</th><th>描述</th><th>是否必须</th></tr></thead><tbody><tr><td><code>AggregateFunctionAnalysis analyze(FunctionArguments arguments);</code></td><td>1. 校验<code>FunctionArguments</code>中的输入列数、数据类型、系统参数等是否合法,不合法则抛出异常。<br> 2. 根据<code>FunctionArguments</code>构造<code>AggregateFunctionAnalysis</code>,包括输出类型、removable 等信息。</td><td>是</td></tr><tr><td><code>void beforeStart(FunctionArguments arguments); </code></td><td>在 UDAF 处理输入数据前,调用用户自定义的初始化行为</td><td>否</td></tr><tr><td><code>State createState();</code></td><td>创建<code>State</code>对象,一般只需要调用默认构造函数,然后按需修改默认的初始值即可。</td><td>是</td></tr><tr><td><code>void addInput(State state, Record input);</code></td><td>更新<code>State</code>对象,将输入的一行<em> </em><code>Record</code>数据添加到聚合状态中。</td><td>是</td></tr><tr><td><code>void combineState(State state, State rhs); </code></td><td>将<code>rhs</code>状态合并至<code>state</code>状态中。在分布式场景下,同一组的数据可能分布在不同节点上,IoTDB 会为每个节点上的部分数据生成一个<code>State</code>对象,然后调用该方法合并成完整的<code>State</code>。</td><td>是</td></tr><tr><td><code>void outputFinal(State state, ResultValue resultValue);</code></td><td>根据<code>State</code>中的数据,计算出最终的聚合结果。注意根据聚合的语义,每一组只能输出一个值。</td><td>是</td></tr><tr><td><code>void remove(State state, Record input);</code></td><td>更新<code>State</code>对象,将输入的一行<em> </em><code>Record</code>数据从聚合状态中剔除。<strong>实现该方法需要设置 AggregateFunctionAnalysis 中的 removable 字段为 true。</strong></td><td>否</td></tr><tr><td><code>void beforeDestroy();</code></td><td>UDSF 的结束方法,您可以在此方法中进行一些资源释放等的操作。此方法由框架调用。对于一个实例而言,生命周期中会且只会被调用一次,即在处理完最后一条记录之后被调用。</td><td>否</td></tr></tbody></table><p>目前 AggregateFunctionAnalysis 中的字段:</p><table><thead><tr><th>字段类型</th><th>字段名称</th><th>默认值</th></tr></thead><tbody><tr><td>Type</td><td>outputDataType</td><td>无</td></tr><tr><td>boolean</td><td>removable</td><td>false</td></tr></tbody></table><p>示例:<a href="https://github.com/apache/iotdb/blob/master/example/udf/src/main/java/org/apache/iotdb/udf/AggregateFunctionExample.java" target="_blank" rel="noopener noreferrer">UDAF 的实现示例</a>,计算不为 NULL 的行数。</p><h3 id="_3-4-完整maven项目示例" tabindex="-1"><a class="header-anchor" href="#_3-4-完整maven项目示例"><span>3.4 完整Maven项目示例</span></a></h3><p>如果使用 <a href="http://search.maven.org/" target="_blank" rel="noopener noreferrer">Maven</a>,可以参考示例项目<a href="https://github.com/apache/iotdb/tree/master/example/udf" target="_blank" rel="noopener noreferrer">udf-example</a>。</p>`,56)]))}const p=n(t,[["render",i],["__file","User-defined-function.html.vue"]]),r=JSON.parse('{"path":"/zh/UserGuide/latest-Table/User-Manual/User-defined-function.html","title":"用户自定义函数","lang":"zh-CN","frontmatter":{"description":"用户自定义函数 1. UDF介绍 UDF(User Defined Function)即用户自定义函数,IoTDB 提供多种内置的时序处理函数,也支持扩展自定义函数来满足更多的计算需求。 IoTDB 表模型中支持两种类型的 UDF ,如下表所示。 UDSF 可用于标量函数出现的任何子句和表达式中,如select子句、where子句等。 select u...","head":[["link",{"rel":"alternate","hreflang":"en-us","href":"https://iotdb.apache.org/UserGuide/latest-Table/User-Manual/User-defined-function.html"}],["meta",{"property":"og:url","content":"https://iotdb.apache.org/zh/UserGuide/latest-Table/User-Manual/User-defined-function.html"}],["meta",{"property":"og:site_name","content":"IoTDB Website"}],["meta",{"property":"og:title","content":"用户自定义函数"}],["meta",{"property":"og:description","content":"用户自定义函数 1. UDF介绍 UDF(User Defined Function)即用户自定义函数,IoTDB 提供多种内置的时序处理函数,也支持扩展自定义函数来满足更多的计算需求。 IoTDB 表模型中支持两种类型的 UDF ,如下表所示。 UDSF 可用于标量函数出现的任何子句和表达式中,如select子句、where子句等。 select u..."}],["meta",{"property":"og:type","content":"article"}],["meta",{"property":"og:locale","content":"zh-CN"}],["meta",{"property":"og:locale:alternate","content":"en-US"}],["meta",{"property":"og:updated_time","content":"2025-04-03T10:16:25.000Z"}],["meta",{"property":"article:modified_time","content":"2025-04-03T10:16:25.000Z"}],["script",{"type":"application/ld+json"},"{\\"@context\\":\\"https://schema.org\\",\\"@type\\":\\"Article\\",\\"headline\\":\\"用户自定义函数\\",\\"image\\":[\\"\\"],\\"dateModified\\":\\"2025-04-03T10:16:25.000Z\\",\\"author\\":[]}"]]},"headers":[{"level":2,"title":"1. UDF介绍","slug":"_1-udf介绍","link":"#_1-udf介绍","children":[]},{"level":2,"title":"2. UDF 管理","slug":"_2-udf-管理","link":"#_2-udf-管理","children":[{"level":3,"title":"2.1 UDF 注册","slug":"_2-1-udf-注册","link":"#_2-1-udf-注册","children":[]},{"level":3,"title":"2.2 UDF 卸载","slug":"_2-2-udf-卸载","link":"#_2-2-udf-卸载","children":[]},{"level":3,"title":"2.3 UDF 查看","slug":"_2-3-udf-查看","link":"#_2-3-udf-查看","children":[]},{"level":3,"title":"2.4 UDF 配置","slug":"_2-4-udf-配置","link":"#_2-4-udf-配置","children":[]}]},{"level":2,"title":"3. UDF 开发","slug":"_3-udf-开发","link":"#_3-udf-开发","children":[{"level":3,"title":"3.1 UDF 依赖","slug":"_3-1-udf-依赖","link":"#_3-1-udf-依赖","children":[]},{"level":3,"title":"3.2 标量函数(UDSF)","slug":"_3-2-标量函数-udsf","link":"#_3-2-标量函数-udsf","children":[]},{"level":3,"title":"3.3 聚合函数(UDAF)","slug":"_3-3-聚合函数-udaf","link":"#_3-3-聚合函数-udaf","children":[]},{"level":3,"title":"3.4 完整Maven项目示例","slug":"_3-4-完整maven项目示例","link":"#_3-4-完整maven项目示例","children":[]}]}],"git":{"createdTime":1743675385000,"updatedTime":1743675385000,"contributors":[{"name":"leto-b","username":"leto-b","email":"bingqian.bai@timecho.com","commits":1,"url":"https://github.com/leto-b"}]},"readingTime":{"minutes":7.92,"words":2376},"filePathRelative":"zh/UserGuide/latest-Table/User-Manual/User-defined-function.md","localizedDate":"2025年4月3日","autoDesc":true}');export{p as comp,r as data}; |