如果没有条件(动态查询)或乱码值,Where子句中的PreparedStatement“为null”

时间:2014-07-24 12:07:12

标签: java sql oracle

假设我有查询:

SELECT * FROM CUSTOMERS WHERE CUSTOMER_ID = ?

使用PreparedStatement,我可以绑定变量:

pstmt.setString(1, custID);

但是,我无法通过以下绑定获得正确的结果:

pstmt.setString(1, null);

这导致:

SELECT * FROM CUSTOMERS WHERE CUSTOMER_ID = NULL

没有给出任何结果。正确的查询应该是:

SELECT * FROM CUSTOMERS WHERE CUSTOMER_ID IS NULL

通常的解决方案是:

解决方案1 ​​

动态生成查询:

String sql = "SELECT * FROM CUSTOMERS WHERE CUSTOMER_ID "
                + (custID==null ? "IS NULL" : "= ?");
if (custID!=null)
    pstmt.setString(1, custID);

解决方案2

使用NVL将空值转换为乱码值:

SELECT * FROM CUSTOMERS WHERE NVL(CUSTOMER_ID, 'GIBBERISH') = NVL(?, 'GIBBERISH');

但你需要100%确定价值' GIBBERISH'永远不会存储。

问题

有没有办法使用静态查询避免依赖于乱码值转换?我正在寻找类似的东西:

SELECT * FROM CUSTOMERS
    WHERE /** IF ? IS NULL THEN CUSTOMER_ID IS NULL ELSE CUSTOMER_ID = ? **/

我想我可能有一个有效的解决方案:

SELECT * FROM CUSTOMERS
    WHERE ((? IS NULL AND CUSTOMER_ID IS NULL) OR CUSTOMER_ID = ?)

pstmt.setString(1, custID);
pstmt.setString(2, custID);

上述工作能否可靠?是否有更好的方法(可能只需要设置参数一次)?或者根本没有办法可靠地做到这一点?

3 个答案:

答案 0 :(得分:2)

您的工作解决方案很好(与I've used before类似)。如果您只想在使用CTE或内联视图为实际查询提供值后进行绑定:

WITH CTE AS (
    SELECT ? AS REAL_VALUE FROM DUAL
)
SELECT C.*   -- but not * really, list all the columns
FROM CTE
JOIN CUSTOMERS C
ON (CTE.REAL_VALUE IS NULL AND C.CUSTOMER_ID IS NULL)
    OR C.CUSTOMER_ID = CTE.REAL_VALUE

因此只有一个占位符可以绑定。

我确实没有看到Java端的分支有问题,除非你的实际查询更加复杂并且会导致重复的重复。

答案 1 :(得分:1)

WHERE 
  DECODE(CUSTOMER_ID, NULL, 'NULL', CUSTOMER_ID || 'NOT NULL') =
  DECODE(?, NULL, 'NULL', CUSTOMER_ID, CUSTOMER_ID || 'NOT NULL')

这相信,我相信

SQLFiddle

请注意,为了在sqlfiddle上测试它,我必须用每个案例的值替换参数[NULL,' NULL',' SMITH']

答案 2 :(得分:0)

动态创建查询适用于所有JDBC,因此您不受特定于平台的SQL的约束 创建一个if - 分支是不是很难阅读,不是吗?

PreparedStatement pst = null; // Avoid initialisation warnings
if (custID == null)
    pst = connection.prepareStatement("SELECT * FROM CUSTOMERS WHERE CUSTOMER_ID IS NULL");
else {
    pst = connection.prepareStatement("SELECT * FROM CUSTOMERS WHERE CUSTOMER_ID = ?");
    pst.setString(1, custID);
}
ResultSet rs = pst.executeQuery();