Home > Enterprise >  Stored Procedure-SQL Developer
Stored Procedure-SQL Developer

Time:10-14

Question: Write a stored procedure which accepts the camperid, category, chargedate, and amount as input parameters and has one output parameter which is of type varchar2 which will contain a message. o If the camperID is not a valid camperID the output parameter should be set to 'ERROR: No such camper ID.' o If the category is not a valid category the output parameter should be set to 'ERROR: No such charge category.' o If the Amount is not in the correct range the output parameter should be set to 'ERROR: amount must be >0 and no more than $ 40.' o If the Amount would cause the total spent to be more than the budget for that camper the output parameter should be set to 'ERROR: insufficient funds.' Here is my query

create or replace procedure P_STORED
       (c_camperid in charges.camperid%type,c_category in charges.camperid%type, c_chargedate in charges.camperid%type, c_amt in charges.camperid%type,
        c_message   out varchar2  )
     as
       p_camperid   charges.camperid%type;
       c_cat charges.category%type;
       a_amt charges.amt%TYPE;
       c_date charges.chargedate%type;
       p_error varchar2(5);
       begin
       v_error := 'False';
       select c.camperid, c.category
        into p_camperid, c_cat
       from charges c
      where c.camperid = c_camperid;
      if p_camperid = c.camperid then
       v_error := 'true';
     c_message := 'Camper ' || c_camperid || ' exists';
     else
     v_error := 'False';
     c_message := 'Error: no such camper ID (' || c_camperid || ')';
     end if;
     if c_cat = c.category then
     v_error := 'true';
     c_message := 'Category ' || c_cat || ' exists';
     else
     v_error := 'False';
     c_message := 'Error: no such charge category (' || c_cat || ')';
      end if;
      if(a_amt >0 and a_amt < 40) then
       v_error := 'true';
     c_message := 'Amount ' || a_amt|| ' is with in range';
     else
     v_error := 'False';
     c_message := 'amount must be >0 and no more than $ 40.';
     end if;
     if(campers.spent < campers.budget) then
     c_message := 'Amount ' || a_amt|| ' is under budget';
     else 
     c_message := 'ERROR: insufficient funds';
     end;

I get this error LINE/COL ERROR


41/5 PLS-00103: Encountered the symbol ";" when expecting one of the following: if Errors: check compiler log

CodePudding user response:

Why would you pass the whole bunch of parameters when all you need is a camper ID (and there's nothing you'd do with the rest of them)? That just doesn't make sense ...

Here's an option which shows how to do it.

Sample table:

SQL> select * from charges;

  CAMPERID CATEGORY   CHARGEDATE                 AMT
---------- ---------- ------------------- ----------
         1 Category 1 12.10.2021 00:00:00        500

SQL>

Procedure:

SQL> create or replace procedure p_test
  2    (par_camperid   in charges.camperid%type,
  3     par_message   out varchar2
  4    )
  5  as
  6    l_camperid   charges.camperid%type;
  7  begin
  8    select c.camperid
  9      into l_camperid
 10      from charges c
 11      where c.camperid = par_camperid;
 12
 13    par_message := 'Camper ' || par_camperid || ' exists';
 14  exception
 15    when no_data_found then
 16      par_message := 'Error: no such camper ID (' || par_camperid || ')';
 17  end;
 18  /

Procedure created.

Testing:

SQL> set serveroutput on;
SQL> declare
  2    l_msg varchar2(100);
  3  begin
  4    p_test(1, l_msg);
  5    dbms_output.put_line(l_msg);
  6  end;
  7  /
Camper 1 exists

PL/SQL procedure successfully completed.

SQL> declare
  2    l_msg varchar2(100);
  3  begin
  4    p_test(2, l_msg);
  5    dbms_output.put_line(l_msg);
  6  end;
  7  /
Error: no such camper ID (2)

PL/SQL procedure successfully completed.

SQL>

Code you wrote:

  • typos, typos, typos ... is it p_error or p_erorr?
  • if should have end if
  • if can't contain a select statement (you have to select value separately, and then use it in if)
  • where condition is wrong. Never, ever name parameters the same as column names. This: where camperid = camperid is equal to where 1 = 1 and you'd get all rows from the table, not just the one you're looking for

CodePudding user response:

Without table definitions (ddl) and sample data it is difficult to address your issues. From your description and code it appears you have a single table; but reading from a design perspective I see at least 3 tables. You have a long way to go. In the following I will essentially just annotate your code (with some format changes, but only format, and of the the annotations). I will then show one on many possible solutions, keeping the code as succinct as possible.

create or replace 
procedure p_stored(                                   -- Bad name. Name should indicate what the procedure does.
          c_camperid   in charges.camperid%type
        , c_category   in charges.camperid%type
        , c_chargedate in charges.camperid%type
        , c_amt        in charges.camperid%type
        , c_message   out varchar2
        )
