我想发出如下所示的查询
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中解决这个问题?
答案 0 :(得分:8)
我建议重新编写语句,以便只有一个绑定参数。 这种方法有点难看,但会返回结果集:
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允许的测试用例,但是有一些更常见的情况会引发异常。)
此时简短的回答是,这让我很难过。我的建议(可能不是对原始问题的实际答案)是重构查询。
[/编辑]
如果这种方法不起作用,或者如果您还有其他问题需要解决,我可以提供更多详细信息。或者如果性能有问题(我可以看到优化器为重写的查询选择不同的计划,即使它返回指定的结果集。为了进一步测试,我们真的需要知道什么是DBMS,什么驱动程序,统计等。)
编辑(八年半之后)
另一次尝试重写查询。同样,我提出的唯一解决方案是使用一个绑定占位符的查询。这一次,我们将其粘贴到一个返回单行的内联视图中,并将其连接到t。我可以看到它在做什么;我不确定Oracle优化器将如何看待它。我们可能希望(或需要)进行明确的转换,例如TO_NUMBER(?) AS param
,TO_DATE(?,'...') AS param
,TO_CHAR(?) AS param
,具体取决于bind参数的数据类型,以及我们希望从视图中返回的数据类型。)
这就是我在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驱动程序中重用相同命名绑定参数并保留等效性的支持。 (我没有测试过,但如果现在有效,那就很好。)
答案 1 :(得分:1)
即使您已通过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
答案 2 :(得分: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();
答案 3 :(得分:0)
在第二种情况下,实际上有两个变量 - 您需要使用相同的值发送它们。