代码之家  ›  专栏  ›  技术社区  ›  Apocalisp

是否可以将group by与bind变量一起使用?

  •  5
  • Apocalisp  · 技术社区  · 15 年前

    我想发出如下查询

    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)")
    

    我从数据库管理系统得到一个错误,抱怨 f(?, col2) 不是分组依据表达式。

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

    4 回复  |  直到 7 年前
        1
  •  8
  •   spencer7593    7 年前

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

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

    这个重新编写的语句只引用了一个bind参数,因此DBMS现在看到group by子句中的表达式,而select列表是相同的。

    高温高压

    [编辑]

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

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

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

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

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

    [/编辑]

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

    编辑 (八年半之后)

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

    这就是我在MySQL中的做法。我的答案中的原始查询在内联视图(mysql)中执行join操作 导出表 )如果我们可以避免,我们希望避免实现Hughjas派生的表。然后,MySQL可能会让原始查询滑动,只要 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驱动程序中重用相同的命名绑定参数的支持,并保留了等效性。(我还没有测试过,但如果现在有效,那就太好了。)

        2
  •  1
  •   MadusankaD    8 年前

    即使您已经通过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 类为您连接。这意味着它是OracleJDBC。然后可以使用命名绑定变量。它会解决你的问题。

    从Oracle数据库10g JDBC驱动程序开始,使用 setXXXAtName 方法。

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

        3
  •  0
  •   Adam Paynter    15 年前

    你试过用吗 ? 而不是指定的绑定变量?还有,你用的是哪个司机?我用瘦驱动程序尝试了这个小例子,它似乎工作得很好:

    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();
    
        4
  •  0
  •   Cade Roux    15 年前

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