提高pl / sql的性能,想在Q1的结果上执行Q2

时间:2011-08-10 08:36:25

标签: java plsql oracle11g

CREATE or REPLACE PROCEDURE TEST(
activationStartDate IN DATE,
activationEndDate IN DATE,
deActivationStartDate IN DATE,
deActivationEndDate IN DATE
)
AS
FirstNameListTable LIST_TABLE;

{--COMMENT :LIST_TABLE is nested table :create or replace TYPE "LIST_TABLE" as table     of varchar2(20);-----Nested Table Declaration
/
}

totalSameFirstName NUMBER;
j NUMBER := 1;
BEGIN



SELECT first_name BULK COLLECT INTO FirstNameListTable FROM Employee where start_date between activationStartDate AND activationEndDate 
MINUS 
SELECT first_name FROM Employee where start_date between deActivationStartDate AND deActivationEndDate


FOR i IN FirstNameListTable.FIRST .. FirstNameListTable.LAST LOOP
     SELECT count(*) INTO totalSameFirstName FROM Employee where start_date between activationStartDate AND activationEndDate AND first_name=FirstNameListTable(i)

       IF totalSameFirstName > 2 THEN
            ---business logic
       END IF;
END LOOP;

实际上上面有两个查询

将此查询视为Q1:

SELECT first_name BULK COLLECT INTO FirstNameListTable FROM Employee where start_date between activationStartDate AND activationEndDate 
MINUS 
SELECT first_name FROM Employee where start_date between deActivationStartDate AND deActivationEndDate

此查询为Q2:

SELECT count(*) INTO totalSameFirstName FROM Employee where start_date between activationStartDate AND activationEndDate AND first_name=FirstNameListTable(i)

在两个qyery我扫描完整表,我认为没有必要。我正在迭代Q1的结果,然后再次扫描表来计算类似的first_name。如果特定的firstName出现两次以上我编写业务逻辑。

我可以将两个查询组合起来,意味着我想在一些PL / SQL数据结构中存储Q1的结果,并希望在Q1的结果上执行Q2。

我想将Q1修改为

SELECT * BULK COLLECT INTO FirstNameListTable FROM Employee where start_date between activationStartDate AND activationEndDate 
 MINUS 
 SELECT * FROM Employee where start_date between deActivationStartDate AND deActivationEndDate

但是如何在pl / sql dataStructure中存储'select *'结果以及如何将这些记录传递给第二个查询。你能告诉我我的代码会是什么样子的吗?  试着更清楚,我在这个PL / SQL中缺乏想法,即使它听起来像一个经典:我花了好几个小时试图玩这个但却无处可去

@Ollie 我按照你的建议更改了代码但是遇到了一些错误并且无法解决它们

CREATE or REPLACE PROCEDURE TEST(
 activationStartDate IN DATE, 
 activationEndDate IN DATE, 
 deActivationStartDate IN DATE, 
  deActivationEndDate IN DATE, 
 Out_Entity OUT TEST1.RefCsr
 )
  AS
  FirstNameListTable CRITERIA_LIST_TABLE;
  out NUMBER;
  j NUMBER := 1;

  CURSOR main_cur
  IS
    WITH include_rec
      AS (SELECT first_name,COUNT(1) OVER (PARTITION BY first_name) name_count  FROM employee where start_date between activationStartDate AND activationEndDate 
     MINUS
        SELECT first_name FROM employee where start_date between deActivationStartDate AND deActivationEndDate)
       SELECT first_name FROM include_rec WHERE name_count > 2;             

    BEGIN
     OPEN main_cur;
     FETCH main_cur BULK COLLECT INTO FirstNameListTable;
     CLOSE main_cur;

      OPEN Out_Entity FOR SELECT * FROM TABLE(
                                               CAST (
                                                    FirstNameListTable AS LIST_TABLE
                                                 )
                                    ) Nos;

      END;
       /

错误1:PL / SQL:忽略SQL语句{表示:“WITH include_rec”行}  错误2:PL / SQL:ORA-01789:查询块的结果列数不正确{指示行:“AS(SELECT first_name,COUNT(1)OVER(PARTITION BY first_name)”} 提前致谢 等待你的回复

2 个答案:

答案 0 :(得分:0)

我想告诉我,我没有太多时间来处理这个问题,所以可能会有一些错误,但它应该给你一个我想告诉你的要点:

CREATE or REPLACE 
PROCEDURE TEST( 
   activationStartDate IN DATE, 
   activationEndDate IN DATE, 
   deActivationStartDate IN DATE, 
   deActivationEndDate IN DATE ) 
AS 
   CURSOR main_cur
   IS
      WITH include_rec 
        AS (SELECT first_name, 
                   start_date,
                   COUNT(1) OVER (PARTITION BY first_name) name_count
              FROM Employee 
             WHERE start_date BETWEEN activationStartDate 
                                  AND activationEndDate)
      SELECT DISTINCT 
             first_name
        FROM include_rec
       WHERE start_date NOT BETWEEN deActivationStartDate 
                                AND deActivationEndDate
         AND name_count > 2; 
   --
   FirstNameListTable dbms_sql.varchar2_table;    
BEGIN    
    OPEN main_cur;
    FETCH main_cur BULK COLLECT INTO FirstNameListTable;
    CLOSE main_cur;

    FOR i IN FirstNameListTable.FIRST .. FirstNameListTable.LAST 
    LOOP                 
           ---business logic         
    END LOOP; 

    etc...

我还会说,如果你期望一个大的结果集,那么也可以把BULK COLLECT放到一个循环中来减少你的内存需求。

在理想的世界中,您将变量activationStartDate,activationEndDate,deActivationStartDate和deActivationEndDate作为游标参数传递给游标,以保持游标模块化,但这取决于您。 ; - )

希望这会有所帮助...... 奥利。

编辑: 在回答有关使用记录从表中选择*的问题时,您可以根据游标列声明一个关联数组,对于上面的示例,如果您想从EMPLOYEE中选择多个列,那么在您选择的WITH子句之后来自EMPLOYEE的命名列,而不是:

FirstNameListTable dbms_sql.varchar2_table;

将关联数组类型和变量声明为:

TYPE main_cur_tabtype IS TABLE OF main_cur%ROWTYPE
     INDEX BY PLS_INTEGER;
main_cur_tab main_cur_tabtype;

这为您提供了一个灵活的数组,可以自动保存光标中选择的列(main_cur)。

您可以使用BULK COLLECT将记录收集到此数组中:

OPEN main_cur;
FETCH main_cur BULK COLLECT INTO main_cur_tab;
CLOSE main_cur;

并通过以下方式循环播放:

FOR i IN main_cur_tab.FIRST .. main_cur_tab.LAST
LOOP
   etc.

至于将结果集返回给Java,我不是java专家,您需要在其他论坛中询问或使用Java标记标记此问题,并希望Java人员选择并为您解答

答案 1 :(得分:0)

你能在SQL中做到这一点吗? 例如。也许像这样使用WITH:

WITH q1 AS (SELECT ....)
SELECT /* q2 */ ... FROM q1
WHERE ..
GROUP BY etc

如果您可以在SQL中执行此操作,则可以在以后随时将其嵌入到PL / SQL中。