如何处理(也许)PreparedStatement中的空值?

声明是

SELECT * FROM tableA WHERE x = ? 

并通过java.sql.PreparedStatement'stmt'插入参数

 stmt.setString(1, y); // y may be null 

如果y为null,则语句在每种情况下都不返回行,因为x = null始终为false(应为x IS NULL )。 一个解决scheme是

 SELECT * FROM tableA WHERE x = ? OR (x IS NULL AND ? IS NULL) 

但是,然后我必须设置相同的参数两次。 有更好的解决scheme吗?

谢谢!

我一直以你在问题中performance的方式去做。 两次设置相同的参数不是一个巨大的困难,是吗?

 SELECT * FROM tableA WHERE x = ? OR (x IS NULL AND ? IS NULL); 

有一个相当未知的ANSI-SQL运算符IS DISTINCT FROM处理NULL值。 它可以这样使用:

 SELECT * FROM tableA WHERE x NOT IS DISTINCT FROM ? 

所以只有一个参数需要设置。 不幸的是,这不是MS SQL Server(2008)所支持的。

另一个解决scheme可能是,如果有一个价值,而且将永远不会被使用('XXX'):

 SELECT * FROM tableA WHERE COALESCE(x, 'XXX') = COALESCE(?, 'XXX') 

只会使用2个不同的语句:

声明1:

 SELECT * FROM tableA WHERE x is NULL 

声明2:

 SELECT * FROM tableA WHERE x = ? 

你可以检查你的variables,并根据条件build立适当的声明。 我认为这使代码更清晰,更易于理解。

编辑顺便说一句,为什么不使用存储过程? 然后你可以在SP中处理所有这些NULL逻辑,你可以简化前端调用的事情。

如果你使用例如mysql,你可能可以做如下的事情:

 select * from mytable where ifnull(mycolumn,'') = ?; 

那么你可以这样做:

 stmt.setString(1, foo == null ? "" : foo); 

你将不得不检查你的解释计划,看看它是否改善你的performance。 它虽然意味着空string等于空,所以它不被授予它将适合您的需要。

在Oracle 11g中,我这样做,因为x = null技术上评估为UNKNOWN

 WHERE (x IS NULL AND ? IS NULL) OR NOT LNNVL(x = ?) 

OR之前的expression式将NULL等同于NULL,然后expression式处理所有其他可能性。 LNNVLUNKNOWN更改为TRUE ,将TRUE更改为TRUE ,将FALSE更改为TRUE ,这与我们想要的完全相反,因此NOT

在某些情况下,接受的解决scheme在甲骨文中并不适用,当时它是涉及到NOT的较大expression式的一部分。