Java Spring集成PostgreSQL时timestamp参数减interval查询报错问题
这个问题的核心是PostgreSQL在解析绑定参数参与的时间运算时,出现了类型推断偏差——它误把你右边的表达式当成了interval类型,而非预期的timestamp类型,所以才会抛出“operator does not exist: timestamp without time zone > interval”的错误。
为什么第一个查询能正常运行?
第一个查询里用的是now() - interval '24 hours',now()是PostgreSQL内置的返回timestamp with time zone的函数,和interval相减的结果类型非常明确,解析器能直接识别出是timestamp类型,和表中的timestamp字段完全匹配,所以没有问题。
可行解决方案
这里有几个按推荐度排序的解决方法:
1. 给时间运算表达式加括号,明确运算优先级
虽然SQL里减法优先级高于大于号,但绑定参数的存在可能让解析器产生误解。给:timestamp - interval '24 hours'加上括号,能强制PostgreSQL先计算这个表达式,再和表中的timestamp字段比较:
@Query(value = "SELECT timestamp, lettura, stato FROM dati_impianti WHERE sigla_impianto = :impianto and timestamp > (:timestamp - interval '24 hours') ORDER BY timestamp DESC", nativeQuery = true) List<ReadingEntity> findDate(@Param("impianto") String s, @Param("timestamp") Timestamp t);
2. 在Java层面提前计算起始时间(最稳妥)
把时间运算从数据库端转移到Java代码里,直接传入已经计算好的24小时前的时间,完全避免数据库端的类型推断问题,代码可读性也更高:
// 调用方法前先计算目标起始时间 Timestamp startTime = new Timestamp(t.getTime() - 24 * 60 * 60 * 1000); // 修改后的查询方法 @Query(value = "SELECT timestamp, lettura, stato FROM dati_impianti WHERE sigla_impianto = :impianto and timestamp > :startTime ORDER BY timestamp DESC", nativeQuery = true) List<ReadingEntity> findDate(@Param("impianto") String s, @Param("startTime") Timestamp startTime);
3. 使用PostgreSQL的make_interval函数生成区间
用make_interval函数替代直接写interval '24 hours',能让类型推断更明确:
@Query(value = "SELECT timestamp, lettura, stato FROM dati_impianti WHERE sigla_impianto = :impianto and timestamp > (:timestamp - make_interval(hours => 24)) ORDER BY timestamp DESC", nativeQuery = true) List<ReadingEntity> findDate(@Param("impianto") String s, @Param("timestamp") Timestamp t);
另外,如果你之前尝试显式类型转换出错,大概率是写法问题——正确的显式转换应该包裹整个运算表达式,比如(:timestamp::timestamp without time zone - interval '24 hours'),不过一般来说前面的方案已经足够解决问题了。
内容的提问来源于stack exchange,提问作者Mastarius

