Databricks Runtime11.3+使用unionByName时出现NoSuchElementException问题
异常触发原因及复现方式
触发原因
- Spark版本升级后的元数据处理差异:Databricks Runtime 11.3基于Spark 3.3.x,相比10.4对应的Spark 3.2.x,在列元数据跟踪、
unionByName的列解析逻辑上做了调整。当使用F.col("OLD.SOME_COLUMN")这种带表别名/前缀的方式删除列时,Spark内部可能未彻底清理该列的原始标识符(如SOME_COLUMN#28800)引用,导致合并后的DataFrame元数据存在残留。 allowMissingColumns参数的行为变化:在11.3+版本中,unionByName处理缺失列时,对已删除列的元数据清理不彻底。而display函数会触发额外的元数据校验与自定义显示数据生成(对应报错中的addCustomDisplayData),这一步在10.4版本中未做严格校验,升级后便暴露了残留列标识符的问题。display函数的深层校验逻辑:Databricks的display操作并非简单的结果展示,会触发DataFrame的元数据完整性检查,当合并后的DataFrame元数据中存在已删除列的标识符引用时,就会抛出NoSuchElementException。
复现步骤
- 创建测试集群:启动Databricks Runtime 11.3或更高版本的集群。
- 构造测试数据与DataFrame:
import pyspark.sql.functions as F # 构造带前缀列的原始DataFrame dfWithErrId = spark.createDataFrame( [(1, "test_val", 101)], ["ID", "OLD.SOME_COLUMN", "OLD.ERR_ID_COLUMN"] ).withColumn("TMP_ERR_ID_COLUMN", F.lit(202)) # 按原方式删除列得到dfA dfA = ( dfWithErrId.drop(F.col("OLD.SOME_COLUMN")) .drop(F.col("OLD.ERR_ID_COLUMN")) .drop("TMP_ERR_ID_COLUMN") ) # 构造dfC并过滤得到dfB dfC = spark.createDataFrame([(2, "valid_val")], ["ID", "OTHER_COLUMN"]) nullSafeErrorCondition = "OTHER_COLUMN IS NULL" dfB = dfC.where(f"!({nullSafeErrorCondition})") - 执行合并并触发display:
dfResult = dfA.unionByName(dfB, allowMissingColumns=True) display(dfResult)
执行上述代码后,会抛出与你遇到的一致的java.util.NoSuchElementException: key not found: SOME_COLUMN#[数字]异常。
临时修复方案
- 修改列删除方式:删除列时直接使用列名字符串,而非带前缀的
F.col引用,避免残留元数据:dfA = dfWithErrId.drop("OLD.SOME_COLUMN", "OLD.ERR_ID_COLUMN", "TMP_ERR_ID_COLUMN") - 重建DataFrame元数据:删除列后通过
select显式指定保留的列,强制刷新元数据:retained_cols = [col for col in dfWithErrId.columns if col not in ["OLD.SOME_COLUMN", "OLD.ERR_ID_COLUMN", "TMP_ERR_ID_COLUMN"]] dfA = ( dfWithErrId.drop(F.col("OLD.SOME_COLUMN")) .drop(F.col("OLD.ERR_ID_COLUMN")) .drop("TMP_ERR_ID_COLUMN") ).select(retained_cols)
内容的提问来源于stack exchange,提问作者Murtaza
相关产品推荐
相关产品推荐

