如何处理(也许)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式处理所有其他可能性。 LNNVL
将UNKNOWN
更改为TRUE
,将TRUE
更改为TRUE
,将FALSE
更改为TRUE
,这与我们想要的完全相反,因此NOT
。
在某些情况下,接受的解决scheme在甲骨文中并不适用,当时它是涉及到NOT
的较大expression式的一部分。