【问题标题】:Improve Performance of pl/sql,want to perform Q2 on result of Q1提高 pl/sql 的性能,想要对 Q1 的结果执行 Q2
【发布时间】:2011-08-10 08:36:25
【问题描述】:
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。如果一个特定的名字出现两次以上,我会编写业务逻辑。

我可以将这两个查询结合起来吗,意味着我想将 Q1 的结果存储在一些 PL/SQL 数据结构中,并希望对 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 中缺乏想法,即使它听起来像一个经典:我花了几个小时试图玩这个,但无处可去

@奥利 我按照您的建议更改了代码,但出现了一些错误并且无法解决它们

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

【问题讨论】:

  • 请不要在 SO 上创建迁移问题。 “迁移问题”是一个得到答案的问题,然后被编辑以添加新内容、新问题,然后得到答案,然后被编辑以添加更多内容。它们通常包含诸如“我尝试了您的建议,它确实解决了我的问题,但现在我有一个新问题......”之类的文字。在这种情况下,请在此处接受答案,再解决您的问题,然后在新问题中留下新问题。
  • 这种情况的典型结果是,回答的人将不得不继续编辑他们的答案。此外,新人可能只回答最新的问题,而将解决方案的零碎部分留在所有您的问题中。提出 1 个问题,接受 1 个答案,然后继续。

标签: java plsql oracle11g


【解决方案1】:

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

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 人能找到它并为您解答.

【讨论】:

  • 如果您在主查询中执行全表扫描,也许您应该与您的 DBA 交谈并询问他们是否可以/有利于 start_date 的索引。
  • @Ollie--请参阅原始问题,我已按照您的建议进行了编辑...但出现了一些错误...等待您的回复...我是 pl/sql 的新手
  • 你没有复制我给你的东西,我可以在你的代码中看到一个减号,我已经从我写的代码中完全消除了它。如果你打算使用我给你的代码,至少按预期使用它,而不是你的原始代码和我给你的答案的混合体。试试我给你的代码,看看是否有错误。
  • 不,我已经回答了你原来的问题,因为你似乎不理解我给你的 Pl/SQL 或建议,并且似乎也在努力处理 Java 方面的问题.我建议您请一位顾问来帮助您和您的组织。你问的每一个问题我都会回答,然后你再问更多问题。这不是这个论坛的目的。由于我已经回答了您关于 PL/SQL 和 SQL 查询性能的原始问题,并且在该主题方面也为您提供了大量帮助,因此我不再能够为您提供进一步的帮助。祝你好运。
  • @user595014 - 您的问题不断变化,因此提供答案变得非常困难。当您在此处获得的帮助改变了您的问题范围时,请考虑提出单独的问题。 Ollie 非常有耐心,但您需要坚持最初的问题。
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 2019-07-28
    • 2021-10-13
    • 1970-01-01
    • 1970-01-01
    • 2018-06-02
    • 1970-01-01
    • 2022-01-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多