Oracle存储过程,返回ref cursor与关联数组

时间:2012-03-09 19:09:55

标签: arrays oracle plsql oracle11g

我们的DBA要求我们从一组关联数组中返回存储过程中的所有表格数据,而不是使用我在Web上的大多数示例中看到的引用游标。他说这是因为Oracle以这种方式做事要快得多,但这对我来说似乎很直观,因为数据需要循环两次,一次在存储过程中,然后再在应用程序中进行处理。此外,值通常需要从其本机类型转换为varchar,以便它们可以存储在数组中,然后在应用程序端进行转换。使用这种方法也很难使用orm工具,因为在大多数情况下它们似乎都需要ref游标。

存储过程的示例如下:

PROCEDURE sample_procedure (
                                p_One       OUT varchar_array_type,
                                p_Two       OUT varchar_array_type,
                                p_Three     OUT varchar_array_type,
                                p_Four      OUT varchar_array_type
                            )
IS
p_title_procedure_name        VARCHAR2(100) := 'sample_procedure';
v_start_time DATE :=SYSDATE;    

CURSOR cur
  IS
    SELECT e.one, e.two, e.three, e.four FROM package.table 
    WHERE filter='something';

    v_counter PLS_INTEGER := 0;
BEGIN

    FOR rec IN cur LOOP
        BEGIN
            v_counter := v_counter + 1;
            p_One(v_counter) := rec.one;
            p_Two(v_counter) := rec.two;
            p_Three(v_counter) := rec.three;
            p_Four(v_counter) := rec.four;
        END;
    END LOOP;
END;

游标用于为返回的每列填充一个数组。我试图找到支持他声称这种方法更快但却无法做到这一点的信息。任何人都可以告诉我为什么他可能希望我们(.net开发人员)以这种方式编写存储过程?

2 个答案:

答案 0 :(得分:12)

DBA的请求没有意义。

DBA几乎肯定会想到的是,他希望最大程度地减少从游标中获取数据时发生的SQL到PL / SQL引擎上下文转换的次数。但是,正在提出的解决方案很难针对这一特定问题,并在大多数系统中引入了其他更为严重的性能问题。

在Oracle中,当PL / SQL VM向SQL VM请求更多数据时,会发生SQL到PL / SQL上下文转换,SQL VM会通过进一步执行语句来响应,然后获取数据然后打包并返回到PL / SQL VM。如果PL / SQL引擎一次请求一行并且您要获取大量行,则这些上下文移位可能是整个运行时的重要部分。为了解决这个问题,Oracle至少在8天内引入了批量操作的概念。这允许PL / SQL VM一次从SQL VM请求多行。如果PL / SQL VM一次请求100行,那么您已经消除了99%的上下文转换,并且您的代码可能运行得更快。

一旦引入批量操作,就会有很多代码可以重构,以便通过显式使用BULK COLLECT操作而不是逐行获取然后使用FORALL来提高效率循环来处理这些集合中的数据。然而,到10.2天,Oracle已将批量操作集成到隐式FOR循环中,因此隐式FOR循环现在自动批量收集100个批次而不是逐行获取。

但是,在您的情况下,由于您将数据返回到客户端应用程序,因此使用批量操作的重要性要小得多。任何体面的客户端API都将具有允许客户端指定每个网络往返中需要从游标中获取多少行的功能,并且这些获取请求将直接转到SQL VM,而不是通过PL / SQL VM,因此没有SQL到PL / SQL上下文转移担心。您的应用程序必须担心在每次往返中获取适当数量的行 - 足以使应用程序不会在网络上变得过于繁琐和瓶颈但不会太多以至于您不得不等待太长时间才能使结果成为返回或将太多数据存储在内存中。

将PL / SQL集合而不是REF CURSOR返回给客户端应用程序不会减少发生的上下文转换次数。但它会有一堆其他缺点,其中最重要的是内存使用情况。 PL / SQL集合必须完全存储在数据库服务器上的进程全局区域(PGA)(假设专用服务器连接)中。这是必须从服务器的RAM分配的一块内存。这意味着服务器将不得不分配内存来获取每个客户端请求的最后一行。反过来,这将极大地限制应用程序的可伸缩性,并且根据数据库配置,可能会从Oracle数据库的其他部分窃取RAM,这对提高应用程序性能非常有用。如果你的PGA空间不足,你的会话将开始出现与内存相关的错误。即使在纯粹基于PL / SQL的应用程序中,您也不希望将所有数据提取到集合中,您总是希望以较小的批次获取它,以便最大限度地减少您正在使用的PGA数量。

此外,将所有数据提取到内存中会使应用程序感觉慢得多。几乎任何框架都允许您根据需要获取数据,例如,如果您有一个报告显示每个25行的页面,您的应用程序只需要在绘制之前获取前25行第一个屏幕。除非用户碰巧请求下一页结果,否则它永远不会获取接下来的25行。但是,如果您像DBA建议的那样将数据提取到数组中,那么在应用程序开始显示第一行之前,您将不得不获取所有行,即使用户从不希望看到超过第一行的数据。行。这意味着要在数据库服务器上获取更多I / O以获取所有行,服务器上有更多PGA,应用程序服务器上有更多RAM来缓冲结果,以及更长时间等待网络。

答案 1 :(得分:0)

我相信Oracle会在扫描数据库时开始从这样的系统发送结果,而不是全部检索它们然后再发送回来。这意味着结果会在找到时发送,从而加速系统运行。 (实际上,如果我没记错的话,它会将批量结果返回到循环中。)这主要是来自某些培训的记忆

然而,真正的问题是,为什么不直接问他他的推理。他可能指的是甲骨文可以利用的一个技巧,如果你了解具体细节,你可以利用速度技巧来发挥它的全部潜力。一般来说,“总是做到这一点,因为这更快”是最可疑的,并且值得仔细研究以充分理解他们的意图。在某些情况下,这可能确实不适用(例如小的查询结果),其中所有可读性问题和开销都无助于提高性能。

也就是说,可以保持代码的一致性和更快速识别。关于他的推理的沟通是最重要的工具,有这样的担忧,因为他知道一个他不完全清楚的商业秘密的可能性很大。