在 Oracle 上的 INSERT ... SELECT 之后获取插入的 ID

Posted

技术标签:

【中文标题】在 Oracle 上的 INSERT ... SELECT 之后获取插入的 ID【英文标题】:Getting inserted ID after INSERT ... SELECT on Oracle 【发布时间】:2011-03-16 17:35:05 【问题描述】:

如果我从我的 Oracle 客户端 (SQL Developer) 运行此 SQL 语句,它就会起作用:

insert into Person (Name) select 'Bob' from dual

如果我通过 Spring JDBC 不使用 KeyHolder 发出它,它也可以工作:

final PreparedStatementCreator psc = new PreparedStatementCreator() 

    @Override
    public PreparedStatement createPreparedStatement(Connection con)
        throws SQLException
    
        return con.prepareStatement(
                "insert into Person (Name) select 'Bob' from dual");
    
;
jdbcOperations.update(psc);

但是我需要使用 KeyHolder 来获取新插入行的 ID。如果我将上述代码更改为使用 KeyHolder,如下所示:

final KeyHolder keyHolder = new GeneratedKeyHolder();
final PreparedStatementCreator psc = new PreparedStatementCreator() 

    @Override
    public PreparedStatement createPreparedStatement(Connection con)
        throws SQLException
    
        return con.prepareStatement(
            "insert into Person (Name) select 'Bob' from dual",
            new String[] "PersonID");
    
;
jdbcOperations.update(psc, keyHolder);

...然后我收到此错误:

Exception in thread "main" org.springframework.jdbc.BadSqlGrammarException: PreparedStatementCallback; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: ORA-00933: SQL command not properly ended
    at org.springframework.jdbc.support.SQLExceptionSubclassTranslator.doTranslate(SQLExceptionSubclassTranslator.java:94)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:72)
    at org.springframework.jdbc.support.AbstractFallbackSQLExceptionTranslator.translate(AbstractFallbackSQLExceptionTranslator.java:80)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:602)
    at org.springframework.jdbc.core.JdbcTemplate.update(JdbcTemplate.java:842)
    at au.com.bisinfo.codecombo.logic.ImportServiceImpl.insertLoginRedirectRule(ImportServiceImpl.java:107)
    at au.com.bisinfo.codecombo.logic.ImportServiceImpl.runImport(ImportServiceImpl.java:68)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:309)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:110)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
    at $Proxy8.runImport(Unknown Source)
    at au.com.bisinfo.codecombo.ui.Main.main(Main.java:39)
Caused by: java.sql.SQLSyntaxErrorException: ORA-00933: SQL command not properly ended
    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:439)
    at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:395)
    at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:802)
    at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:436)
    at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:186)
    at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:521)
    at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:205)
    at oracle.jdbc.driver.T4CPreparedStatement.executeForRows(T4CPreparedStatement.java:1008)
    at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1307)
    at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3449)
    at oracle.jdbc.driver.OraclePreparedStatement.executeUpdate(OraclePreparedStatement.java:3530)
    at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeUpdate(OraclePreparedStatementWrapper.java:1350)
    at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
    at org.apache.commons.dbcp.DelegatingPreparedStatement.executeUpdate(DelegatingPreparedStatement.java:105)
    at org.springframework.jdbc.core.JdbcTemplate$3.doInPreparedStatement(JdbcTemplate.java:844)
    at org.springframework.jdbc.core.JdbcTemplate$3.doInPreparedStatement(JdbcTemplate.java:1)
    at org.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:586)
    ... 15 more

FWIW,如果我执行 INSERT ... VALUES 而不是 INSERT ... SELECT,一切都很好(尽管这对我没有帮助,因为我需要选择一些东西):

final KeyHolder keyHolder = new GeneratedKeyHolder();
final PreparedStatementCreator psc = new PreparedStatementCreator() 

    @Override
    public PreparedStatement createPreparedStatement(Connection con)
        throws SQLException
    
        return con.prepareStatement(
            "insert into Person (Name) values ('Bob')",
            new String[] "PersonID");
    
;
jdbcOperations.update(psc, keyHolder);

我正在使用:

Spring JDBC 3.0.3.RELEASE JDBC 驱动程序:ojdbc6.jar 版本 11.2.0.1.0 RDBMS:Oracle9i 版本 9.2.0.5.0 - 生产 commons-dbcp 1.4

注意我的应用需要使用标准 SQL 以保持 db-neutral,这排除了任何特定于 Oracle 的 SQL(在现实生活中我不会从“双重”中选择)。

感谢您的帮助。

【问题讨论】:

【参考方案1】:

java.sql.Connection.prepareStatement(java.lang.String, int)界面清晰

创建一个默认的 PreparedStatement 对象,该对象能够检索自动生成的键

所以你使用了错误的方法。试试

return con.prepareStatement(
        "insert into Person (Name) select 'Bob' from dual",
        Statement.RETURN_GENERATED_KEYS);

改为

【讨论】:

该方法是可以返回生成键的三个方法之一,另外两个是 prepareStatement(String, int[]) 和 prepareStatement(String, String[])。我使用的是后者,它是适合 Oracle 的 IME。不过,我会尝试你的建议,然后告诉你进展如何。 @Andrew Swan 你是对的。谢谢!但是让我知道输出【参考方案2】:

怎么样

INSERT INTO blah b (blah1, blah2, blah3)
VALUES (?, ?, ?) RETURNING b.id INTO ?";

【讨论】:

我不能使用 INSERT ... VALUES 因为我需要选择东西。我也不想使用 RETURNING .. INTO 因为 AFAIK 它是 Oracle 特有的。【参考方案3】:

Oracle JDBC 驱动程序不支持此功能

【讨论】:

这就是我的猜测;你有这方面的参考吗?【参考方案4】:

我怀疑使用带有 INSERT SELECT 语句的 KeyHolder 不受支持,因为 select 理论上可以选择多行,如果这样做,则无法将这些多个键返回到单个 KeyHolder 中。对于您要完成的任务,简单地使用 select 语句后跟 insert 语句可能会更容易。

【讨论】:

以上是关于在 Oracle 上的 INSERT ... SELECT 之后获取插入的 ID的主要内容,如果未能解决你的问题,请参考以下文章

oracle 大量数据insert操作怎么提高效率

insert 数据走索引问题

Oracle 11g 代码上的 PL/SQL 是在执行存储过程时

Oracle基础之Merge into

HIVE:INSERT 上的列引用无效。在选择中工作,而不是与插入结合使用

Oracle Merge语句