UCanAccess读取Access表重复将列名作为数据返回如何解决
异常根因
该异常是UCanAccess 5.0.1版本已知游标初始化缺陷与Spark 3.1.2 JDBC数据源默认读取逻辑共同触发的兼容问题:
Spark JDBC读取表时会在同一个JDBC连接上先后执行两次操作:第一次查询表元数据获取列结构,第二次拉取全表数据统计行数、返回记录。UCanAccess 5.0.1在同连接连续执行同表查询时,结果集游标不会自动重置到真实数据行的起始位置,会停留在驱动内部缓存的列名元数据行,后续遍历行时持续返回列名值;由于驱动内部缓存的真实数据行数统计正常,因此df.count()会返回正确的记录数,printSchema()也能正常识别列结构,但实际读取的行内容全部为列名。
Databricks 9.1LTS默认开启JDBC连接复用,不会为元数据查询和数据查询单独创建新连接,刚好触发该缺陷。
排查思路
- 先排除源文件问题:将Access文件下载到本地,用本地Access客户端打开,确认Table1内存储的真实数据和预期一致,没有误把列名存成数据行、也没有错误设置"第一行作为列标题/数据"的表属性。
- 验证驱动兼容问题:在同集群用Scala/Java原生JDBC代码写测试逻辑,在单个UCanAccess连接上连续执行两次
SELECT * FROM Table1,如果第二次查询返回的内容全部是列名,即可确认是驱动本身的游标bug。 - 参数调整验证:在JDBC URL中逐个追加UCanAccess官方提供的缓存、连接控制参数,测试数据读取是否恢复正常,定位触发bug的配置项。
- 版本对比验证:替换不同版本的UCanAccess驱动(比如4.0.4、5.0.2),测试相同读取代码的返回结果,确认版本兼容问题。
可行解决方案
按落地成本从低到高排序:
- 调整JDBC连接URL参数,绕过bug
不需要更换驱动,直接在原有URL后追加参数,关闭驱动内存缓存和连接复用,强制每次查询重置游标:
同时读取时指定子查询代替直接读表,避免Spark拆分两次查询触发游标异常:url = "jdbc:ucanaccess:///dbfs/mnt/internal/Temporal/Database1.accdb;memory=false;openExclusive=false;ignoreCase=true"df = spark.read.jdbc(url=url, table="(SELECT ID, Field1, Field2 FROM Table1) AS query_res", properties=connectionProperties) - 更换UCanAccess驱动版本
该游标bug在UCanAccess 5.0.2及以上版本已经修复,也可以选择兼容性更稳定的4.0.4版本,上传到Databricks集群的依赖库中替换原有5.0.1版本即可,注意要同步上传驱动对应版本的配套依赖(jackcess、hsqldb、commons-logging等),避免类冲突。 - 调整Spark JDBC读取配置
关闭Spark的JDBC谓词下推和schema自动推断,手动指定schema,避免Spark单独发起元数据查询:from pyspark.sql.types import StructType, StructField, StringType # 手动定义表结构 custom_schema = StructType([ StructField("ID", StringType(), True), StructField("Field1", StringType(), True), StructField("Field2", StringType(), True) ]) df = spark.read.format("jdbc") \ .option("url", url) \ .option("dbtable", "Table1") \ .option("driver", "net.ucanaccess.jdbc.UcanaccessDriver") \ .option("pushDownPredicate", "false") \ .option("schema", custom_schema.json()) \ .load()
内容的提问来源于stack exchange,提问作者Alex Wong
相关产品推荐
相关产品推荐

