8

我想发出如下查询

select max(col1), f(:1, col2) from t group by f(:1, col2)

其中:1是绑定变量。使用PreparedStatement,如果我说

connection.prepareStatement
  ("select max(col1), f(?, col2) from t group by f(?, col2)")

我从 DBMS 收到一个错误,抱怨f(?, col2)不是 GROUP BY 表达式。

通常如何在 JDBC 中解决这个问题?

4

4 回答 4

9

我建议重新编写语句,以便只有一个绑定参数。这种方法有点难看,但会返回结果集:

select max(col1) 
     , f_col2
  from (
         select col1
              , f(? ,col2) as f_col2 
           from t
       )
 group
    by f_col2

这个重写的语句只引用了一个绑定参数,所以现在 DBMS 看到 GROUP BY 子句和 SELECT 列表中的表达式是相同的。

高温高压

[编辑]

(我希望有更漂亮的方式,这就是为什么我更喜欢 Oracle 使用的命名绑定参数方法。使用 Perl DBI 驱动程序,位置参数在实际发送到 Oracle 的语句中转换为命名参数。)

起初我没有看到问题,我不明白原来的问题。(显然,其他几个人也错过了它。)但是在运行了一些测试用例之后,我明白了问题是什么,问题是什么。

让我看看我是否可以说明问题:如何让两个单独的(位置)绑定参数被(由 DBMS)处理,就好像它是对相同(命名)绑定参数的两个引用一样。

DBMS 期望 GROUP BY 中的表达式与 SELECT 列表中的表达式匹配。但是即使表达式相同,这两个表达式也被认为是不同的,唯一的区别是每个表达式引用不同的绑定变量。(我们可以演示一些至少某些 DBMS 允许的测试用例,但还有更一般的情况会引发异常。)

在这一点上,简短的回答是,这让我很难过。我的建议(可能不是对原始问题的实际答案)是重组查询。

[/编辑]

如果这种方法不起作用,或者您在解决它时遇到其他问题,我可以提供更多详细信息。或者如果存在性能问题(我可以看到优化器为重新编写的查询选择不同的计划,即使它返回指定的结果集。为了进一步测试,我们真的需要知道什么 DBMS,什么驱动程序,统计等)

编辑(八年半后)

再次尝试重写查询。同样,我想出的唯一解决方案是带有一个绑定占位符的查询。这一次,我们将它放入一个返回单行的内联视图中,并将其连接到 t。我可以看到它在做什么;我不确定 Oracle 优化器将如何看到这一点。我们可能希望(或需要)进行显式转换,例如TO_NUMBER(?) AS param, TO_DATE(?,'...') AS param, TO_CHAR(?) AS param,具体取决于绑定参数的数据类型,以及我们希望从视图返回的数据类型。)

这就是我在 MySQL 中的做法。我的答案中的原始查询在内联视图(MySQL派生表)中执行连接操作。如果可以避免,我们希望避免实现一个hughjass 派生表。再说一次,只要sql_mode不包含ONLY_FULL_GROUP_BY. MySQL 也会让我们放弃FROM DUAL)

  SELECT MAX(t.col1)
       , f( v.param ,t.col2)
    FROM t
   CROSS
    JOIN ( SELECT ? AS param FROM DUAL) v
   GROUP
      BY f( v.param ,t.col2)

根据 MadusankaD 的回答,在过去的八年里,Oracle 增加了对在 JDBC 驱动程序中重用相同命名的绑定参数的支持,并保留等效性。(我还没有测试过,但如果它现在有效,那就太好了。)

于 2009-06-02T18:38:55.677 回答
3

即使您已通过 JDBC 驱动程序(使用PreparedStatement)发出查询,如下所示:

select max(col1), f(:1, col2) from t group by f(:1, col2)

最后 JDBC 驱动程序在解析到数据库之前替换了如下查询,即使您在两个地方都使用了相同的绑定变量名称。

select max(col1), f(*:1*, col2) from t group by f(*:2*, col2)

但在 oracle 中,这不会被视为有效的 group by 子句。而且普通的 JDBC 驱动程序也不支持命名绑定变量。

为此,您可以使用OraclePreparedStatement类进行连接。这意味着它是 oracle JDBC。然后你可以使用命名绑定变量。它将解决您的问题。

从 Oracle 数据库 10g JDBC 驱动程序开始,使用这些setXXXAtName方法支持按名称绑定。

http://docs.oracle.com/cd/E24693_01/java.11203/e16548/apxref.htm#autoId20

于 2016-11-23T15:12:08.887 回答
0

您是否尝试使用?而不是命名绑定变量?另外,您使用的是哪个驱动程序?我使用瘦驱动程序尝试了这个简单的示例,它似乎工作正常:

PreparedStatement ps = con.prepareStatement("SELECT COUNT(*), TO_CHAR(SYSDATE, ?) FROM DUAL GROUP BY TO_CHAR(SYSDATE, ?)");
ps.setString(1, "YYYY");
ps.setString(2, "YYYY");
ps.executeQuery();
于 2009-06-02T18:24:51.347 回答
0

在第二种情况下,实际上有两个变量 - 您需要将它们都发送为相同的值。

于 2009-06-02T18:30:29.997 回答