我必须改进一些从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
答案 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;
`