假设我有查询:
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
通常的解决方案是:
动态生成查询:
String sql = "SELECT * FROM CUSTOMERS WHERE CUSTOMER_ID "
+ (custID==null ? "IS NULL" : "= ?");
if (custID!=null)
pstmt.setString(1, custID);
使用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);
上述工作能否可靠?是否有更好的方法(可能只需要设置参数一次)?或者根本没有办法可靠地做到这一点?
答案 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上测试它,我必须用每个案例的值替换参数[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();