2

I am trying to solve a problem in PL/SQL, but I am new to the language. I decided to solve it as I would solve it in another language, and so I learned something about how SQL declares and utilizes arrays in this thread:

Oracle PL/SQL - How to create a simple array variable?

I followed that example, but my code doesn't run. The error message is unhelpful. In particular it references line 21 whereas my code block begins on line 54. Here it is, in case it makes sense by error code, somehow:

ORA-06550: line 21, column 7: PLS-00103: Encountered the symbol "IN" when expecting one of the following: := . ( @ % ; 06550. 00000 - "line %s, column %s:\n%s" *Cause: Usually a PL/SQL compilation error.

And here is my code block. I am sure I made a bunch of mistakes since I am learning by code example here. If you could please point out any syntax or other errors I would very much appreciate it. Thank you

declare
    vISBNa books.ISBN%type := '1059831198';
    vISBNb books.ISBN%type := '0401140733';
    vISBNc books.ISBN%type := '4981341710';
    vISBNd books.ISBN%type := '8843172113';
    vCATEGORYtemp books.CATEGORY%type;
    vRETAILtemp books.RETAIL%type;
    type arry is varray(4) of books.ISBN%type;
    array arry := arry(vISBNa, vISBNb, vISBNc, vISBNd);
begin
   for i in 1..array.count loop
        select category
            into vCATEGORYtemp
            from books
            where ISBN = i;
        select retail
            into vRETAILtemp
            from books
            where ISBN = i;
        IF vCATEGORYtemp = 'COMPUTER' THEN
          vRETAILtemp := vRETAILtemp * 0.7;
        ELSIF vCATEGORYtemp = 'FITNESS' THEN
          vRETAILtemp := vRETAILtemp * 0.6; 
        ELSIF vCATEGORYtemp = 'BUSINESS' THEN
          vRETAILtemp := vRETAILtemp * 0.8;
        ELSE
          vRETAILtemp := vRETAILtemp * 0.9;
        END IF;            
       dbms_output.put_line(vRETAILtemp);
   end loop;
end;
sqwirl grrl
  • 23
  • 1
  • 5
  • 1
    Are you trying to solve a problem in SQL (as you said in your introduction), or in PL/SQL as you are showing in the code? Or do you think they are the same thing? For the record, the problem seems to have a trivial solution in SQL; and SQL is **not** like any other languages you may have used in the past (C, Java, Python, etc.) It has an entirely different philosophy - it doesn't even *look* anything like those other languages. –  Jun 22 '21 at 04:02
  • 1
    Hi Mathguy. I did state that this is PL/SQL in the heading. I edited the body of my question to remove any confusion due to non-specificity. We have been working with SQL throughout the course, and this is my first foray into PL/SQL. I don't feel very comfortable with it yet, so I decided to solve the problem in a way that resembles something more familiar to me. My teacher has a bit of a throw them in the pool and let them figure it out philosophy and solutions to given problems often require more than what is taught in class. – sqwirl grrl Jun 22 '21 at 04:36
  • Then your thought is OK, PL/SQL is a procedural language similar to other languages you may have used in the past. Arrays, loops etc. make perfect sense. –  Jun 22 '21 at 04:43
  • Are you sure the error is thrown from the PL/SQL code? What interface are you using (such as SQL Developer, or Toad, or SQL\*Plus, etc.)? The error may indeed be on line 21 which is outside the PL/SQL block. Are you just selecting the block and then executing it? The code looks correct to me (if a bit too complicated). –  Jun 22 '21 at 04:46
  • I am using Oracle SQL Developer, and yes, I am selecting the block that I want to execute. Line 21 is a comment, so I find that error message confusing. – sqwirl grrl Jun 22 '21 at 04:52
  • If you are selecting the block, then "line 21" means line 21 of the block (probably line 74 in the editing window, 53 + 21 = 74). –  Jun 22 '21 at 04:59
  • Wow, I feel almost embarrassed that I didn't think of that myself. Yes, looking at the line number relative to the start of the block helps me see what's happening more clearly. – sqwirl grrl Jun 22 '21 at 05:26
  • By the way, it doesn't make much difference in this example, but generally *nested table collections* e.g. `type arry is table of books.ISBN%type;` are more useful than *varrays* like `type arry is varray(4) of books.ISBN%type;`. https://www.williamrobertson.net/documents/collection-types.html – William Robertson Jun 22 '21 at 07:36

3 Answers3

3

The problem certainly has a trivial SQL solution, but since this is PL/SQL practice (specifically), here are a few suggestions for improvement. Note that the code is almost correct; in particular, it is not clear what, if anything, throws the exact error you mentioned.

In the loop, you can assign to both local variables in a single select statement, as shown below. Notice also the where clause, where you must compare ISBN from the table to array(i), not to i. (That is the only error I found!)

for i in 1 .. array.count loop
  select category, retail
    into vCATEGORYtemp, vRETAILtemp
  from   books
  where  ISBN = array(i);

Finally, the assignment to vRETAILtemp can be simplified, using a case expression (not a case statement, which is a construct very similar to the if... then... elsif... else... end construct):

vRETAILtemp := vRETAILtemp * case vCATEGORYtemp when 'COMPUTER' then 0.7
                                                when 'FITNESS'  then 0.6
                                                when 'BUSINESS' then 0.8
                                                                else 0.9 end;
1

There are plenty of errors in there :

  • You have a type called arry but you have also a variable with the same name. PL/SQL doesn't like that.

  • In the for loop : You are trying to loop over the type and not the array

    for i in 1..array.count loop 
    

it should be :

   for i in 1..arry.count loop

Generally speaking it's not common to use arrays in Oracle. Although it's possible but it's much more efficient to use SQL which is very simple and straightforward. Your code can be simplified to :

    declare         
        Cursor Cur is select case 
                            when vCATEGORYtemp = 'COMPUTER' then 0.7 
                            when vCATEGORYtemp = 'FITNESS' then 0.6
                            when vCATEGORYtemp = 'BUSINESS' then 0.8
                            else vCATEGORYtemp = 'COMPUTER' then 0.9
                          end * vRETAILtemp as Output
                    from books 
                    where ISBN in ('1059831198','0401140733','4981341710','8843172113');
        R Cur%Rowtype;
    begin
        Open Cur;
        loop
            fetch Cur into R;
            exit when Cur%notfound;
            
            dbms_output.put_line(R.Output);
        end loop;
        Close Cur;
    end ;

I think you have to review the basics of coding which are :

  • Making the efforts of naming the objects properly. When you have two things called array and arry you save 0.5 seconds writing your code but you have great chances to spend hours trying to investigate what the problem is in case of a bug.

  • In coding, we almost never write a big code and then check if it compiles. Is should be incremental especially as beginner. Write two lines and check it compiles, etc. Otherwise, it's complicated to know where the problem is coming from.

  • In Oracle, you can use SQL Developer, and put your code inside a Function/Procedure or Package. At least in case of a compilation problem, it will tell you the right line which has a problem.

Hope this helps.

Thomas Carlton
  • 5,344
  • 10
  • 63
  • 126
  • Thanks very much. I will go over this carefully and assimilate as well as I am able. For the record, arry was meant as a var name. It's not the best choice for a var name here, I can see. Re the if else, that is stipulated in the question, but my first preference was to use case, too. Also, I copied " for i in 1..array.count loop" from the other question thread even though I thought a var name should go in place of "array". I think I see now that "array" is in fact a var name there. Is that correct? – sqwirl grrl Jun 22 '21 at 03:28
  • I believe you got confused by the names. `arry` is the type and `array` is an instance of that type (an actual array). The OP used them correctly; the loop is over `array`, which is the actual array, not the type name. –  Jun 22 '21 at 05:08
  • 1
    A cursor for loop is much simpler and generally more robust and efficient than an explicitly declared record and open-fetch-exit-close. – William Robertson Jun 22 '21 at 08:15
1

You can do this more simply (but still using PL/SQL for this learning exercise):

begin
    for r in (
        select * from books
    )
    loop
        r.retail := r.retail *
            case r.category
                when 'COMPUTER' then 0.7
                when 'FITNESS'  then 0.6
                when 'BUSINESS' then 0.8
                else 0.9
            end;

        dbms_output.put_line(r.isbn||' '||rpad(r.category,10)||' '||r.retail);
    end loop;
end;

Or using an array:

declare
    type book_tt is table of books%rowtype;
    l_books book_tt;
begin
    select *
    bulk collect into l_books
    from   books b;

    for i in 1..l_books.last loop
        l_books(i).retail := l_books(i).retail *
            case l_books(i).category
                when 'COMPUTER' then 0.7
                when 'FITNESS'  then 0.6
                when 'BUSINESS' then 0.8
                else 0.9
            end;

        dbms_output.put_line(l_books(i).isbn||' '||rpad(l_books(i).category,10)||' '||l_books(i).retail);
    end loop;
end;

(I've cheated a little bit by using select * and books%rowtype, because this is just an example and I am lazy. For real code it's usually better to list the actual columns you want explicitly.)

Test data:

create table books
( isbn varchar2(10)
, category varchar2(20)
, retail number(6,2) );

insert all
    into books values ( '1059831198', 'COMPUTER', 10)
    into books values ( '0401140733', 'CATS', 10)
    into books values ( '4981341710', 'FITNESS', 10)
    into books values ( '8843172113', 'BUSINESS', 10)
select * from dual;

Output:

1059831198 COMPUTER   7
0401140733 CATS       9
4981341710 FITNESS    6
8843172113 BUSINESS   8
William Robertson
  • 15,273
  • 4
  • 38
  • 44