从 Spark 写入时避免丢失分区数据的数据类型

sur*_*nto 8 apache-spark parquet apache-spark-sql

我有一个如下所示的数据框。

itemName, itemCategory
Name1, C0
Name2, C1
Name3, C0
Run Code Online (Sandbox Code Playgroud)

我想将此数据框保存为分区镶木地板文件:

df.write.mode("overwrite").partitionBy("itemCategory").parquet(path)
Run Code Online (Sandbox Code Playgroud)

对于这个数据帧,当我读回数据时,它将具有 String 的数据类型itemCategory

然而有时,我有来自其他租户的数据框,如下所示。

itemName, itemCategory
Name1, 0
Name2, 1
Name3, 0
Run Code Online (Sandbox Code Playgroud)

在这种情况下,在写入分区后,读回时,生成的数据帧将具有 Int 的数据类型itemCategory

Parquet 文件具有描述数据类型的元数据。如何指定分区的数据类型,以便将其读回为 String 而不是 Int?

Dav*_*ler 9

如果将“spark.sql.sources.partitionColumnTypeInference.enabled”设置为“false”,spark 会将所有分区列推断为字符串。

在spark 2.0或更高版本中,你可以这样设置:

spark.conf.set("spark.sql.sources.partitionColumnTypeInference.enabled", "false")
Run Code Online (Sandbox Code Playgroud)

在1.6中,像这样:

sqlContext.setConf("spark.sql.sources.partitionColumnTypeInference.enabled", "false")
Run Code Online (Sandbox Code Playgroud)

缺点是每次读取数据时都必须执行此操作,但至少它有效。


Sha*_*ica 0

当您按列分区时itemCategory,该数据将存储在文件结构中,而不是实际的 csv 文件中。Spark 根据值推断数据类型,如果所有值都是整数,则列类型将为 int。

一种简单的解决方案是StringType在读取数据后将列转换为:

import spark.implicits._
df.withColumn("itemCategory", $"itemCategory".cast(StringType))
Run Code Online (Sandbox Code Playgroud)

另一种选择是复制列本身。然后其中一列将用于分区,因此保存在文件结构中。但是,其他重复的列将正常保存在镶木地板文件中。要制作副本,只需使用:

df.withColumn("itemCategoryCopy", $"itemCategory")
Run Code Online (Sandbox Code Playgroud)