неинициализированная коллекция отображается при вызове процедуры в pl / sql - PullRequest
0 голосов
/ 26 июня 2018

Я видел другие подобные вопросы, но не могу понять, в чем проблема в моем коде.Ниже приведено определение процедуры

Proc.sql

set serveroutput on

create or replace type myarray is varray(1000) of number;
/ 
create or replace procedure Bill(cid in number , bill out number) is
md myarray;
q myarray;
pr myarray;
rs myarray;
begin
bill:=0;
select model,quantity BULK COLLECT into md,q from transactions where customer=cid;

for i in 1..md.COUNT loop
select price BULK COLLECT into pr from computers where model_no=md(i);
END LOOP;

for i in 1..md.COUNT loop
rs(i):=q(i)*pr(i);
end loop;



for i in 1..md.COUNT loop
bill:=bill+rs(i);
end loop;

end;
/
show errors;

, и это мой файл для вызова процедуры

CallProc.sql

set serveroutput on
declare
bll number(2):=0;
begin
Bill(1,bll);
DBMS_OUTPUT.PUT_LINE('T = '|| bll);
end;
/

1 Ответ

0 голосов
/ 26 июня 2018

Если вы инициализируете массив как пустую коллекцию:

declare
  ...
  rs myarray := myarray();
begin

, тогда вам придется увеличить его емкость , либо во время обхода цикла:

for i in 1..md.COUNT loop
  rs.extend;
  rs(i):=q(i)*pr(i);
end loop;

или за один раз (что должно быть более эффективным):

rs.extend(md.COUNT);
for i in 1..md.COUNT loop
  rs(i):=q(i)*pr(i);
end loop;

Вам все еще нужно инициализировать пустую коллекцию перед ее расширением.


У вас есть другие проблемыхоть.Вы делаете:

rs(i):=q(i)*pr(i);

, но коллекции rs и pr полностью независимы, поэтому их индексы не имеют отношения.Я думаю, что вы пытаетесь добавлять записи к pr каждый раз вокруг этого цикла:

for i in 1..md.COUNT loop
select price BULK COLLECT into pr from computers where model_no=md(i);
END LOOP;

но на самом деле вы заменяете все содержимое коллекции однимзначение, не добавляя;и в конце этого цикла у вас будет только цена последней строки в коллекции md.Единственное значение i, допустимое для этой коллекции, равно 1, поскольку существует только pr(1) (при условии, что model_no уникально);и если только у клиента нет только одной транзакции, которая не будет правильной ценой, за исключением последней модели (где i! = 1).

Вы можете просто получить соответствующую цену внутри цикла, без сборадля этого:

create or replace procedure Bill(cid in number , bill out number) is
  md myarray;
  q myarray;
  pr number; -- scalar variable
  rs myarray := myarray();
begin
  select model, quantity bulk collect into md, q from transactions where customer=cid;

  rs.extend(md.COUNT);
  for i in 1..md.COUNT loop
    select price into pr from computers where model_no=md(i); -- not bulk collect
    rs(i):=q(i)*pr; -- pr no longer indexed
  end loop;

  bill:=0;
  for i in 1..md.COUNT loop
    bill:=bill+rs(i);
  end loop;
end;
/

Было бы более эффективно привести цены в соответствие с остальными данными, присоединившись к таблицам и выполнив один массовый сбор во всех трех коллекциях:

create or replace procedure Bill(cid in number , bill out number) is
  md myarray;
  q myarray;
  pr myarray;
  rs myarray := myarray();
begin
  select t.model, t.quantity, c.price
  bulk collect into md, q, pr
  from transactions t
  join computers c on c.model_no = t.model
  where t.customer = cid;

  rs.extend(md.COUNT);
  for i in 1..md.COUNT loop
    rs(i):=q(i)*pr(i);
  end loop;

  bill:=0;
  for i in 1..md.COUNT loop
    bill:=bill+rs(i);
  end loop;
end;
/

Но тогда вы можете еще больше упростить, выполнив оба вычисления в одном цикле:

  bill:=0;
  rs.extend(md.COUNT);
  for i in 1..md.COUNT loop
    rs(i):=q(i)*pr(i);
    bill:=bill+rs(i);
  end loop;

, и из этого вы можете видеть, что вам вообще не нужно rs, вы можете просто рассчитать изменение счетанапрямую:

create or replace procedure Bill(cid in number , bill out number) is
  md myarray;
  q myarray;
  pr myarray;
begin
  select t.model, t.quantity, c.price
  bulk collect into md, q, pr
  from transactions t
  join computers c on c.model_no = t.model
  where t.customer = cid;

  bill:=0;
  for i in 1..md.COUNT loop
    bill := bill + q(i)*pr(i);
  end loop;
end;
/

Но вам не нужны циклы или коллекции вообще:

create or replace procedure Bill(cid in number , bill out number) is
begin
  select sum(t.quantity * c.price)
  into bill
  from transactions t
  join computers c on c.model_no = t.model
  where t.customer = cid;
end;
/

, и, очевидно, вы можете просто выполнить этот запрос как обычный SQL, не используя PL / SQL илиПроцедура вообще.

Зависит от того, что ваше назначение говорит вам использовать ...

...