为什么从Oracle存储过程中检索ResultSet的速度如此之慢?

时间:2010-05-27 14:55:18

标签: java oracle jdbc

我必须改进一些从Java程序调用Oracle存储过程的代码。目前代码真的很慢:在我的开发机器上最多约8秒。在同一台机器上,如果我直接调用一个执行相同处理并返回相同数据的SQL查询,则需要不到100毫秒...

代码创建一个CallableStatement,将其中一个输出参数注册为Oracle游标,然后使用该语句的getObject方法检索游标并将其解析为ResultSet:

cstmt = conn.prepareCall("{ call PKG_ESPECEW.P_ListEspece( ?, ?, ?, ?, ?, ? ) }");
cstmt.registerOutParameter(4, oracle.jdbc.OracleTypes.CURSOR);
[...]
cstmt.executeQuery();
rs = (ResultSet)cstmt.getObject(4);
rs.setFetchSize(1000); //supposed to help ?

options = new HashMap<String, String>(1000);
rs.next() //added that to measure exactly the length of the first call

while(rs.next()) {
    [...]
}

我在代码中添加了一些时间戳,以了解哪个部分花了这么长时间。结果:第一次调用rs.next()需要花费不同的时间。结果集是平均值,从10到几千行。正如我之前所说,处理来自常规PreparedStatement的类似结果集需要10-100毫秒,具体取决于大小。

代码有什么问题吗?我该如何改进?如果我没有任何其他解决方案,我会直接使用SQL,但是我更喜欢一个允许我不重写所有程序的解决方案!

以下是存储过程的定义:

PROCEDURE P_ListEspece(P_CLT_ID IN ESPECE.ESP_CLT_ID%TYPE,     -- Langue de l'utilisateur
                        P_ESP_GROUP_CODE IN ESPECE.ESP_CODE%TYPE,-- Code du groupe ou NULL
                        P_Filter IN VARCHAR2,                   -- Filtre de la requête
                        P_Cursor OUT L_CURSOR_TYPE,             -- Curseur
                        P_RecordCount OUT NUMBER,               -- Nombre d'enregistrement retourne
                        P_ReturnStatus OUT NUMBER);              -- Code d'erreur

4 个答案:

答案 0 :(得分:3)

“我认为程序已执行,然后将结果存储在oracle服务器的内存中,最后通过游标和结果集和JDBC”

传回客户端(java app)

这是不正确的。 oracle作为游标返回的内容基本上是指向查询的指针(所有绑定变量都准备就绪)。它没有在内存中实现结果集。它可能是数百万/数十亿行的庞大结果集。

所以它很可能是一个缓慢的查询,需要很长时间才能提供结果。

答案 1 :(得分:1)

显然,存储过程正在进行一些数据转换/向前和向后按摩(例如int&lt; - &gt; varchar)。众所周知,这对于大型表来说需要花费很多时间。确保您已在SP参数中声明了正确的数据类型,并在CallableStatement中设置了正确的数据类型。

答案 2 :(得分:1)

在Java之外执行程序需要多长时间?在SQL * Plus中检查这样的脚本:

var ref refcursor
var cnt number
var status number
exec p_listespece (xx, yy, zz, :ref, :cnt, :status);--replace with actual values
print :ref

如果超过10-100毫秒,则问题可能来自存储过程。

答案 3 :(得分:0)

我有同样的问题,我们通过将返回的参数从游标更改为varchar来解决(我和oracle专用人员),这是存储在内部执行的普通查询。 这是一个巨大的实现,我不知道这是否适用于您的场景。

这里是片段:

`

String sql = "call MyStored(?,?,?,?)";
CallableStatement st = Conn.prepareCall(sql);
st.setInt(1, 10);
st.setInt(2, 20);
st.setInt(3, 30);
st.registerOutParameter(4, OracleTypes.VARCHAR);

st.execute();

String query = (String) st.getObject(4);
Statement stmt = Conn.createStatement();
rs = stmt.executeQuery(query);
[...]
//work with resultset
[...]
stmt.close();
stmt = null;

`