FEATURED · 精选文章

StarRocks SQL UDF:将 SQL 表达式封装为可复用函数的完整指南(4.1+)

发布时间 / 2026/9/17 17:59:51
来源 / 创域科博编辑部
栏目 / 资讯中心
StarRocks SQL UDF:将 SQL 表达式封装为可复用函数的完整指南(4.1+) StarRocks SQL UDF将 SQL 表达式封装为可复用函数的完整指南4.1【免费下载链接】starrocksThe worlds fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks自 StarRocks 4.1 版本起支持创建 SQL 用户自定义函数SQL UDF它把一段 SQL 表达式封装成一个可复用的函数并在查询时被动态展开为实际的 SQL 表达式。与需要上传 JAR/Python 文件的传统 UDF 不同SQL UDF 是一种轻量级的函数定义方式无需编写任何外部代码。读完本文你将掌握 SQL UDF 的完整语法、典型用法与运维管理命令并能从源码层面理解它零函数调用开销的实现原理——即函数在查询优化阶段被展开为原始表达式从而让优化器获得完整的优化视野。概述SQL UDF 解决什么问题SQL UDF 的核心特性有三点动态展开Dynamic Expansion在查询优化阶段把函数调用展开为实际的 SQL 表达式类型安全Type Safety支持显式声明参数类型与返回类型参数化Parameterized支持带名称的参数提升函数可读性。典型适用场景需要反复使用的复杂 SQL 表达式例如多步 JSON 抽取、格式化拼接简单的数据转换与计算逻辑折扣价、税率等统一规范的数据处理规则同一口径在多个查询中保持一致。从源码结构看SQL UDF 与 Java/Python 等传统 UDF 在 FE前端节点内被明确区分为两种模式。CreateFunctionStmt.java 中通过isBuildFunctionMode()判断当语句携带Expr expr即RETURNS后面是一个表达式时属于构建函数模式SQL UDF而isUdfFunctionMode()则对应带content文件/符号或 properties 的传统 UDF 模式。这一区分决定了 SQL UDF 不需要任何外部文件上传流程。语法创建 SQL UDFCREATE [GLOBAL] FUNCTION function_name(arg1_name arg1_type, arg2_name arg2_type, ...) RETURNS expression参数说明参数说明GLOBAL可选。指定后创建全局函数对所有数据库可见function_name函数名可包含数据库名例如db1.my_funcarg_name参数名用于在表达式中引用arg_type参数类型支持 StarRocks 的全部基础数据类型expression函数被调用时展开为实际的 SQL 表达式词法层佐证在 StarRocks.g4 中可以看到两条并行的创建规则——createUdfFunctionStmt要求RETURNS后是type即显式返回类型用于传统 UDF而createInternalFunctionStmt要求RETURNS后是expression用于 SQL UDF。两条规则都允许前置OR REPLACE、GLOBAL、IF NOT EXISTS与本文档语法一致。这也解释了为什么 SQL UDF 不需要显式写返回类型它在分析阶段由表达式推导。分析阶段佐证CreateFunctionAnalyzer.java 中的analyzeExpression方法是 SQL UDF 的落点它先用ExpressionAnalyzer.analyzeExpressionResolveSlot以参数名即 slot的方式解析表达式因此函数体中可以直接使用arg1_name等参数名随后通过AstToSQLBuilder.toSQLWithCredential(expr)将表达式序列化回 SQL 文本并构造SqlFunction存入 catalog若函数名标记了isGlobalFunction()则被登记到FunctionRefAnalyzer.GLOBAL_UDF_DB这个全局命名空间下。删除 SQL UDFDROP FUNCTION [IF EXISTS] function_name(arg_type [, ...])注意删除时以函数名 参数类型列表作为唯一标识因此同名但参数类型不同的重载函数互不影响。查看 SQL UDFSHOW [GLOBAL] FUNCTIONS;使用示例完整继承示例 1字符串处理函数-- Create a function that converts string to uppercase and adds a prefix CREATE FUNCTION format_username(name STRING) RETURNS concat(USER_, upper(name)); -- Use the function SELECT format_username(alice); -- Result: USER_ALICE -- Use in queries SELECT format_username(username) as display_name FROM users;示例 2多参数计算函数-- Create a function that calculates discounted price CREATE FUNCTION calculate_discount_price(original_price DECIMAL(10,2), discount_rate DOUBLE) RETURNS original_price * (1 - discount_rate); -- Use the function SELECT calculate_discount_price(100.00, 0.2); -- Result: 80.00 SELECT calculate_discount_price(price, 0.15) as final_price FROM products;示例 3复杂表达式封装-- Create a function that extracts JSON and transforms CREATE FUNCTION extract_user_info(json_str STRING, field_name STRING) RETURNS get_json_string(get_json_string(json_str, concat($., field_name)), $.value); -- Simplify complex nested calls SELECT extract_user_info(user_data, email) as user_email FROM events;示例 4条件逻辑函数-- Create a function with conditional logic CREATE FUNCTION classify_temperature(temp DOUBLE) RETURNS CASE WHEN temp 30 THEN hot WHEN temp 20 THEN warm WHEN temp 10 THEN cool ELSE cold END; -- Use the function SELECT classify_temperature(25); -- Result: warm示例 5全局 SQL UDF-- Create a global function visible to all databases CREATE GLOBAL FUNCTION format_date_display(dt DATETIME) RETURNS concat(year(dt), -, lpad(month(dt), 2, 0), -, lpad(day(dt), 2, 0)); -- Can be used directly in any database SELECT format_date_display(create_time) from my_table;全局函数与库级函数的差异在源码中有明确体现CreateFunctionAnalyzer.java 在多处如 L134、L154、L442、L563以functionRef.isGlobalFunction()为条件把全局函数的默认库替换为GLOBAL_UDF_DB即全局函数统一注册在一个独立的全局命名空间中因此跨库无需加库前缀即可直接调用。高级特性1. 嵌套调用SQL UDF 支持函数之间的嵌套调用CREATE FUNCTION func_a(x INT, y INT) RETURNS x y; CREATE FUNCTION func_b(a INT, b INT) RETURNS func_a(a, b) * 2; SELECT func_b(3, 4); -- Result: 14由于嵌套函数同样会在优化阶段逐层展开func_b(3, 4)最终等价于(3 4) * 2。2. 隐式类型转换SQL UDF 支持表达式内的隐式/显式类型转换CREATE FUNCTION convert_and_add(a STRING, b INT) RETURNS cast(a AS INT) b; SELECT convert_and_add(100, 50); -- Result: 150注意参数声明的类型如a STRING只是入口类型函数体内仍按 SQL 表达式的类型系统做转换与推导返回类型由表达式结果决定——这与 CreateFunctionAnalyzer.java 中new SqlFunction(functionName, argsDef.getArgTypes(), expr.getType(), ...)直接取expr.getType()作为返回类型的做法一致。3. 与内置函数自由组合CREATE FUNCTION get_year_month(dt DATETIME) RETURNS concat(year(dt), -, lpad(month(dt), 2, 0)); SELECT get_year_month(create_time) as ym FROM events GROUP BY ym;查看与管理查看所有函数-- Show functions in current database SHOW FUNCTIONS; -- Show all global functions SHOW GLOBAL FUNCTIONS;删除函数-- Drop function in current database DROP FUNCTION format_username(STRING); -- Drop global function DROP GLOBAL FUNCTION format_date_display(DATETIME); -- Use IF EXISTS to avoid errors DROP FUNCTION IF EXISTS my_function(INT, STRING);替换函数使用OR REPLACE关键字覆盖同名函数定义-- Modify function definition CREATE OR REPLACE FUNCTION calculate_tax(amount DECIMAL(10,2)) RETURNS amount * 0.1 5.0;性能特征为什么 SQL UDF 几乎没有开销SQL UDF 在查询优化阶段被展开为实际的 SQL 表达式由此带来三个性能特征零函数调用开销不存在传统 UDF 的进程内调用/跨语言边界开销优化器可见性优化器能看到完整表达式从而应用更多优化规则谓词下推等优化展开后的表达式可参与谓词下推等规则。例如CREATE FUNCTION my_func(x INT) RETURNS x * 2 1; SELECT * FROM t WHERE my_func(a) 10;会被优化器展开为SELECT * FROM t WHERE a * 2 1 10;源码层面的展开路径从源码结构看这一展开发生在 FE 的 SQL→优化器算子翻译阶段。SqlToScalarOperatorTranslator.java 在处理函数调用时对 SQL 函数SqlFunction直接使用其analyzeExpr分析期保存的表达式树翻译为标量算子而不是像传统 UDF 那样生成一个黑盒函数调用算子。此外SqlFunction.java 的toThrift()方法直接Preconditions.checkState(false, sql function does not support toThrift)——SQL 函数不会以 Thrift 形式下发给 BE因为它在 FE 侧就已经完全消化成了普通表达式BE 根本感知不到这个函数的存在。这与文档所述零函数调用开销相互印证。同时SqlFunction将表达式文本以SerializedName(sql)持久化在 catalog 中getProperties()返回该 SQL 文本并通过toSql()生成CREATE ... RETURNS expr形式的 DDL 用于备份/恢复保证函数定义随元数据一起流转。限制与注意事项参数数量限制参数数量必须与表达式中使用的变量一致——函数体内引用的每一个参数名都必须出现在参数列表中反之参数也应被表达式合理使用类型匹配参数类型需与表达式中的使用方式匹配类型不兼容的表达式会在分析阶段报错。补充一个实践视角由于 SQL UDF 在分析期就会执行ExpressionAnalyzer校验见 CreateFunctionAnalyzer.javaCREATE FUNCTION时如果表达式引用了不存在的内置函数或类型推导失败会立即失败而不是等到查询时才暴露——这为函数即表达式的类型安全提供了前置保障。小结SQL UDF 是 StarRocks 4.1 引入的轻量函数机制CREATE FUNCTION ... RETURNS expression一条语句即可完成表达式 → 可复用函数的封装查询时函数在优化阶段被展开为原始表达式使谓词下推等优化规则照常生效且无需任何外部运行时。对于重复表达式、统一口径的计算逻辑它是成本最低的方案当逻辑复杂到超出单条表达式的能力范围例如需要循环、状态或外部调用时则应考虑仓库中同时提供的传统 UDF 路径Java/Python见 CreateFunctionStmt.java 中TYPE_STARROCKS_JAR/TYPE_STARROCKS_PYTHON所代表的 UDF 模式。参考文档SQL_UDF.md。【免费下载链接】starrocksThe worlds fastest open query engine for sub-second analytics both on and off the data lakehouse. With the flexibility to support nearly any scenario, StarRocks provides best-in-class performance for multi-dimensional analytics, real-time analytics, and ad-hoc queries. A Linux Foundation project.项目地址: https://gitcode.com/GitHub_Trending/st/starrocks创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