使用Polybase T-SQL导入分区Parquet至Azure Synapse表失败求助
问题背景
用Spark按Sport列分区保存Parquet文件后,通过Polybase创建外部表时出现以下错误:
External file access failed due to internal error: 'File test_partition_polybase.parquet/Sport=Tennis/part-00000-tid-5109481329839379912-631db0ad-cd52-4f9e-acf6-76828a8aa4eb-67-1.c000.snappy.parquet: HdfsBridge::CreateRecordReader - Unexpected error encountered creating the record reader: HadoopExecutionException: Column count mismatch. Source file has 2 columns, external table definition has 3 columns
原因是Spark分区保存Parquet时,会将分区列(Sport)从数据文件中移除,仅通过目录路径(如Sport=Tennis)存储分区信息,而外部表定义中包含了Sport列,导致列数不匹配。
解决方法
方法1:修改Spark保存逻辑,将分区列保留在数据文件中
Spark 3.0及以上版本支持includePartitionColumns选项,设置为true后,分区列会同时写入数据文件和目录路径,这样外部表可以直接读取完整的3列数据。
修改后的Spark保存代码:
data = [["Jean", 15, "Tennis"], ["Jane", 20, "Yoga"], ["Linda", 35, "Yoga"], ["Linda", 35, "Tennis"]] columns = ["Name", "Age", "Sport"] df = spark.createDataFrame(data, columns) df\ .write\ .option("header", True)\ .option("includePartitionColumns", "true")\ # 添加此选项保留分区列在文件中 .partitionBy("Sport")\ .format("parquet")\ .mode("overwrite")\ .save("/mnt/test_partition_polybase.parquet")
重新保存后,原有的外部表创建语句无需修改,即可正常执行。
方法2:修改外部表定义,利用分区路径提取Sport列
如果无法修改Spark保存逻辑,可以调整外部表的定义,将Sport作为分区列,通过目录路径的元数据获取该列的值,无需从数据文件中读取。
步骤1:创建不含分区列的外部表框架
IF OBJECT_ID(N'EVENTSTORE.TEST') IS NOT NULL BEGIN DROP EXTERNAL TABLE EVENTSTORE.TEST END CREATE EXTERNAL TABLE EVENTSTORE.TEST( [Name] NVARCHAR(250), [Age] BIGINT ) WITH ( DATA_SOURCE = [my_data_source], LOCATION = N'test_partition_polybase.parquet', FILE_FORMAT = [SynapseParquetFormat], REJECT_TYPE = VALUE, REJECT_VALUE = 0 ) PARTITIONED BY ([SPORT] NVARCHAR(250))
步骤2:手动添加分区(对应目录路径)
为每个分区目录添加对应的分区信息:
ALTER EXTERNAL TABLE EVENTSTORE.TEST ADD PARTITION (SPORT = 'Tennis') LOCATION 'test_partition_polybase.parquet/Sport=Tennis' ALTER EXTERNAL TABLE EVENTSTORE.TEST ADD PARTITION (SPORT = 'Yoga') LOCATION 'test_partition_polybase.parquet/Sport=Yoga'
步骤3:验证查询
执行查询时,Sport列会自动从分区路径中提取:
SELECT * FROM EVENTSTORE.TEST
内容的提问来源于stack exchange,提问作者Quynh-Mai Chu

