SELECT查询中的PreparedStatement setNull

时间:2017-11-16 22:28:14

标签: postgresql jdbc

我正在使用Postgresql和HikariCP,我的查询类似于

SELECT * FROM my_table WHERE int_val = ? ...

现在,我想为我的变量设置NULL值 - 我已经尝试了

ps.setNull(1, Types.INTEGER); // ps is instance of PreparedStatement
try (ResultSet rs = ps.executeQuery()) {
... // get result from resultset
}

虽然我有符合条件的行('int_val'列中的NULL),但我没有收到任何记录..

问题是(我认为)在Statement生成的查询中,如下所示:

System.out.println(ps.toString());
// --> SELECT * FROM my_table WHERE int_val = NULL ...

但查询应如下所示:

“SELECT * FROM my_table WHERE int_val IS NULL ...” - 此查询有效

我需要动态创建包含NULL值的PreparedStatements,所以我不能轻易绕过这个。

我尝试在没有HikariCP的情况下创建具有相同结果的连接,所以我认为问题出在postgresql驱动程序中?或者我做错了什么?

更新

根据@Vao Tsun的回答,我在postgresql.conf中设置了transform_null_equals = on,后者开始更改val = null - > “简单”语句中的val is null,但不在PreparedStatements中。

总结:

try (ResultSet rs = st.executeQuery(SELECT * FROM my_table WHERE int_val = NULL)){ 
  // query is replaced to '.. int_val IS NULL ..' and gets correct result
}

ps.setNull(1, Types.INTEGER);
try (ResultSet rs = ps.executeQuery()) {
  // Does not get replaced and does not get any result
}

我使用的是JVM版本1.8.0_121,最新的postgres驱动程序(42.1.4),但我也尝试过较旧的驱动程序(9.4.1212)。数据库版本 - PostgreSQL 9.6.2,由Visual C ++ build 1800,64位编译。

3 个答案:

答案 0 :(得分:2)

这意味着比较x = null的行为等于null(无论x等于什么)。基本上对于SQL NULLunknown,而不是实际值...要绕过它,您可以将transform_null_equals设置为ontrue。请查看文档:

https://www.postgresql.org/docs/current/static/functions-comparison.html

  

某些应用程序可能期望expression = NULL返回true   expression的计算结果为null值。强烈建议   修改这些应用程序以符合SQL标准。   但是,如果无法完成transform_null_equals   配置变量可用。如果启用,PostgreSQL会   将x = NULL子句转换为x IS NULL。

答案 1 :(得分:1)

我刚刚找到了一个解决方案,对于"值和#34;和" NULLs"使用IS NOT DISTINCT FROM代替=

More on postgresql wiki

答案 2 :(得分:0)

重要的是要认识到null不是SQL的值。它正在编码" unknown "的逻辑概念。这就是null = var始终false导致var的原因,即使null的值为?的情况也是如此。因此,即使您使用值null替换变量的值(在您的情况下也称为null),结果定义必须不是您所期望的,只要SQL标准是符合。

现在有一些数据库试图通过假设null的列值应被视为编程语言nilundefnull来超越SQL标准或用于此目的的任何东西)。 这为不谨慎的程序员创造了一些便利,但从长远来看,只要你需要在SQL null和编程语言transform_null_equals之间进行真正的区分,就会产生悲伤。

然而,为了便于从这些数据库移植到PostgresQL(或简单易于编程),您可以设置VAR is null

但是,您正在使用预备语句。因此,预准备语句将转换为查询计划一次,并且此类查询计划需要对预准备语句查询中使用的变量的所有潜在值有效。现在,VAR = ?transform_null_equals根本不同。因此,查询解析器,查询优化器甚至查询执行引擎都无法根据传入的实际参数值动态重写(已准备好的)查询。

由此,您应该采用VAR is null文档中提供的严肃建议,并在搜索空值时将代码更改为使用VAR = ?并{{1}对于其他情况。