如果 SQL :parameter 在 DB2 JDBC 中为“ALL”,如何匹配所有行?

Posted

技术标签:

【中文标题】如果 SQL :parameter 在 DB2 JDBC 中为“ALL”,如何匹配所有行?【英文标题】:How to match all rows if SQL :parameter is 'ALL' in DB2 JDBC? 【发布时间】:2021-10-22 01:24:58 【问题描述】:

这只是重现错误的示例代码

SELECT *
FROM ( VALUES (10,'A'),(20,'B'),(30,'C'),(40,'D') ) AS T(COL1,COL2)
WHERE T.COL2 = :PARAM OR :PARAM = 'ALL'

如果将'A' 分配给PARAM 参数,上述语句应返回第一行,如果'B' 则返回第二行,等等... 否则,如果将'ALL' 分配给PARAM,则应返回所有行。

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,并在README 中添加了构建和运行它所需的所有步骤。你也可以在https://hub.docker.com/r/noureldin/db2-sql-error找到一个预建的docker镜像

我知道有很多解决方法(例如,如果参数在 java 中被替换,如 cmets 所示,它会按预期工作)但我想了解我在这里缺少什么。


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 如果我的参数的字符长度超过它们中的任何一个。我希望这可以帮助任何面临类似错误的人。

【问题讨论】:

如果您认为这可能是一个错误,请告诉我在哪里可以报告。 错误SQL0301N在你的情况下意味着ALL对于:PARAM来说太长了,db2无法检测到:PARAM可以等于ALL根据你的查询,也许试试@987654343 @ 谢谢@nfgl,我会试试这个,如果它有效,请告诉你 嗨@nfgl,我确认您的解决方案有效,如果您没有在评论中写下它,这将是第一个被接受的答案(标记前 8 分钟)。但是,我仍然不会使用这种方法,因为我发现我不应该在 > Left-hand side operand of an IN predicate 中使用参数我不明白如果这个参数的使用受到限制,它是如何工作的,所以我将使用建议的CAST 方法@马克 【参考方案1】:

我将其称为“功能”,而不是“错误”。

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 个字节的值,则:

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'

【讨论】:

感谢您的解释。现在这很有意义。我会尝试你的建议,并在我确认它适用于我的情况后接受你的回答。 非常感谢您的帮助。我非常感谢。

以上是关于如果 SQL :parameter 在 DB2 JDBC 中为“ALL”,如何匹配所有行?的主要内容,如果未能解决你的问题,请参考以下文章

如果我们传递空值,Sql 查询 ISNULL(@parameter,ColumnName) 返回啥?

如何在 SQL/DB2 中使用数据透视?

从 db2 sql 服务中清除数据的最佳实践

【DB2】SQL优化

在 DB2 sql 中获取最接近的匹配值

DB2中如果转换数值显示为两位小数,并显示千分位逗号要怎么写SQL?