Spark SQL查询Hive表某列返回NULL,Hive查询却有值的问题求助
这个问题的核心在于Spark SQL读取Hive表时的Schema来源和Hive本身、直接读取Parquet文件的逻辑不一样,我来帮你拆解原因和解决办法:
先明确Spark SQL获取Hive表Schema的逻辑
当你用Spark SQL查询Hive分区表时,Spark是直接从**Hive Metastore(元数据存储)**中读取表的Schema定义,包括列名、数据类型、分区信息等,然后基于这个元数据去加载底层的Parquet文件。而:
- Hive直接查询时,可能会自动校验并适配Parquet文件的实际Schema;
- 你直接用
read.parquet读取文件时,Spark是从Parquet文件的Footer中直接读取Schema,完全依赖文件本身的元数据。
这就导致如果Metastore中的Schema和实际Parquet文件的Schema不匹配,Spark SQL就会无法正确映射列,返回NULL。
可能的问题点&对应的解决方案
1. Hive表元数据与Parquet文件Schema不匹配
这是最常见的原因:比如你创建Hive表时定义的headertime列,在数据类型、列名拼写(甚至大小写)上和Parquet文件中的实际列不一致。
排查&解决:
- 先对比两者的Schema:
- 用Hive命令查看表元数据:
DESCRIBE dbName.test_bug; - 在Scala Notebook中读取Parquet文件后打印Schema:
val searchRequest = session.sqlContext.read.parquet("s3n://bucketName/module/search_request/eventDate=20180510") searchRequest.printSchema()
- 用Hive命令查看表元数据:
- 如果发现不一致(比如Hive表中
headertime定义为string,但Parquet中是timestamp;或者列名大小写不同),需要更新Hive表的元数据:- 可以用
ALTER TABLE语句修正列定义:ALTER TABLE dbName.test_bug REPLACE COLUMNS ( -- 这里列出所有列,确保和Parquet文件Schema一致 headertime timestamp, -- 其他列... eventdate string ); - 或者如果表结构差异较大,删除表后重新创建,确保
CREATE TABLE语句的列定义完全匹配Parquet文件的Schema(可以用read.parquet的Schema来生成建表语句)。
- 可以用
2. Spark关闭了Parquet Schema合并功能
Spark默认关闭了Parquet的Schema合并(spark.sql.parquet.mergeSchema默认值为false),这意味着Spark只会用Metastore中的Schema去读取文件,不会自动合并文件中的额外列或修正列定义。而Hive查询时可能会自动处理这种Schema不一致的情况。
解决:
在创建SparkSession时开启Schema合并功能(适合临时排查,注意会影响性能):
val session = org.apache.spark.sql.SparkSession.builder .appName("searchRequests") .enableHiveSupport() .config("spark.sql.parquet.mergeSchema", "true") // 开启Schema合并 .getOrCreate()
开启后,Spark会合并Metastore的Schema和Parquet文件的Schema,这样就能正确读取到headertime列的值。
3. 列名大小写敏感性问题
Hive默认是不区分列名大小写的,但Spark SQL默认是区分大小写的(由spark.sql.caseSensitive配置控制)。如果Hive表元数据中的列名和Parquet文件中的列名大小写不一致(比如Hive表是HEADERTIME,Parquet文件是headertime),Spark SQL查询时用小写就会找不到列,返回NULL。
解决:
- 要么修改Spark配置关闭大小写敏感性:
val session = org.apache.spark.sql.SparkSession.builder .appName("searchRequests") .enableHiveSupport() .config("spark.sql.caseSensitive", "false") .getOrCreate() - 要么查询时使用和Hive表元数据完全一致的列名(比如Hive表中是大写,就用
SELECT HEADERTIME ...)。
总结
你遇到的问题本质是Hive元数据和Parquet文件Schema不匹配,导致Spark SQL基于元数据读取时无法映射到正确的列。按照上面的步骤排查元数据一致性、开启Schema合并或调整大小写配置,就能解决这个问题。
内容的提问来源于stack exchange,提问作者user2221654

