通过循环计算PL / SQL?

时间:2017-03-10 04:43:09

标签: sql oracle plsql

我有一项任务要求我重写我为之前任务编写的PL / SQL代码:

DECLARE
    -- Variables used to count a, b, c, d, and f grades:
    na integer := 0;
    nb integer := 0;
    nc integer := 0;
    nd integer := 0;
    nf integer := 0;
BEGIN
    select count(*) into na
    from   gradeReport1
    where  grade = 'A';

    select count(*) into nb
    from   gradeReport1
    where  grade = 'B';

    select count(*) into nc
    from   gradeReport1
    where  grade = 'C';

    select count(*) into nd
    from   gradeReport1
    where  grade = 'D';

    select count(*) into nf
    from   gradeReport1
    where  grade = 'F';

    if na > 0 then
        DBMS_OUTPUT.PUT_LINE('There are total ' || na || ' A''s');
    else
        DBMS_OUTPUT.PUT_LINE('There are no As');
    end if;

    if nb > 0 then
        DBMS_OUTPUT.PUT_LINE('There are total ' || nb || ' B''s');
    else 
        DBMS_OUTPUT.PUT_LINE('There are no Bs');
    end if;

    if nc > 0 then
        DBMS_OUTPUT.PUT_LINE('There are total ' || nc || ' C''s');
    else
        DBMS_OUTPUT.PUT_LINE('There are no Cs');
    end if;

    if nd > 0 then
        DBMS_OUTPUT.PUT_LINE('There are total ' || nd || ' D''s');
    else
        DBMS_OUTPUT.PUT_LINE('There are no Ds');
    end if;

    if nf > 0 then
        DBMS_OUTPUT.PUT_LINE('There are total ' || nf || ' F''s');
    else
        DBMS_OUTPUT.PUT_LINE('There are no Fs');
    end if;
END;

所有这一切都是搜索我制作的名为gradeReport的表格,该表格存储了学生ID并将其与成绩相关联。 PL / SQL计算A级到F级的所有实例。问题是我要使用循环和VARRAYS重写此解决方案。任何人都可以给我一些提示来帮助我为我滚球吗?我只使用PL / SQL几个星期,并没有比语法的基本理解更多,所以我完全迷失了,不知道从哪里开始。

这里没有寻找任何答案,只是一些想法。

谢谢

4 个答案:

答案 0 :(得分:1)

答案 1 :(得分:1)

编辑:由于要求专门针对varrays,请参阅AmmoQ和MTO的回复。但是,正如他们都指出的那样,在实践中你不太可能需要这类任务的数组,即使你这样做了,你也会使用nested tableassociative array而不是{{} {3}}

你需要一个varray,与

一致
for r in (
    select grade from gradereport1
)
loop
    ...
end loop;

在实际代码中,您可能会进行group by查询并让SQL为您进行计数。

然后根据r.grade的值,有条件地增加循环中的计数器。

您可以合理化所有if语句,以便通过编写一个获得成绩和总计的程序来报告总计,因为所有这些都是相同的逻辑。

procedure showgrade
    ( p_grade gradereport1.grade%type
    , p_count integer )
is
begin
    ...
end showgrade;

我会将细节留作练习。

只是为了好玩,这是另一种方法,使用数组和循环(但不是varray - 它们确实有点无用):

declare
    type gradereport_tt is table of pls_integer index by gradereport.grade%type;
    gradecounts gradereport_tt;
    g gradereport.grade%type;
begin
    -- Initialise counts:
    gradecounts('A') := 0;
    gradecounts('B') := 0;
    gradecounts('C') := 0;
    gradecounts('D') := 0;
    gradecounts('E') := 0;
    gradecounts('F') := 0;

    -- Count grades:
    for r in (
        select grade from gradereport
    )
    loop
        gradecounts(r.grade) := gradecounts(r.grade) +1;
    end loop;

    -- Report counts:
    g := gradecounts.first;

    while g is not null loop
        dbms_output.put_line(g || ': ' || gradecounts(g));
        g := gradecounts.next(g);
    end loop;
end;

顺便说一句,除了条件包含需要分离的ifand条件的混合之外,没有必要像or一样放置括号。

也没有必要用大写写任何东西。这很常见,Cursor FOR loop一直都是这样,但是他们在HTML / CSS世界中进行了这场辩论,并以小写字母为可读性。如果您打算使用大写规则,至少要始终如一地使用它。您的代码示例包含end if;END;更不用说Select(我已修复)。有些人似乎能够阅读这样的代码而不会让他们疯狂,但我担心我不是其中之一。

答案 2 :(得分:1)

DECLARE
  -- Need to ensure the array size will hold all the grades
  TYPE grade_tab IS VARRAY(200) OF gradeReport1.grade%TYPE;

  -- variable used to store the grades:
  t_grades grade_tab;

  -- Variables used to count a, b, c, d, and f grades:
  na INTEGER;
  nb INTEGER;
  nc INTEGER;
  nd INTEGER;
  nf INTEGER;
BEGIN
  -- Store the grades in an array:
  SELECT grade
  BULK COLLECT INTO t_grades
  FROM   gradeReport1
  WHERE  grade IN ( 'A', 'B', 'C', 'D', 'F' );

  -- Loop through the grades and count how many of each:
  FOR i IN 1 .. t_grades.COUNT LOOP
    IF    t_grades(i) = 'A' THEN na := na + 1;
    ELSIF t_grades(i) = 'B' THEN nb := nb + 1;
    ELSIF t_grades(i) = 'C' THEN nc := nc + 1;
    ELSIF t_grades(i) = 'D' THEN nd := nd + 1;
    ELSIF t_grades(i) = 'F' THEN nf := nf + 1;
    END IF;
  END LOOP;

  -- Output grade counts
END;
/

但是,更简单的解决方案是在单个SQL查询中进行计数(尽管这不符合评估使用VARRAY的要求):

DECLARE
  -- Variables used to count a, b, c, d, and f grades:
  na INTEGER;
  nb INTEGER;
  nc INTEGER;
  nd INTEGER;
  nf INTEGER;
BEGIN
  SELECT COUNT( CASE grade WHEN 'A' THEN 1 END ),
         COUNT( CASE grade WHEN 'B' THEN 1 END ),
         COUNT( CASE grade WHEN 'C' THEN 1 END ),
         COUNT( CASE grade WHEN 'D' THEN 1 END ),
         COUNT( CASE grade WHEN 'F' THEN 1 END )
  INTO   na,
         nb,
         nc,
         nd,
         nf
  FROM   gradeReport1;

  -- Output grade counts...
END;
/

答案 3 :(得分:0)

使用VARRAY的解决方案可能如下所示:

change_column :tippies, :tip, :float, null: true, default: 0

但老实说,在这种情况下使用VARRAYs毫无意义。只需使用游标循环:

DECLARE 
   type chararray IS VARRAY(6) OF CHAR(1); 
   type numarray IS VARRAY(6) OF INTEGER; 
   grades chararray; 
   cnt  numarray; 
BEGIN 
   select grade, count(*) 
     bulk collect into grades, cnt
     from gradeReport1
     group by grade
     order by grade;

   for i in 1..grades.count loop
     DBMS_OUTPUT.PUT_LINE('There are total ' || cnt(i) || ' ' ||grades(i)||'s');
   end loop;
END; 
/

但是,找到缺失的标记(计数为0的标记)有点困难。