You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何精准提取SELECT类SQL依赖字段 剥离函数保留原始字段

SELECT SQL字段血缘解析:函数包裹字段提取优化

问题现状

  • 现有实现基于Antlr SqlBaseBaseListener 完成Java端解析逻辑,已支持SELECT语句输入表、依赖字段的基础提取
  • 现存缺陷:输出结果inPutTableAndFields会保留完整函数表达式,例如datediff(current_date(),registration_time)会被整体作为依赖字段存储,不符合血缘提取的最小粒度要求
  • 优化目标:剥离所有函数包裹层,仅提取表达式内的原始物理字段,上述示例仅保留registration_time作为有效依赖字段

核心实现逻辑

  • 废弃原有直接读取表达式整段文本存入结果的逻辑,改为递归遍历解析树节点的方式提取字段
  • 遍历规则:
    • 遇到函数调用节点时,不采集函数本身文本,递归遍历函数的所有入参节点
    • 遇到常量、字面量、无参内置函数(如current_date())节点时直接跳过,不做采集
    • 递归遍历到最底层列引用节点时,才将对应字段名加入依赖字段集合
    • 自动支持嵌套函数穿透,无需为每个函数单独写适配规则

关键代码修改

import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTree;
import java.util.HashSet;
import java.util.Set;

// 在自定义的SqlBaseListener实现类中新增以下方法
/**
 * 从表达式节点递归提取原始物理字段
 */
private Set<String> extractRawFields(ParserRuleContext exprNode) {
    Set<String> result = new HashSet<>();
    // 命中列引用节点,直接加入结果集
    if (exprNode instanceof ColumnReferenceContext) {
        result.add(exprNode.getText());
        return result;
    }
    // 命中函数节点,仅遍历参数,不采集函数本身
    if (exprNode instanceof FunctionCallContext) {
        FunctionCallContext funcNode = (FunctionCallContext) exprNode;
        if (funcNode.expression() != null) {
            for (ExpressionContext argExpr : funcNode.expression()) {
                result.addAll(extractRawFields(argExpr));
            }
        }
        return result;
    }
    // 命中常量节点直接跳过
    if (exprNode instanceof ConstantContext) {
        return result;
    }
    // 其他类型节点继续向下遍历子节点
    for (int i = 0; i < exprNode.getChildCount(); i++) {
        ParseTree child = exprNode.getChild(i);
        if (child instanceof ParserRuleContext) {
            result.addAll(extractRawFields((ParserRuleContext) child));
        }
    }
    return result;
}

// 修改原有字段采集入口逻辑,示例为select单字段退出时的处理
@Override
public void exitSelectSingle(SelectSingleContext ctx) {
    // 原有逻辑:String fullExpr = ctx.expression().getText(); 直接存入inPutTableAndFields
    // 替换为递归提取原始字段
    Set<String> rawFields = extractRawFields(ctx.expression());
    // 对齐原有逻辑里当前上下文对应的输入表,将提取到的字段存入inPutTableAndFields
    for (String field : rawFields) {
        // addFieldToCurrentInputTable为原有实现中表和字段映射的存储方法
        addFieldToCurrentInputTable(field);
    }
}

覆盖场景说明

  • 无参内置函数:如datediff(current_date(), reg_time)会自动过滤无参的current_date(),仅提取reg_time
  • 常量混合参数:如concat('prefix_', user_id, '_suffix')会过滤字符串常量,仅提取user_id
  • 多层嵌套函数:如date_format(from_unixtime(create_time), '%Y%m%d')会穿透两层函数,仅提取create_time
  • 运算表达式:如price * discount as actual_price会跳过运算符,提取price、discount两个物理字段

内容的提问来源于stack exchange,提问作者Huanqing Cheng

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.20 16:15:50