如何用Scala向PostgreSQL插入Interval类型值?报错求助
解决Scala向PostgreSQL interval字段插入数据的报错问题
问题场景
尝试用Scala计算任务运行时长,插入PostgreSQL表的interval类型字段job_run_duration时,两次尝试均出现报错:
第一次尝试代码与报错
var tm1 = LocalDateTime.now() var tm2 = LocalDateTime.now() var milliseconds = tm1.until(tm2, ChronoUnit.MILLIS) var seconds = (milliseconds / 1000) % 60 var minutes = (milliseconds / (1000*60)) % 60 var hours = (milliseconds / (1000*60*60)) % 24 var milisec = milliseconds - ((seconds * 1000) + (minutes * 60 * 1000) + (hours * 60 *60 * 1000)) var secondsNew = if (seconds.toString().length() == 1) "0" + seconds else seconds var minutesNew = if (minutes.toString().length() == 1) "0" + minutes else minutes var hoursNew = if (hours.toString().length() == 1) "0" + hours else hours var milisecNew = if (milisec.toString().length() == 1) milisec + "00" else if (milisec.toString().length() == 2) milisec + "0" else milisec var timeString = "%s:%s:%s".format(hoursNew, minutesNew, secondsNew) var duration = LocalTime.parse(timeString) var tm = Time.valueOf(DateTimeFormatter.ofPattern("HH:mm:ss").format(duration)) // 插入语句 sql = "INSERT into etl.mls_refresh_snapshot(job_name,job_type,job_frequency, source_table_name,source_count,target_count,job_run_duration,started_at,ended_at) VALUES(?,?,?,?,?,?::interval,?,?)" var statement : PreparedStatement = conn.prepareStatement(sql) statement.setTime(6,Time.valueOf(DateTimeFormatter.ofPattern("HH:mm:ss.SSS").format(duration)))
报错信息:
java.lang.NumberFormatException: For input string: "18.000"
第二次修改后代码与报错
var timeString = "%s:%s:%s.%s".format(hoursNew, minutesNew, secondsNew, milisecNew) var duration = LocalTime.parse(timeString) var tm = Time.valueOf(DateTimeFormatter.ofPattern("HH:mm:ss.SSS").format(duration))
报错信息:
ERROR: invalid input syntax for type interval: "00:02:07+05:30"
问题原因
- 类型不匹配:
Time对应SQL的TIME类型(表示一天中的时刻),并非用于传递时长的interval类型,强行将带毫秒的字符串转为Time会导致解析失败。 - 格式错误:
LocalTime会附带时区偏移信息(如+05:30),而PostgreSQL的interval不接受带时区的格式,仅支持纯时长格式。
解决方案
方案1:直接构造PostgreSQL兼容的interval字符串
手动计算时长各部分,格式化出HH:MM:SS.SSS格式的字符串,直接通过setString传递:
val tm1 = LocalDateTime.now() val tm2 = LocalDateTime.now() val milliseconds = tm1.until(tm2, ChronoUnit.MILLIS) val seconds = (milliseconds / 1000) % 60 val minutes = (milliseconds / (1000*60)) % 60 val hours = (milliseconds / (1000*60*60)) % 24 val milisec = milliseconds % 1000 // 简化毫秒计算,直接取余1000 // 用f-string自动补零,生成标准时长格式 val intervalStr = f"$hours%02d:$minutes%02d:$seconds%02d.$milisec%03d" // 插入SQL无需额外的::interval转换(JDBC可自动识别) val sql = """INSERT into etl.mls_refresh_snapshot( job_name,job_type,job_frequency, source_table_name,source_count,target_count, job_run_duration,started_at,ended_at ) VALUES(?,?,?,?,?,?,?,?,?)""" val statement: PreparedStatement = conn.prepareStatement(sql) statement.setString(6, intervalStr) // 直接传递字符串格式的时长 // 设置其他参数... statement.executeUpdate()
方案2:用Java Duration类优雅处理时长
利用Duration类直接计算时间间隔,避免手动计算错误:
val tm1 = LocalDateTime.now() val tm2 = LocalDateTime.now() val duration = Duration.between(tm1, tm2) // 直接获取时长各部分 val hours = duration.toHoursPart val minutes = duration.toMinutesPart val seconds = duration.toSecondsPart val millis = duration.toMillisPart val intervalStr = f"$hours%02d:$minutes%02d:$seconds%02d.$millis%03d" // 后续插入代码同方案1,用setString传递intervalStr
关键注意点
- 不要用
Time或LocalTime类型传递interval数据,二者分别对应时刻和带时区的时间,与时长类型不匹配。 - PostgreSQL的
interval支持多种格式,HH:MM:SS.SSS是最直观且易构造的标准格式。
内容的提问来源于stack exchange,提问作者Pelab
相关产品推荐
相关产品推荐

