Pyspark 可为空的 uuid 类型 uuid 但表达式的类型为字符变化

Posted

技术标签:

【中文标题】Pyspark 可为空的 uuid 类型 uuid 但表达式的类型为字符变化【英文标题】:Pyspark nullable uuid type uuid but expression is of type character varying 【发布时间】:2020-11-03 22:39:30 【问题描述】:

给定一个带有 non-nullable uuid 列和 nullable uuid 列的表设计,如何使用 python 3.7.9 和 Pyspark 2.4.3 数据框插入postgresql-42.2.18.jar 驱动?

table_df = spark.read.format('jdbc) \
                     .option('driver', 'org.postgresql.Driver') \
                     .option('dbtable', 'example_table') \
                     .load()

table_df.printSchema()

root
 |-- id: string (nullable = false)
 |-- created: timestamp (nullable = true)
 |-- modified: timestamp (nullable = true)
 |-- example_uuid: string (nullable = true)


from pyspark.sql.functions import when, lit, col

from pyspark.sql.types import NullType, StringType

def replace(column, value):
  return when (column == value, lit(None).cast(NullType())).otherwise(column.cast(StringType()))

example_df = tasklog_df.withColumn("example_uuid", replace(col("example_uuid"), "NULL"))

example_df.write.mode('append').format('jbdc') \
                .option('driver', 'org.postgresql.Driver')\
                .option('stringtype', 'unspecified') \
                .save()

这会导致 Pyspark 尝试插入

INSERT INTO example_table
 ("id",
 "created",
 "modified",
 "example_uuid") 
VALUES 
 ('b49a90aa-a415-4aeb-a7ed-bfc42e43f5c7',
 '2020-03-29 02:00:11.06534-07',
 '2020-03-29 02:00:11.065361-07',
 NULL)

这会导致臭名昭著

ERROR: column "example_uuid" is of type uuid but expression is of type character
  Hint: You will need to rewrite or cast the expression.

我已经投射了数据。 Pyspark 没有生成正确的 INSERT 语句,或者 postgres 驱动程序将单词 NULL 视为字符而不是关键字。我需要使用.option('stringtype', 'unspecified'),以免Pyspark 抱怨id 列是uuid

lit(None).cast(NullType()) 似乎什么也没做。 pyspark.sql.types 中没有 uuid 类型的条目。

如果没有option('stringtype', 'unspecified'),那么 Pyspark 会抛出错误:

Caused by: org.postgresql.util.PSQLException: ERROR: column "id" is of type uuid but expression is of type character varying
  Hint: You will need to rewrite or cast the expression.

剩下的唯一方法似乎是将数据帧拆分为两个数据帧,一个具有包含 NULL 的 example_uuid 字段,另一个是 example_uuid 字段是 uuid。然后使用 NULL 从数据框中删除 example_uuid 字段,以便在保存到表时不会引发错误。当 Pyspark 应该只支持 uuid 类型时,这似乎浪费了很多精力。建议或建议?

【问题讨论】:

【参考方案1】:

在 pyspark 中包含此功能需要将相应的类型添加到 Spark SQL 中,并且添加可能需要付出很大的努力,因为它需要更改优化器和其他部分。作为解决方法,这应该在 JDBC 驱动程序中完成(但这可能违反 JDBC 规范),或者在 Spark 本身的 JDBC 连接器中完成,但它不会与数据库无关。例如,Spark Cassandra 连接器在将数据保存到 Cassandra 时会自动转换为“兼容”,这允许它在读/写时支持 UUID,尽管在 Spark 中它们表示为字符串。

【讨论】:

【参考方案2】:

我知道这很讨厌并且会影响存储,但在解决此问题之前(即支持 UUID 数据类型和/或能够为 UUID 字段写入 NULL),我选择存储零 GUID (00000000-0000-0000- 0000-000000000000)。

.withColumn("nullable_uuid_field", coalesce($"nullable_uuid_field", lit("00000000-0000-0000-0000-000000000000")))

【讨论】:

【参考方案3】:

我个人最终拆分了我的写入并依赖 db 来设置 null。

insert(to_insert.where(F.col("col_name").isNull()).drop("col_name"))
insert(to_insert.where(F.col("col_name").isNotNull()))

【讨论】:

以上是关于Pyspark 可为空的 uuid 类型 uuid 但表达式的类型为字符变化的主要内容,如果未能解决你的问题,请参考以下文章

Typescript 映射类型,其中仅保留可为空的属性并转换为字符串类型

XmlCodeExporter 和可为空的类型

c#静态类中的8个可为空的引用类型

如何使用 System.Text.Json 处理可为空的引用类型?

如何将 CheckBox 绑定到可为空的布尔类型 DbColumn?

.NET 使用可为空的引用类型实现 IEnumerator