是否可以将 GROUP BY 与绑定变量一起使用?

问题描述 投票:0回答:4

我想发出如下查询

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 中解决这个问题?

java sql oracle-database jdbc prepared-statement
4个回答
10
投票

我们可以尝试重写该语句,以便只有一个绑定参数。 这种方法有点丑陋。但返回结果集:

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

这个重写的语句仅引用单个绑定参数,因此现在 DBMS 看到 GROUP BY 子句和 SELECT 列表中的表达式是相同的。

HTH

[编辑]

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

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

让我看看我是否可以陈述问题:如何(由 DBMS)将两个单独的(位置)绑定参数视为对同一(命名)绑定参数的两个引用。

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

在这一点上,简短的答案是,这让我感到困惑。 我的建议(可能不是原始问题的实际答案)是重构查询。

[/编辑]

如果这种方法不起作用,我们可以更深入地研究细节,我们可以找出更深层次的问题。像这样的“聪明”SQL 特别值得关注的是(一如既往)性能权衡。 (我们可以看到优化器为重写的查询选择不同的计划,即使它返回指定的结果集。为了进一步测试,我们确实需要知道什么 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 可能就会让原始查询滑动。 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驱动程序中重用相同命名的绑定参数并保持等效性的支持。 (我还没有测试过,但如果现在有效,那就太好了。)


4
投票

即使您已通过 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 Database 10g JDBC 驱动程序开始,支持使用

setXXXAtName
方法按名称绑定。

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


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

0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.