如果 SQL :parameter is 'ALL' in DB2 JDBC 如何匹配所有行?
How to match all rows if SQL :parameter is 'ALL' in DB2 JDBC?
这只是重现错误的示例代码
SELECT *
FROM ( VALUES (10,'A'),(20,'B'),(30,'C'),(40,'D') ) AS T(COL1,COL2)
WHERE T.COL2 = :PARAM OR :PARAM = 'ALL'
上面的语句应该 return 第一行如果 'A'
分配给 PARAM
参数,第二行如果 'B'
,等等...
否则,如果将 'ALL'
分配给 PARAM
,则所有行都应 returned。
String PARAM = "ALL";
// SQL = SQL.replaceAll(":PARAM", "'" + PARAM + "'"); // Uncomment me
try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
DB2PreparedStatement statement = (DB2PreparedStatement) connection.prepareStatement(SQL)) {
print(connection.getMetaData());
statement.setJccStringAtName("PARAM", PARAM); // Comment me
try (ResultSet resultSet = statement.executeQuery()) {
print(resultSet);
}
} catch (SQLException exception) {
print(exception);
}
令人惊讶的是,它不起作用。
这是应用程序输出:
Database Product Name: DB2/LINUXX8664
Database Product Version: SQL110551
Database Version: 11.5
Driver Name: IBM Data Server Driver for JDBC and SQLJ
Driver Version: 4.29.24
JDBC Version: 4.1
SQLException information:
Error msg: DB2 SQL Error: SQLCODE=-302, SQLSTATE=22001, SQLERRMC=null, DRIVER=4.29.24
SQLSTATE: 22001
Error code: -302
com.ibm.db2.jcc.am.SqlDataException: DB2 SQL Error: SQLCODE=-302, SQLSTATE=22001, SQLERRMC=null, DRIVER=4.29.24
at com.ibm.db2.jcc.am.b7.a(b7.java:802)
at com.ibm.db2.jcc.am.b7.a(b7.java:66)
at com.ibm.db2.jcc.am.b7.a(b7.java:140)
at com.ibm.db2.jcc.am.k9.c(k9.java:2844)
at com.ibm.db2.jcc.am.k9.a(k9.java:2281)
at com.ibm.db2.jcc.t4.ab.r(ab.java:1670)
at com.ibm.db2.jcc.t4.ab.l(ab.java:754)
at com.ibm.db2.jcc.t4.ab.d(ab.java:110)
at com.ibm.db2.jcc.t4.p.c(p.java:44)
at com.ibm.db2.jcc.t4.av.j(av.java:162)
at com.ibm.db2.jcc.am.k9.an(k9.java:2276)
at com.ibm.db2.jcc.am.k_.a(k_.java:4699)
at com.ibm.db2.jcc.am.k_.b(k_.java:4215)
at com.ibm.db2.jcc.am.k_.a(k_.java:4860)
at com.ibm.db2.jcc.am.k_.b(k_.java:4215)
at com.ibm.db2.jcc.am.k_.bd(k_.java:785)
at com.ibm.db2.jcc.am.k_.executeQuery(k_.java:750)
at com.ibm.db2.jcc.am.d0.executeQuery(d0.java:297)
at com.example.App.main(App.java:38)
The JDBC trace file is uploaded here
我将完整的示例项目代码推送到 https://github.com/noureldin-eg/db2-sql-error and added all required steps to build and run it in the README. You can also find a pre-built docker image on https://hub.docker.com/r/noureldin/db2-sql-error
我知道有很多解决方法(例如,如果在 java 中替换参数,它会按预期工作,如评论中所示),但我想了解我在这里遗漏了什么。
更新于 2021-10-22 1:30 下午(UTC)
我发现 ParameterMetaData API 在调试这个问题时非常有用。
int parameterCount = parameterMetaData.getParameterCount();
System.out.println("Number of statement parameters: " + parameterCount);
for (int i = 1; i <= parameterCount; i++) {
String sqlType = parameterMetaData.getParameterTypeName(i);
int precision = parameterMetaData.getPrecision(i);
System.out.printf("SQL type of parameter %d is %s(%d)%n", i, sqlType, precision);
}
以上代码显示我的命名参数在后台被转换为参数标记样式的 2 个问号 (?)。我已经从跟踪文件中注意到这一点,但现在很明显每个文件都有自己的类型和长度。
Number of statement parameters: 2
SQL type of parameter 1 is VARCHAR(1)
SQL type of parameter 2 is VARCHAR(3)
这就是为什么如果我的参数的字符长度超过其中任何一个,我会得到 SqlDataException
的原因。我希望这可以帮助任何面临类似错误的人。
我将其称为“功能”,而不是“错误”。
CALL ADMIN_CMD ('DESCRIBE SELECT * FROM ( VALUES (10,''A''),(20,''B''),(30,''C''),(40,''D'') ) AS T(COL1,COL2)');
SQLTYPE_ID
SQLTYPE
SQLLENGTH
SQLSCALE
SQLNAME_DATA
SQLNAME_LENGTH
SQLDATATYPE_NAME_DATA
SQLDATATYPE_NAME_LENGTH
496
INTEGER
4
0
COL1
4
0
448
VARCHAR
1
0
COL2
4
0
COL2
具有 VARCHAR(1)
数据类型。因此,当您使用 WHERE T.COL2 = :PARAM
时,参数的数据类型和长度在编译时是未知的,并且 Db2 尝试从上下文中派生它 - 它假定其数据类型和长度等于数据T2.COL2
== VARCHAR(1)
的类型和长度。但是您提供了数据类型为 VARCHAR(3)
的参数值 'ALL',并在 OPEN 调用中获得 SQLCODE=-302。
:PARAM = 'ALL'
也是如此。如果您提供长度 <=3 的实际参数值,它会起作用,否则会失败(例如,在 'ALL1' 值上)。
解决方法是明确指定所有参数标记的数据类型和长度。例如,如果您要提供长度不超过 3 个字节的值,则:
WHERE T.COL2 = CAST (:PARAM AS VARCHAR(3)) OR :PARAM = 'ALL'
如果不是,那么您应该使用以下内容,其中 x
是您要提供的参数值的最大长度:
WHERE T.COL2 = CAST (:PARAM AS VARCHAR(x)) OR CAST (:PARAM AS VARCHAR(x)) = 'ALL'
这只是重现错误的示例代码
SELECT *
FROM ( VALUES (10,'A'),(20,'B'),(30,'C'),(40,'D') ) AS T(COL1,COL2)
WHERE T.COL2 = :PARAM OR :PARAM = 'ALL'
上面的语句应该 return 第一行如果 'A'
分配给 PARAM
参数,第二行如果 'B'
,等等...
否则,如果将 'ALL'
分配给 PARAM
,则所有行都应 returned。
String PARAM = "ALL";
// SQL = SQL.replaceAll(":PARAM", "'" + PARAM + "'"); // Uncomment me
try (Connection connection = DriverManager.getConnection(URL, USER, PASSWORD);
DB2PreparedStatement statement = (DB2PreparedStatement) connection.prepareStatement(SQL)) {
print(connection.getMetaData());
statement.setJccStringAtName("PARAM", PARAM); // Comment me
try (ResultSet resultSet = statement.executeQuery()) {
print(resultSet);
}
} catch (SQLException exception) {
print(exception);
}
令人惊讶的是,它不起作用。
这是应用程序输出:
Database Product Name: DB2/LINUXX8664
Database Product Version: SQL110551
Database Version: 11.5
Driver Name: IBM Data Server Driver for JDBC and SQLJ
Driver Version: 4.29.24
JDBC Version: 4.1
SQLException information:
Error msg: DB2 SQL Error: SQLCODE=-302, SQLSTATE=22001, SQLERRMC=null, DRIVER=4.29.24
SQLSTATE: 22001
Error code: -302
com.ibm.db2.jcc.am.SqlDataException: DB2 SQL Error: SQLCODE=-302, SQLSTATE=22001, SQLERRMC=null, DRIVER=4.29.24
at com.ibm.db2.jcc.am.b7.a(b7.java:802)
at com.ibm.db2.jcc.am.b7.a(b7.java:66)
at com.ibm.db2.jcc.am.b7.a(b7.java:140)
at com.ibm.db2.jcc.am.k9.c(k9.java:2844)
at com.ibm.db2.jcc.am.k9.a(k9.java:2281)
at com.ibm.db2.jcc.t4.ab.r(ab.java:1670)
at com.ibm.db2.jcc.t4.ab.l(ab.java:754)
at com.ibm.db2.jcc.t4.ab.d(ab.java:110)
at com.ibm.db2.jcc.t4.p.c(p.java:44)
at com.ibm.db2.jcc.t4.av.j(av.java:162)
at com.ibm.db2.jcc.am.k9.an(k9.java:2276)
at com.ibm.db2.jcc.am.k_.a(k_.java:4699)
at com.ibm.db2.jcc.am.k_.b(k_.java:4215)
at com.ibm.db2.jcc.am.k_.a(k_.java:4860)
at com.ibm.db2.jcc.am.k_.b(k_.java:4215)
at com.ibm.db2.jcc.am.k_.bd(k_.java:785)
at com.ibm.db2.jcc.am.k_.executeQuery(k_.java:750)
at com.ibm.db2.jcc.am.d0.executeQuery(d0.java:297)
at com.example.App.main(App.java:38)
The JDBC trace file is uploaded here
我将完整的示例项目代码推送到 https://github.com/noureldin-eg/db2-sql-error and added all required steps to build and run it in the README. You can also find a pre-built docker image on https://hub.docker.com/r/noureldin/db2-sql-error
我知道有很多解决方法(例如,如果在 java 中替换参数,它会按预期工作,如评论中所示),但我想了解我在这里遗漏了什么。
更新于 2021-10-22 1:30 下午(UTC)
我发现 ParameterMetaData API 在调试这个问题时非常有用。
int parameterCount = parameterMetaData.getParameterCount();
System.out.println("Number of statement parameters: " + parameterCount);
for (int i = 1; i <= parameterCount; i++) {
String sqlType = parameterMetaData.getParameterTypeName(i);
int precision = parameterMetaData.getPrecision(i);
System.out.printf("SQL type of parameter %d is %s(%d)%n", i, sqlType, precision);
}
以上代码显示我的命名参数在后台被转换为参数标记样式的 2 个问号 (?)。我已经从跟踪文件中注意到这一点,但现在很明显每个文件都有自己的类型和长度。
Number of statement parameters: 2
SQL type of parameter 1 is VARCHAR(1)
SQL type of parameter 2 is VARCHAR(3)
这就是为什么如果我的参数的字符长度超过其中任何一个,我会得到 SqlDataException
的原因。我希望这可以帮助任何面临类似错误的人。
我将其称为“功能”,而不是“错误”。
CALL ADMIN_CMD ('DESCRIBE SELECT * FROM ( VALUES (10,''A''),(20,''B''),(30,''C''),(40,''D'') ) AS T(COL1,COL2)');
SQLTYPE_ID | SQLTYPE | SQLLENGTH | SQLSCALE | SQLNAME_DATA | SQLNAME_LENGTH | SQLDATATYPE_NAME_DATA | SQLDATATYPE_NAME_LENGTH |
---|---|---|---|---|---|---|---|
496 | INTEGER | 4 | 0 | COL1 | 4 | 0 | |
448 | VARCHAR | 1 | 0 | COL2 | 4 | 0 |
COL2
具有 VARCHAR(1)
数据类型。因此,当您使用 WHERE T.COL2 = :PARAM
时,参数的数据类型和长度在编译时是未知的,并且 Db2 尝试从上下文中派生它 - 它假定其数据类型和长度等于数据T2.COL2
== VARCHAR(1)
的类型和长度。但是您提供了数据类型为 VARCHAR(3)
的参数值 'ALL',并在 OPEN 调用中获得 SQLCODE=-302。
:PARAM = 'ALL'
也是如此。如果您提供长度 <=3 的实际参数值,它会起作用,否则会失败(例如,在 'ALL1' 值上)。
解决方法是明确指定所有参数标记的数据类型和长度。例如,如果您要提供长度不超过 3 个字节的值,则:
WHERE T.COL2 = CAST (:PARAM AS VARCHAR(3)) OR :PARAM = 'ALL'
如果不是,那么您应该使用以下内容,其中 x
是您要提供的参数值的最大长度:
WHERE T.COL2 = CAST (:PARAM AS VARCHAR(x)) OR CAST (:PARAM AS VARCHAR(x)) = 'ALL'