我在尝试转换存储在 Spark DataFrame 中的原始时间字符串 (hh:mm:ss) 时遇到问题,其中我使用列类型为“string”的 StructType。目标是将这些时间值写入关系数据库,其中目标列类型为“时间”。
val timeRegex = "(([0-1]?[0-9])|(2[0-3])):[0-5][0-9]:[0-5][0-9]"
var convertedData = newData
newData.schema.fields.foreach { field =>
val columnName = field.name
val dataType = field.dataType
if (dataType == StringType) {
convertedData = convertedData.withColumn(
columnName,
when(expr(s"col($columnName) rlike '$timeRegex'"),
to_utc_timestamp(substring(col(columnName), 1, 8), "HH:mm:ss").cast("timestamp").cast("time"))
.otherwise(col(columnName))
)
}
}
newData 是包含我的数据的数据框,时间列是 hh:mm:ss 格式的字符串。 但是,当任何字符串(甚至是“ABCDF_12345”等非时间字符串)与 timeRegex 匹配时,就会出现问题。这会导致 else 块尝试转换为时间戳,从而导致错误。
我正在寻求有关如何修改代码或改进正则表达式的建议,以确保仅转换有效的时间字符串,而其他字符串保持不变。当前的正则表达式似乎过于宽松,允许非时间字符串匹配。
任何在 Spark SQL 中实现此转换的见解或替代方法将不胜感激。
如果 Postgres Time 列可以接受 Spark Timestamp,则带有时间的 String 字段可以转换为 Timestamp;无效时间将被转换为空值:
val df = Seq("ABCDF_12345", "13:04:22").toDF
val timeRegex = "(([0-1]?[0-9])|(2[0-3])):[0-5][0-9]:[0-5][0-9]"
val stringFieldNames = df.schema.fields
.filter(_.dataType == StringType)
.map(_.name)
def castToTimestamp = (fieldName: String) => when(expr(s"$fieldName rlike '$timeRegex'"), col(fieldName).cast(TimestampType))
val result = stringFieldNames.foldLeft(df)((acc, fieldName) => acc.withColumn(fieldName, castToTimestamp(fieldName)))
结果:
+-------------------+
|value |
+-------------------+
|null |
|2023-12-04 13:04:22|
+-------------------+
结果架构:
root
|-- value: timestamp (nullable = true)