使用导致多个查询运行的函数返回多个值

时间:2018-02-22 13:00:41

标签: sql oracle plsql oracle11g

我们有售货亭供客户查看两种不同类别商品的购买量。他们将输入他们的手机号码,它会将OTP发送到他们的手机号码,他们会将其输入以进行身份​​验证,系统必须检查数据并显示它们。作为开发人员,自助服务终端供应商为我们提供了一个有限的功能开发工具包,通过该工具包,我们可以在数据库上执行select语句并在自助服务终端上显示返回的值。

我创建了一个对象类型,如下所示:

CREATE OR REPLACE TYPE rebate_values
AS
   OBJECT (ASales_total number,
           ACurrent_Rebate_Percent number,
           ANeeded_Sales number,
           ANext_Rebate_Percent number,
           BSales_total number,
           BCurrent_Rebate_Percent number,
           BNeeded_Sales number,
           BNext_Rebate_Percent number);

我将通过客户的功能'移动获取他们的销售和折扣信息:

CREATE OR REPLACE FUNCTION AA_rebate_function (P_phone IN NUMBER)
   RETURN rebate_values
IS
   A_P_Sales_total              NUMBER;
   A_P_Current_Rebate_Percent   NUMBER;
   A_P_Needed_Sales             NUMBER;
   A_P_Next_Rebate_Percent      NUMBER;
   B_P_Sales_total              NUMBER;
   B_P_Current_Rebate_Percent   NUMBER;
   B_P_Needed_Sales             NUMBER;
   B_P_Next_Rebate_Percent      NUMBER;
   P_CODE                       VARCHAR (10);
BEGIN
   SELECT   CC_CODE
     INTO   P_CODE
     FROM   CUSTOMERS
    WHERE   C_MOBILE = P_phone;



   FOR OUTDATA
   IN (  

--My Query to retrieve the data
Select ................ 


)
   LOOP
      IF OUTDATA.CLASS = 'X'
      THEN
         A_P_Sales_total := OUTDATA.SALES_TOTAL;
         A_P_Current_Rebate_Percent := OUTDATA.CURRENT_REBATE_PERCENT;
         A_P_Needed_Sales := OUTDATA.NEEDED_SALES_FOR_HIGHER_REBATE;
         A_P_Next_Rebate_Percent := OUTDATA.NEXT_HIGHER_REBATE_PERCENT;
      END IF;


      IF OUTDATA.CLASS = 'Y'
      THEN
         B_P_Sales_total := OUTDATA.SALES_TOTAL;
         B_P_Current_Rebate_Percent := OUTDATA.CURRENT_REBATE_PERCENT;
         B_P_Needed_Sales := OUTDATA.NEEDED_SALES_FOR_HIGHER_REBATE;
         B_P_Next_Rebate_Percent := OUTDATA.NEXT_HIGHER_REBATE_PERCENT;
      END IF;
   END LOOP;



   RETURN rebate_values (A_P_Sales_total,
                         A_P_Current_Rebate_Percent,
                         A_P_Needed_Sales,
                         A_P_Next_Rebate_Percent,
                         B_P_Sales_total,
                         B_P_Current_Rebate_Percent,
                         B_P_Needed_Sales,
                         B_P_Next_Rebate_Percent);
END;
/

查询需要27秒才能检索每个客户的值。每个客户都有2行,这就是我使用LOOP收集价值的原因。

当我执行该功能时:

SELECT   AA_rebate_function (XXXXXXXXXX) FROM DUAL;

我在27秒内在一个列中获得如下数据:

(XXXX, X, XXXX, X, XXXX, X, XXXX, X)

但是当我执行函数以获取不同列中的值时,需要27 x 8秒= 216秒,即大约3.6分钟,这是一个大问题,因为客户不能在服务终端上等待3.6分钟查看数据。

SELECT   x.c.ASales_total,
         x.c.ACurrent_Rebate_Percent,
         x.c.ANeeded_Sales,
         x.c.ANext_Rebate_Percent,
         x.c.BSales_total,
         x.c.BCurrent_Rebate_Percent,
         x.c.BNeeded_Sales,
         x.c.BNext_Rebate_Percent
  FROM   (SELECT   AA_rebate_function (XXXXXXXXXX) c FROM DUAL) x;

我尝试使用带有OUT值的存储过程,但它不适合我的环境,因为我无法编程从kiosk开发工具包执行存储过程,因为它只支持select语句,使用供应商,他们没有任何计划在不久的将来增加这种支持。

我尝试使用REGEXP_SUBSTR将单个字段转换为多个列,但我得到了类型转换错误,因为它是一个数组。

查询非常复杂,必须计算过去10年的数据并且有数百万行,27秒实际上是获得所需结果的最佳时间。

1 个答案:

答案 0 :(得分:0)

有趣!我没有意识到当你查询一个返回一个对象的函数时,它会为你引用该对象的每一列运行一次该函数。这很尴尬。

我能找到的最简单的解决方案是将您的功能切换为PIPELINED。您需要创建嵌套表类型才能执行此操作。

create type rebate_values_t is table of rebate_values;
/

CREATE OR REPLACE FUNCTION AA_rebate_function (P_phone IN NUMBER)
  RETURN rebate_values_t PIPELINED
IS
  ... your code here ...
  PIPE ROW (rebate_values (A_P_Sales_total,
                     A_P_Current_Rebate_Percent,
                     A_P_Needed_Sales,
                     A_P_Next_Rebate_Percent,
                     B_P_Sales_total,
                     B_P_Current_Rebate_Percent,
                     B_P_Needed_Sales,
                     B_P_Next_Rebate_Percent));
  RETURN;
END;  
/

SELECT   x.ASales_total,
         x.ACurrent_Rebate_Percent,
         x.ANeeded_Sales,
         x.ANext_Rebate_Percent,
         x.BSales_total,
         x.BCurrent_Rebate_Percent,
         x.BNeeded_Sales,
         x.BNext_Rebate_Percent
  FROM   TABLE(AA_rebate_function (XXXXXXXXXX)) x;

出于某种原因,这应该只执行一次,并需要27秒。

相关问题