声明是
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
)。
一种解决方案是
SELECT * FROM tableA WHERE x = ? OR (x IS NULL AND ? IS NULL)
但是我必须设置相同的参数两次。有更好的解决办法吗?
谢谢!
我一直按照您在问题中显示的方式这样做。 设置相同的参数两次并不是那么困难,不是吗?
SELECT * FROM tableA WHERE x = ? OR (x IS NULL AND ? IS NULL);
有一个相当未知的 ANSI-SQL 运算符
IS DISTINCT FROM
可以处理 NULL 值。可以这样使用:
SELECT * FROM tableA WHERE x IS NOT DISTINCT FROM ?
因此只需设置一个参数。不幸的是,MS SQL Server (2008) 不支持此功能。
另一种解决方案可能是,如果存在一个永远不会使用的值('XXX'):
SELECT * FROM tableA WHERE COALESCE(x, 'XXX') = COALESCE(?, 'XXX')
只会使用两种不同的语句:
声明1:
SELECT * FROM tableA WHERE x is NULL
声明2:
SELECT * FROM tableA WHERE x = ?
您可以检查变量并根据条件构建正确的语句。我认为这使得代码更清晰、更容易理解。
编辑 顺便问一下,为什么不使用存储过程呢?然后你可以在 SP 中处理所有这些 NULL 逻辑,并且可以简化前端调用的事情。
如果您使用例如 mysql,您可能可以执行以下操作:
select * from mytable where ifnull(mycolumn,'') = ?;
那么你可以这样做:
stmt.setString(1, foo == null ? "" : foo);
您必须检查您的解释计划,看看它是否可以提高您的表现。但这意味着空字符串等于 null,因此不能满足您的需求。
在 Oracle 11g 中,我这样做是因为
x = null
在技术上评估为 UNKNOWN
:
WHERE (x IS NULL AND ? IS NULL)
OR NOT LNNVL(x = ?)
OR
之前的表达式负责将 NULL 与 NULL 等同,然后之后的表达式负责所有其他可能性。 LNNVL
将 UNKNOWN
更改为 TRUE
,将 TRUE
更改为 FALSE
,将 FALSE
更改为 TRUE
,这与我们想要的完全相反,因此是 NOT
。
在某些情况下,接受的解决方案在 Oracle 中对我不起作用,因为它是更大表达式的一部分,涉及
NOT
。