2010-05-27 49 views
4

我必须改进一些从Java程序调用Oracle存储过程的代码。目前代码真的非常慢:在我的开发机器上高达大约8秒钟。在同一台机器上,如果我直接调用一个执行相同处理并返回相同数据的SQL查询,则需要100   ms ...为什么从Oracle存储过程中检索ResultSet非常慢?

该代码创建一个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   ms。

代码有什么问题吗?我如何改进它?如果我没有任何其他解决方案,我会直接在SQL中执行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

有没有你不使用'OracleCallableStatement的理由。getCursor'而不是'getObject'? – 2010-05-27 15:31:25

+1

我必须将CallableStatement强制转换为OracleCallableStatement,但我使用的是DBCP,而CallableStatement实际上是DBCP提供的“代理”,所以我会得到一个异常(我试过了)。 – 2010-05-27 15:47:50

+1

对于抓取,要通过网络进行优化,您应该使用setRowPrefetch。这产生了巨大的影响。虽然setFetchSize约为20-30%,但在大型数据集上预取可高达10倍。 – zhrist 2016-08-11 11:44:12

回答

3

“我认为程序执行,那么它存储在Oracle服务器的内存结果,最后通过光标回传给客户端(Java应用程序)和结果集和JDBC”

这是不正确。 Oracle作为游标返回的内容基本上是一个指向查询的指针(所有这些指针都可以使用任何绑定变量)。它没有实现内存中的结果集。它可能是一个数以百万计/十亿行的巨大结果集。

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

+0

我与处理数据库的DB人讨论过,似乎在SP内部执行的查询实际上是问题,因为它调用的是为找到的每个记录打开另一个游标的函数。他删除了这个电话,执行时间缩短到不到1秒。它仍然非常缓慢,但已经更可以接受。我想查询本身仍然可以改进,问题实际上是查询而不是数据的传输。 – 2010-05-28 07:18:24

1

显然该存储过程是做一些数据变换/按摩来回(例如int < - >varchar)。这是众所周知的需要大量的时间在大桌子的情况下。确保您已经在SP参数中声明了正确的数据类型,并在CallableStatement中设置了正确的数据类型。

+0

实际上它不是读取或设置需要时间的SP参数,而是从ResultSet中读取,它本身就是SP的一个参数。我会用SP定义更新这个问题。所以你认为在这种情况下类型转换可能是一个问题? – 2010-05-27 15:50:17

+0

你说'PreparedStatement'更快,所以问题可能发生在SP上。如果需要(隐式)转换任何列值,则需要花费时间,尤其是在SP的WHERE子句中使用时。 – BalusC 2010-05-27 15:54:13

+0

但它不是cstmt.executeQuery();这很慢,这是结果集浏览。我认为这个过程被执行了,然后它的结果存储在oracle服务器的内存中,最后通过游标,结果集和JDBC传输回客户端(java应用程序)。但也许我错了?我是Oracle/PLSQL的无知者。读取结果集时,程序是否会“一步一步”执行? – 2010-05-27 16:01:30

1

在Java之外执行程序需要多长时间?请在SQL *这样的脚本加:

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毫秒,您的问题可能来自于存储过程。

+0

其实我没有SQLPlus或安装在我的开发机器上的oracle客户端。我使用SQLDevelopper完成大部分thnigs,然后向DBA请教。我明天会和他联系。或者你知道我是否可以直接从SQLDev执行plsql? – 2010-05-27 16:40:15

+0

我安装了oracle客户端和sql plus,并试过这个。结果在命令行中的显示是永久性的,但是它可能被windows命令行中的命令行打印放慢了。无论如何,问题在于SP本身。 – 2010-05-28 07:20:17

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; 

`