【问题标题】:Procedure takes several hours to complete程序需要几个小时才能完成
【发布时间】:2014-09-11 13:08:10
【问题描述】:

我正在尝试生成一份报告,为我提供每个科目每个学期每个字母的每个等级的百分比。最终报告将 MAT、ENC 等主题代码作为行标题,然后将术语和字母等级作为列标题。我目前正在遍历每个主题,然后是每个学期,然后是每个年级,并且需要 2 个多小时才能完成。什么是正确/更有效的方法来实现这一点。如果您有任何问题,请随时询问。谢谢你的帮助!!

v_subj          varchar2(5);
v_grade         varchar2(5);
v_termloop      varchar2(6);
c_total         number;
c_dfw           number;
c_gradetotal    number;
c_gradedfw      number;
v_output    varchar2(1000);


Cursor Get_Terms is
select distinct ssbsect_term_code from ssbsect
where ssbsect_term_code >= 201230
and ssbsect_term_code not like '%5'
order by 1;

Cursor Get_Subj is
Select distinct ssbsect_subj_code from ssbsect
where ssbsect_term_code >= 201230
and ssbsect_insm_code = 'DL'
and ssbsect_term_code not like '%5'
order by 1;

Cursor Get_Grade is
select distinct msvcrse_grde_code from msvcrse
where msvcrse_grde_code in ('A','B','C','D','F','S','U','W')
order by 1;

Cursor Get_Total is
select count(msvcrse_pidm) from msvcrse
join ssbsect on msvcrse_crn = ssbsect_crn
                           and msvcrse_Term_code = ssbsect_term_code
where msvcrse_term_code = v_termloop
and ssbsect_insm_code = 'DL'
and msvcrse_crse_numb < '3000'
and msvcrse_subj_code = v_subj
and msvcrse_gmod_code <> 'D';

Cursor Get_GradeTotal is
select count(msvcrse_pidm) from msvcrse
join ssbsect on msvcrse_crn = ssbsect_crn
                           and msvcrse_Term_code = ssbsect_term_code
where msvcrse_term_code = v_termloop
and ssbsect_insm_code = 'DL'
and msvcrse_grde_code like '%'||v_grade||'%'
and msvcrse_crse_numb < '3000'
and msvcrse_subj_code = v_subj
and msvcrse_gmod_code <> 'D';


Begin

Open Get_Subj;
    Loop
    Fetch Get_Subj into v_subj;
    Exit when Get_Subj%NOTFOUND;
    v_output := '';

    v_output := v_subj;

    Open Get_Terms;
        Loop
        Fetch Get_Terms into v_termloop;
        Exit when Get_Terms%NOTFOUND;   


        Open Get_Grade;
            Loop
            Fetch Get_Grade into v_grade;
            Exit when Get_grade%NOTFOUND;


            Open Get_Total;
            Fetch Get_Total into c_total;
            Close Get_Total;


            Open Get_GradeTotal;
            Fetch Get_GradeTotal into c_gradetotal;
            Close Get_GradeTotal;

            if c_total = 0 then
                c_total := NULL;
            end if; 

            v_output := v_output ||'|'||nvl(100*(round(c_gradetotal/c_total,3)),0);

            --dbms_output.put_line(v_subj||'|'||v_termloop||'|'||v_insm||'|'||nvl(100*(round(c_dfw/c_total,3)),0));



            End Loop;
        Close Get_Grade;

        End Loop;
    Close Get_Terms;

    dbms_output.put_line(v_output);

【问题讨论】:

  • 您绝对可以将其作为单个查询执行,这将比循环快得多。但是,如果没有样本数据和表格结构,很难向您解释如何做到这一点。
  • 您想知道什么。这里唯一重要的表是 msvcrse,它是一个课程历史表。它有一个学生参加的每个班级的条目,以及他们参加的年级和学期代码。这有帮助吗?感谢您的意见!

标签: sql performance plsql procedure banner


【解决方案1】:

我对你的代码稍作改动,但结果应该是一样的。

请合并游标 get_grade 和 get_gradetotal,这段代码中有两个是没有意义的。