as  
    -- what do the various prefix values mean. Would be better (IMHO) to have a single 
    -- prefix indicating the use/location. My preference is l_ for local variables
    p_camperid   charges.camperid%type;
    c_cat        charges.category%type;
    a_amt        charges.amt%TYPE;
    c_date       charges.chargedate%type;
    p_error      varchar2(5);
          
begin
    v_error := 'False';              -- v_error not defined. Do you mean p_error? 
                                     -- really does not matter As it is not used other 
                                     -- setting its value ( multiple times) 
    select c.camperid, c.category
      into p_camperid, c_cat
      from charges c
     where c.camperid = c_camperid;
      
    if (p_camperid = c.camperid) then  -- this would always be true, if not Oracle would have  thrown   
       v_error := 'true';              -- a no_data_found exception on above select. that is if it were valid - it is not 
       c_message := 'Camper ' || c_camperid || ' exists';
     else
       v_error := 'False';
       c_message := 'Error: no such camper ID (' || c_camperid || ')';
    end if;
     
     if c_cat = c.category then        -- invalid refrence to table c.xxx does not exist after the Select 
        v_error := 'true';
        c_message := 'Category ' || c_cat || ' exists';
     else
        v_error := 'False';
        c_message := 'Error: no such charge category (' || c_cat || ')';
      end if;
      
    if (a_amt >0 and a_amt < 40) then               -- a_amt never populated so it is NULL 
        v_error := 'true';
        c_message := 'Amount ' || a_amt|| ' is with in range';
     else                                           -- because a_amt is null code falls into here every time
        v_error := 'False';
        c_message := 'amount must be >0 and no more than $ 40.';
     end if;
     
     -- on if below 
     --  Cannot refer to a table_name.column_name Those values need to be: 
     --   defined in the procedure
     --   populated via select or value set via assignment
     --   same applies above to c.xxx references above 
     -- Even so the comparision does NOT reference the amount value
     --   so if spent=50 and budget=100 and amount = 200 your test passes 
     if (campers.spent < campers.budget) then       
         c_message := 'Amount ' || a_amt|| ' is under budget';   -- if all syntax/logic errors/ corrected   
                                                                -- so evetything is valid you get this message every time.
     else 
         c_message := 'ERROR: insufficient funds';
     end;                                    -- add an end if before here. must terminate the last if statement.                              

Now a possible rewrite keeping the assumption that everything necessary exists in this single table. Probably/hopefully a bad assumption, but it can be worked with.

create or replace 
procedure validate_camper(
          p_camperid   in charges.camperid%type
        , p_category   in charges.camperid%type
        , p_chargedate in charges.camperid%type
        , p_amt        in charges.camperid%type
        , p_message   out varchar2
        )
as  
    -- define user exceptions 
    e_no_category      exception; 
    e_amt_range        exception; 
    e_budget_violation exception; 
    
    -- local constants
    k_nl constant varchar2(2) := chr(13) || chr(10);  -- New_Line: or as appropriate for your OS
    
    -- locak variables 
    l_charges_rec  charges%rowtype; 

begin  
    select *
      into l_charges_rec
      from charges c
     where c.camperid = p_camperid;

     if p_category  <>  l_charges_rec.category then
        raise e_no_category;
      end if;
      
    if p_amt < 0 or p_amt > 40 then
       raise e_amt_range;      
     end if;
     
     if l_charges_rec.spent   p_amt > l_charges_rec.budget then
        raise e_budget_violation; 
     end if;

    p_message := 'Camper Valid: No errors detected.';      
     
exception 
    when no_data_found then 
         p_message := 'Error: No such camper ID (' || p_camperid || ')';
         
    when e_no_category then 
         p_message := 'Error: No such charge category (' ||  p_category  || ')';   
    
    when e_amt_range then 
         p_message := 'Error: Amount must be >0 and no more than $ 40.';

    when e_budget_violation then 
         p_message := 'Error: Insufficient funds';
    
    when others then 
         dbms_output.put_line('Unexpected Error=>' || k_nl 
                              || DBMS_UTILITY.format_error_stack); --  better write to Error_Log table 
         raise_application_error(-20199
                                ,'Fatial Error occured in validate_camper.'   -- Process failed do not hide that from user
                                  || k_nl || 'Process Unsuccessful'           -- or just raise to show actual error   
                                );
end validate_camper; 
     

You may wish to read up on Oracle user defined exceptions.
I realize this is a homework assignment (at least hopefully) and you need to write a procedure. However, just to point out with proper Primary key, Foreign key, and Check constraints defined at the table level this can all be done without any additional code. I will leave those for you to investigate. (Hint: 3 tables).

Note: Not tested.

  • Related