admin

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

sql

假设我有查询:

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);

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


阅读 250

收藏
2021-06-07

共1个答案

admin

您的工作解决方案很好(并且与我之前使用的类似)。如果只想绑定一次,则可以使用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分支的问题,除非您的实际查询复杂得多,并且会导致大量重复。

2021-06-07