DECLARE
  c_total      NUMBER;
  v_output     VARCHAR2 ( 1000 );


  CURSOR get_subj  IS
      SELECT DISTINCT ssbsect_subj_code
        FROM ssbsect
       WHERE ssbsect_term_code >= 201230
         AND ssbsect_insm_code = 'DL'
         AND ssbsect_term_code NOT LIKE '%5'
    ORDER BY 1;

  CURSOR get_terms  IS
      SELECT DISTINCT ssbsect_term_code
        FROM ssbsect
       WHERE ssbsect_term_code >= 201230
         AND ssbsect_term_code NOT LIKE '%5'
    ORDER BY 1;

  CURSOR get_total ( lci_termloop IN ssbsect.msvcrse_term_code%TYPE
                   , lci_subj     IN msvcrse.msvcrse_subj_code%TYPE ) IS
    SELECT COUNT ( msvcrse_pidm )
      FROM msvcrse
           JOIN ssbsect
             ON msvcrse_crn = ssbsect_crn
            AND msvcrse_term_code = ssbsect_term_code
     WHERE msvcrse_term_code = lci_termloop
       AND ssbsect_insm_code = 'DL'
       AND msvcrse_crse_numb < '3000'
       AND msvcrse_subj_code = lci_subj
       AND msvcrse_gmod_code <> 'D';

  CURSOR get_grade  IS
      SELECT DISTINCT msvcrse_grde_code
        FROM msvcrse
       WHERE msvcrse_grde_code IN ('A', 'B', 'C', 'D', 'F', 'S', 'U', 'W')
    ORDER BY 1;

  CURSOR get_gradetotal ( lci_termloop IN ssbsect.msvcrse_term_code%TYPE
                        , lci_grade    IN msvcrse.msvcrse_grde_code%TYPE
                        , lci_subj     IN msvcrse.msvcrse_subj_code%TYPE ) IS
    SELECT COUNT ( msvcrse_pidm ) AS msvcrse_pidm
      FROM msvcrse
           JOIN ssbsect
             ON msvcrse_crn = ssbsect_crn
            AND msvcrse_term_code = ssbsect_term_code
     WHERE msvcrse_term_code = lci_termloop
       AND ssbsect_insm_code = 'DL'
       AND msvcrse_grde_code LIKE '%' || lci_grade || '%'
       AND msvcrse_crse_numb < '3000'
       AND msvcrse_subj_code = lci_subj
       AND msvcrse_gmod_code <> 'D';
BEGIN

  FOR rec_subj IN get_subj LOOP
    v_output := rec_subj.ssbsect_subj_code;

    FOR rec_term IN get_terms LOOP

      OPEN get_total ( lci_termloop => rec_term.ssbsect_term_code
                     , lci_subj     => rec_subj.ssbsect_subj_code );
      FETCH get_total INTO c_total;
      CLOSE get_total;

      FOR rec_grade IN get_grade LOOP
        <<grade_total>>
        FOR rec_gradetotal IN get_gradetotal ( lci_termloop => rec_term.ssbsect_term_code
                                             , lci_grade    => rec_grade.msvcrse_grde_code
                                             , lci_subj     => v_output ) LOOP
          IF c_total = 0 THEN
            c_total := NULL;
            v_output := v_output || '|';
            CONTINUE grade_total; -- <-- AVOID WORK
          END IF;

          v_output := v_output || '|' || NVL ( 100 * ( ROUND ( rec_gradetotal.msvcrse_pidm / c_total, 3 ) ), 0 ); -- <-- PLEASE MAKE THIS CODE BETTER
          --dbms_output.put_line(rec_subj.ssbsect_subj_code||'|'||rec_term.ssbsect_term_code||'|'||v_insm||'|'||nvl(100*(round(c_dfw/c_total,3)),0));
        END LOOP grade_total;
      END LOOP;
    END LOOP;

    --DBMS_OUTPUT.put_line ( rec_subj.ssbsect_subj_code );
  END LOOP;

END;

我更改所有代码没有编译,请确保光标使用的类型。

我猜有 50% 的改进,但你需要测试。

如果您有任何问题,请告诉我,请在 SQL 中使用表别名并小心循环。

谢谢。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-05-08
    • 1970-01-01
    • 2021-09-10
    • 2017-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多