Spark 2.3.0 Java调用UDF时触发TreeNodeException绑定属性错误
我之前在使用Spark 2.3的时候也踩过类似的字段绑定坑,结合你给出的环境(Spark 2.3.0、Java 1.8.0)、代码和报错信息,来帮你分析下问题根源和解决办法:
问题根源
从报错信息可以明确定位核心问题:
Caused by: java.lang.RuntimeException: 在字段列表[outpatient_visit_id#0,outpatient_no#1,...clinic_type#38]中未找到visit_datetime#161
== 物理计划 ==
!Project [trim(outpatient_visit_id#0, None) AS outpatient_visit_id#361,...trim(UDF(patient_age#16, visit_datetime#161, birth_date#201, false, true), None) AS patient_age#1001,...]
你传给UDF的参数里包含了visit_datetime#161这个字段,但当前的tableDataSet中根本不存在该字段(原字段列表里最大编号只到clinic_type#38),物理计划试图引用一个不存在的字段,最终导致绑定失败。
具体排查与解决步骤
1. 先确认字段是否真的存在于当前DataSet中
在调用UDF的代码之前,添加一行schema打印,直观确认所有要传给UDF的字段是否存在:
tableDataSet.printSchema(); // 打印当前DataSet的完整字段结构 UserDefinedFunction udf = (UserDefinedFunction)udfs.get(function.name + seq.length());
重点检查:
- 是否有名为
visit_datetime的字段(注意Spark字段名是大小写敏感的,别写错大小写) - 字段是否在之前的操作中被误删(比如用了
drop()或者select()只保留了部分字段)
2. 检查UDF参数列表seq的构造逻辑
你的代码里把seq作为参数传给UDF的apply()方法,要确保:
seq里的每个元素都是当前tableDataSet中已存在的Column对象,而不是凭空构造的字段名- 有没有拼写错误,比如把
visit_date写成了visit_datetime,或者字段名带了多余的空格/特殊字符
3. 排查是否引用了未生成的字段
如果visit_datetime#161是某个后续步骤才会生成的新字段(比如另一个UDF的输出),但你在当前UDF调用时就提前引用了它,这时候这个字段还没被加入到DataSet中,自然会找不到。这种情况要调整代码顺序,确保先生成该字段再引用。
4. 针对你的代码的额外建议
从你的UDF注册逻辑来看,你是根据参数数量动态获取UDF:
udfs.put(function.name + "3", udf((String a, Boolean b, Boolean c) -> "hello", DataTypes.StringType)); udfs.put(function.name + "5", udf((String a, String b, String c, Boolean d, Boolean e) -> "world", DataTypes.StringType)); // 动态获取对应参数数量的UDF UserDefinedFunction udf = (UserDefinedFunction)udfs.get(function.name + seq.length());
这里要额外确认:seq.length()和你注册的UDF的参数数量是否严格匹配,避免因为参数数量不匹配导致的隐式错误(虽然本次报错不是这个原因,但也是潜在的坑)。
内容的提问来源于stack exchange,提问作者luyao

