PAYATHON 2026

如何解决 Databricks 中 Spark 的 CAST_INVALID_INPUT 错误?

数据小许

结论

先定位查询中发生 STRING → BIGINT 转换的表达式,并确认比较双方的类型。只添加 UPPER(...) <> 'UNKNOWN',不一定能消除转换错误。

优先检查这两个位置:

  • B.ITEM_CODE IN (2009, 2012):如果 ITEM_CODE 是 STRING,数字字面量可能促使 Spark 将列值隐式转换为数值类型。
  • A.PRODUCT_ID = B.PRODUCT_ID:如果两侧分别是字符串和整数类型,连接时也可能发生隐式转换。

缺少表结构和执行计划,还不能确定是哪一列触发了错误。Databricks 的类型规则允许在类型协调时将 STRING 转换为 BIGINT。遇到 'UNKNOWN' 这样的不合法值,执行时就可能报错。

为什么子查询中过滤后仍会报错?

子查询并不是一张清洗后保存下来的中间表。Spark 优化整个查询时,可能合并子查询、下推筛选条件或调整表达式的位置。因此,不能根据 SQL 的书写顺序,认定排除 'UNKNOWN' 的条件一定先于数值转换执行。

Databricks 的表达式求值说明指出,Spark SQL 不保证子表达式的求值顺序,AND、OR 也没有固定的从左到右短路语义。仅靠排列条件或嵌套子查询,无法保证先过滤,再执行可能失败的转换。

此外,UPPER(col) <> 'UNKNOWN' 只排除了一个特定文本值,既不能验证该列是否能转换为整数,也不能排除带空格的 ' UNKNOWN ' 或其他非数字内容。

这些是原有过滤方法的局限。要确定当前查询的具体触发路径,仍需查看执行计划。

第一步:检查字段类型和隐式转换位置

先查看两张表的 schema:

spark.table("TABLE_1").printSchema()
spark.table("TABLE_2").printSchema()

重点核对两侧的 PRODUCT_ID、B.ITEM_CODE 和 A.ORDER_DATE。然后查看原始查询生成的 DataFrame 的执行计划:

df.explain("extended")

在分析后的逻辑计划和优化计划中,查找针对相关列的 cast(... as bigint)。同时检查完整异常中的 SQL 上下文:如果错误提供了表达式位置,可以据此缩小范围。CAST_INVALID_INPUT 错误说明也指出,失败的转换可能由系统隐式插入。

df.display() 需要实际读取和计算数据,所以运行时的转换错误可能直到展示结果时才暴露。

第二步:按字段含义选择修复方式

ITEM_CODE 是文本编码时,使用字符串字面量

如果 schema 确认 ITEM_CODE 是 STRING,业务上也按文本编码匹配,可以将条件改为:

AND B.ITEM_CODE IN ('2009', '2012')

这样会按字符串比较,无需将 'UNKNOWN' 转换成整数。这个改动只处理 ITEM_CODE 条件;如果连接键仍有类型冲突,还需要单独处理。

字段确实表示整数时,使用 try_cast

如果字段应该按整数解释,并且允许无效值不参与查询,可以使用:

AND TRY_CAST(B.ITEM_CODE AS BIGINT) IN (2009, 2012)

对于支持的类型转换,try_cast 遇到格式不合法或超出目标类型范围的值时会返回 NULL。这里的 NULL IN (2009, 2012) 不会使该行通过筛选。具体行为见 try_cast 文档。

如果连接两侧的 PRODUCT_ID 都应该按整数解释,也可以写成:

ON TRY_CAST(A.PRODUCT_ID AS BIGINT)
   = TRY_CAST(B.PRODUCT_ID AS BIGINT)

转换失败得到的 NULL 不会在这个等值 INNER JOIN 中匹配。使用 try_cast 可以让转换表达式本身处理无效输入,避免依赖过滤条件的执行先后。

完整示例:按整数匹配产品 ID 和商品编码

以下示例有两个前提:PRODUCT_ID 和 ITEM_CODE 的业务含义都是整数;ORDER_DATE 已经是适合原日期条件的 DATE 或 TIMESTAMP 类型。

df = spark.sql('''
SELECT A.CUSTOMER_ID
FROM TABLE_1 A
INNER JOIN TABLE_2 B
    ON TRY_CAST(A.PRODUCT_ID AS BIGINT)
       = TRY_CAST(B.PRODUCT_ID AS BIGINT)
WHERE A.ORDER_DATE BETWEEN DATE_SUB(CURRENT_DATE, 182) AND CURRENT_DATE
  AND A.CUSTOMER_ID IS NOT NULL
  AND B.ITEM_STATUS IN ('Live', 'Active')
  AND TRY_CAST(B.ITEM_CODE AS BIGINT) IN (2009, 2012)
GROUP BY A.CUSTOMER_ID
''')

df.display()

这个改法保留了原来的日期条件、状态条件和客户去重逻辑。仅凭错误信息无法判断它是否适合实际数据,仍需先确认字段类型及匹配规则。

如果产品 ID 是文本标识符,应统一为字符串比较,例如:

ON CAST(A.PRODUCT_ID AS STRING) = CAST(B.PRODUCT_ID AS STRING)

字符串比较与整数比较的含义不同:'00123' 和 '123' 按字符串比较不相等,转成整数后则相等。应根据业务规则选择类型。

第三步:找出无效数据并核对结果

如果已经确认 ITEM_CODE 应当是整数,可以先找出无法转换的值:

SELECT ITEM_CODE, COUNT(*) AS row_count
FROM TABLE_2
WHERE ITEM_CODE IS NOT NULL
  AND TRY_CAST(ITEM_CODE AS BIGINT) IS NULL
GROUP BY ITEM_CODE
ORDER BY row_count DESC;

如果两张表的 PRODUCT_ID 也确实应当表示整数,可以用同样的方法检查。

还需要注意:

  • try_cast 会让无效值变成 NULL。查询能够完成不代表数据已经修复,应检查被排除的数据是否需要补录或纠正。
  • 如果 ORDER_DATE 是字符串,日期比较还可能触发另一种转换错误。确认其格式后,再决定是否使用 TRY_CAST(A.ORDER_DATE AS DATE);这不能直接修复当前的 BIGINT 错误。
  • 原查询仅要求 CUSTOMER_ID IS NOT NULL。额外排除字符串 'UNKNOWN' 会改变客户筛选规则,并非解决整数转换错误的必要步骤。
  • 如果表名实际指向包含普通 CAST 的视图,错误可能发生在视图内部。此时应检查视图定义中的转换位置,外层再加 try_cast 未必能处理已经发生的错误。

长期来看,应在数据接入或清洗阶段统一字段类型,明确无效值的处置规则,减少查询时对隐式转换的依赖。

备注:内容仅供参考。