【问题标题】:Returning multiple values using function causing multiple query runs使用导致多次查询运行的函数返回多个值
【发布时间】:2018-02-22 13:00:40
【问题描述】:

我们有售货亭供客户检查他们购买的两种不同类别商品的数量。他们将输入他们的手机号码,这将向他们的手机号码发送一个 OTP,然后他们将输入回验证,系统必须检查数据并为他们显示。作为开发人员,kiosk 供应商为我们提供了一个功能有限的开发工具包,我们可以通过它对数据库执行 select 语句并在 kiosk 上显示返回值。

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

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 开发工具包编程执行存储过程,因为它只支持选择语句,与供应商核实过,但他们不'没有任何计划在不久的将来添加该支持。

我尝试使用 REGEXP_SUBSTR 将单个字段转换为多个列,但由于它是一个数组,因此出现类型转换错误。

查询很复杂,要计算最近10年的数据,有几百万行,27秒其实是得到想要的结果的最佳时间。

【问题讨论】:

  • 简化您的问题并创建一个模型来展示您遇到的特殊困难。这样您就可以包含真实代码(而不是删减比特)和代表性数据(而不是让我们猜测)。见这里:stackoverflow.com/help/mcve
  • 当时真的要计算过去10年吗?您不能在级别上预先计算/汇总值以减少行数吗?

标签: sql oracle plsql oracle11g


【解决方案1】:

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

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

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 秒。

【讨论】:

  • 感谢只花了 27 秒就得到了结果。我将尝试进一步优化查询以减少运行时间。再次感谢。
猜你喜欢
  • 1970-01-01
  • 2021-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多