Here is my sql code for a function that calculates the area of a circle. I am having trouble getting a dbms_output to show the user defined exception.
I begin by declaring an exception underzero. then i raise it if the user inputs a number less than or equal to 0. In the exception i have dbms_output then return the result. It shows in the query result that the value is null, however the output isn't showing.
I have already set serveroutput on and set verify off. I don't know why it wont output anything. If i run that line alone it outputs to the dbms output window but not when the exception is raised.
create or replace function circle_area
(p_radius number)
return number
is
c_Pi Constant number := acos(-1);
v_result number(10, 2);
underzero exception;
begin
if p_radius <=0 then
raise underzero;
else
v_result := c_pi * p_radius * p_radius;
return v_result;
end if;
exception
when underzero then
dbms_output.put_line('enter number greater than 0');
return v_result;
when others then
dbms_output.put_line('Exception Location: Anonymous Block');
dbms_output.put_line(sqlcode || ': ' || sqlerrm);
return v_result;
end circle_area;
to call the function:
select circle_area(-2) from dual;
I'm not sure that I understand the problem. The code you posted will generate output to dbms_output if serveroutput is enabled...
SQL> set serveroutput on;
SQL> create or replace function circle_area
2 (p_radius number)
3 return number
4 is
5 c_Pi Constant number := acos(-1);
6 v_result number(10, 2);
7 underzero exception;
8 begin
9 if p_radius <=0 then
10 raise underzero;
11 else
12 v_result := c_pi * p_radius * p_radius;
13 return v_result;
14 end if;
15
16 exception
17 when underzero then
18 dbms_output.put_line('enter number greater than 0');
19 return v_result;
20 when others then
21 dbms_output.put_line('Exception Location: Anonymous Block');
22 dbms_output.put_line(sqlcode || ': ' || sqlerrm);
23 return v_result;
24 end circle_area;
25 /
Function created.
SQL> select circle_area(-2) from dual;
CIRCLE_AREA(-2)
---------------
enter number greater than 0
I'm assuming that this is part of a homework assignment and not a real problem that you're facing. In reality, you'd never write code whose primary purpose was to write to dbms_output and you would never, ever have an exception handler that only wrote to dbms_output.
Related
Please find the error and rectify this code
This is a PL/SQL function to check whether a number is prime or not
By using the flag method instead of using count to check the requirement, it's working perfectly but not for this method.
create or replace function isprime(x in number)
RETURN number
IS
i int;
count int;
BEGIN
count:=0;
for i in 2..x/2 LOOP
if mod(x,i)=0 then
count:=count+1;
end if;
end loop;
return count;
end;
/
Warning: Function created with compilation errors.
DECLARE
n int;
c int;
BEGIN
n:=&n;
c:=isprime(n);
if c=0 then
dbms_output.put_line(n||'is a prime number');
else
dbms_output.put_line(n||'is not prime');
end if;
end;
/
Enter value for n: 5
old 5: n:=&n;
new 5: n:=5;
c:=isprime(n);
*
ERROR at line 6:
ORA-06550: line 6, column 4:
PLS-00905: object SYSTEM.ISPRIME is invalid
ORA-06550: line 6, column 1:
PL/SQL: Statement ignored
Here is the error list:
SQL> show errors
Errors for FUNCTION ISPRIME:
LINE/COL ERROR
-------- -----------------------------------------------------------------
10/1 PL/SQL: Statement ignored
10/8 PLS-00204: function or pseudo-column 'COUNT' may be used inside a
SQL statement only
13/1 PL/SQL: Statement ignored
13/8 PLS-00204: function or pseudo-column 'COUNT' may be used inside a
SQL statement only
COUNT is reserve sql word, have a look at this as an alternative solution for this task.
DECLARE
value_ NUMBER default :A;
i NUMBER DEFAULT 5;
is_prime VARCHAR2(5 CHAR);
BEGIN
IF value_ = 2 OR value_ = 3 THEN
is_prime := 'TRUE';
ELSIF MOD(value_,2)=0 OR MOD(value_,3)=0 OR value_ <= 1 THEN
is_prime := 'FALSE';
END IF;
WHILE POWER(i,2) <= value_ LOOP
IF MOD(value_,i)=0 OR MOD(value_,i + 2)=0 THEN
is_prime := 'FALSE';
exit;
END IF;
i := i + 6;
end loop;
is_prime := nvl(is_prime,'TRUE');
DBMS_Output.Put_Line(is_prime||' IT IS '||CASE is_prime WHEN 'FALSE' THEN 'NOT' END||'A PRIME NUMBER');
END;
I am trying to write a procedure that will produce the following output
exec WinOrLose(4)
Welcome to the Win or Lose Game. Your number is 4.
You win.
You lose.
You win.
You lose.
==> You lose!
So far I have this:
CREATE or REPLACE Procedure WinOrLose (
p_choice number ) AS
v_answer number;
DECLARE
v_answer := p_choice
BEGIN
dbms_output.put_line ('Welcome to the Win or Lose Game. Your number is ' ||
v_answer);
FOR v_answer in 1..10
IF MOD(v_answer, 2) = 0 THEN -- v_answer is even
dbms_output.put_line (You lose)
END;
/
I'm unsure of where to go from there. My thought process (psuedocode) is this:
SET v_answer := 1
While Loop (outside)
MOD(v_answer,2) = 0 then dbms.output (YOU LOSE)
ELSE
dbms.output (YOU WIN)
end if;
v_answer := p_choice
CREATE or REPLACE Procedure WinOrLose (
p_choice number ) AS
BEGIN
dbms_output.put_line ('Welcome to the Win or Lose Game. Your number is ' ||
p_choice);
FOR v_counter in 1..p_choice LOOP
IF (MOD(v_counter, 2) = 0)
THEN
dbms_output.put_line ('You win');
ELSE
dbms_output.put_line ('You lose');
END IF;
END LOOP;
IF (MOD(p_choice , 2) = 0)
THEN
dbms_output.put_line ('==> You win!');
ELSE
dbms_output.put_line ('==> You lose!');
END IF;
END;
/
I have multiple stored procedures calling multiple stored procedures in my database. To give a small example, I've constructed a fictionalised version of a few of them below. In my example, a Java program calls calculate_bill, which calls calculate_commission, which calls update_record.
I'm hoping to get some advice on how best to propagate the error messages up the stack to the calling Java layer, so the user gets a precise error message corresponding to wherever the error has occurred.
I'm really quite stuck on this. I've played around in my example with raise_application_error and just continually shuffling it up the stack. Is the way I'm doing it below remotely correct? Or is one raise_application_error in the relevant procedure enough, with no pragma exception init etc needed?
To give an idea of what I mean, in the example below, if a user entered a number which corresponded to a record which couldn't be updated because it didn't exist, I'd like them to get the message:
"Error calculating bill. Error calculating commission. No record exists to be updated" or something to that effect.
So two questions:
What is the best practice, most efficient, most tidy way to pass error messages up the stack for the end user in the application layer?
Does anyone have any suggestions on tidier output from the code, i.e. the best way to concatenate these errors to make them more meaningful? I'm really open to any suggestions on how to make this work best as I have absolutely no prior experience in this.
Example:
(Error in code):
-20000 : Error in top level procedure
-20001 : Error in middle level procedure
-20002 : Error in bottom level procedure
Java code:
try {
// call calculate_bill
exception (SQLException ex)
// output oracle code and relevant message.
Oracle code:
create or replace procedure calculate_bill(in_num NUMBER)
is
error_calculating_commission EXCEPTION;
error_updating_record EXCEPTION;
PRAGMA EXCEPTION_INIT (error_calculating_commission, -20001);
PRAGMA EXCEPTION_INIT (error_updating_record , -20002);
begin
if in_num > 2 then
calculate_commission(in_num);
else
raise_application_error(-20000, 'Error calculating bill. ' || 'Record number doesn''t exist.', false);
end if;
exception
when error_calculating_commission then
raise_application_error(SQLCODE, 'Error calculating bill. ' || SQLERRM, false);
when error_updating_record then
raise_application_error(SQLCODE, 'Error calculating bill. ' || SQLERRM, false);
when others then
raise_application_error(-20000, 'Unknown error encountered calculating bill.', false);
end;
create or replace procedure calculate_commission(in_num NUMBER)
is
begin
if in_num < 30 then
raise_application_error(-20001, 'Number too small to calculate commission.', false);
elsif in_num >= 30 and < 40 then
declare
error_storing_record EXCEPTION;
PRAGMA EXCEPTION_INIT (error_storing_record , -20002);
begin
update_record(in_num);
exception
when error_storing_record then
raise_application_error(SQLCODE, 'Error calculating commission. ' || SQLERRM, false);
when others then
raise_application_error(-20001, 'Unknown error encountered calculating commission.', false);
else
raise_application_error(-20001, 'Number too large to calculate commission', false);
end if;
end;
create or replace procedure update_record(in_num NUMBER)
is
begin
//some SQL query with a where clause, where in_num equals something
exception
when no_data_found then
raise_application_error(-20002, 'No record exists to be updated', false);
when others then
raise_application_error(-20002, 'Unknown error encountered updating record.', false);
end if;
end;
Note: I know this example is a little contrived. I was just trying to keep it brief.
The way I would implement this is to use RAISE_APPLICATION_ERROR where the error actually originates, then leave it unhandled in the other layers (or, if you want to do logging in the database, catch it in the OTHERS section, then re-raise using RAISE rather than RAISE_APPLICATION_ERROR.
You're OTHERS sections are still perpetuating the problem raised in your previous question: when an unknown error occurs you're replacing it with a generic, unhelpful message. To paraphrase Tom Kyte, "a WHEN OTHERS that does not end in RAISE is a bug`.
With no logging in the database, I would re-write the provided code like this:
create or replace procedure calculate_bill(in_num NUMBER)
is
begin
if in_num > 2 then
calculate_commission(in_num);
else
raise_application_error(-20000, 'Error calculating bill. ' || 'Record number doesn''t exist.', false);
end if;
end;
create or replace procedure calculate_commission(in_num NUMBER)
is
begin
if in_num < 30 then
raise_application_error(-20001, 'Number too small to calculate commission.', false);
elsif in_num >= 30 and in_num < 40 then
update_record(in_num);
else
raise_application_error(-20001, 'Number too large to calculate commission', false);
end if;
end;
create or replace procedure update_record(in_num NUMBER)
is
v_stub number;
begin
select 1 into v_stub from dual where 1 = 0;
exception
when no_data_found then
raise_application_error(-20002, 'No record exists to be updated', false);
end;
Below is sample input and the stack traces they created:
exec calculate_bill(0)
ORA-20000: Error calculating bill. Record number doesn't exist.
ORA-06512: at "SCHEMANAME.CALCULATE_BILL", line 7
ORA-06512: at line 1
exec calculate_bill(10)
ORA-20001: Number too small to calculate commission.
ORA-06512: at "SCHEMANAME.CALCULATE_COMMISSION", line 5
ORA-06512: at "SCHEMANAME.CALCULATE_BILL", line 5
ORA-06512: at line 1
exec calculate_bill(35)
ORA-20002: No record exists to be updated
ORA-06512: at "SCHEMANAME.UPDATE_RECORD", line 8
ORA-06512: at "SCHEMANAME.CALCULATE_COMMISSION", line 7
ORA-06512: at "SCHEMANAME.CALCULATE_BILL", line 5
ORA-06512: at line 1
exec calculate_bill(100)
ORA-20001: Number too large to calculate commission
ORA-06512: at "SCHEMANAME.CALCULATE_COMMISSION", line 9
ORA-06512: at "SCHEMANAME.CALCULATE_BILL", line 5
ORA-06512: at line 1
If you were to add error logging to these procedures, it would be a simple matter of adding an OTHERS clause to the EXCEPTION sections:
WHEN OTHERS THEN
my_logging (SQLCODE, SQLERRM, DBMS_UTILITY.format_error_backtrace ());
RAISE;
The new exception created by your RAISE_APPLICATION_ERROR calls will be handled transparently by the RAISE.
I'm trying to create a function that will compute for factorial but it returns an error when I do a SELECT FACTORIAL('1') FROM DUAL;
It returns the heinous error:function returned without value. I tried adding an exception but it seems that it doesn't work either. Care to help?
CREATE OR REPLACE FUNCTION FACTORIAL(p_factorial INTEGER)
RETURN NUMBER
AS var_fnumber number(2);
ctr number(2);
var_contain number(2) := 1;
BEGIN
FOR ctr in 1..p_factorial
LOOP
BEGIN
var_contain := var_contain * ctr;
DBMS_OUTPUT.put_line(var_contain);
EXCEPTION
WHEN OTHERS THEN
RETURN 0;
END;
END LOOP;
END; --FACTORIAL;
/
You MUST return something in a function.
SQL> CREATE OR REPLACE
2 FUNCTION FACTORIAL(
3 p_factorial INTEGER)
4 RETURN NUMBER
5 AS
6 var_fnumber NUMBER(2);
7 ctr NUMBER(2);
8 var_contain NUMBER(2) := 1;
9 BEGIN
10 FOR ctr IN 1..p_factorial
11 LOOP
12 BEGIN
13 var_contain := var_contain * ctr;
14 END;
15 END LOOP;
16 RETURN var_contain;
17 END;
18 /
Function created.
SQL>
SQL> SELECT factorial(2) FROM dual
2 /
FACTORIAL(2)
------------
2
For more details, read http://lalitkumarb.wordpress.com/2014/05/01/ora-06503-plsql-function-returned-without-value/
CREATE OR REPLACE FUNCTION FACTORIAL(p_factorial INTEGER)
RETURN NUMBER
AS var_fnumber number;
ctr number;
var_contain number := 1;
BEGIN
FOR ctr in 1..p_factorial
LOOP
BEGIN
var_contain := var_contain * ctr;
EXCEPTION
WHEN OTHERS THEN
RETURN 0;
RETURN var_contain;
END;
END LOOP;
RETURN var_contain;
END; --FACTORIAL;
/
Was gonna say that I found it. Thank you for the answers.
As for the reason why I used varchar2, I wasn't finalizing it yet.
modified your code try this:
create or replace
FUNCTION FACTORIAL(p_factorial INTEGER)
RETURN VARCHAR2
AS
var_contain varchar2(50):= 1;
BEGIN
FOR ctr in 1..p_factorial
LOOP
var_contain := var_contain * ctr;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
RETURN O;
return var_contain;
I am new to PL/SQL... In Varray how can i get multiple value from IN parameter..... else Is there another ways to get the values...
I want to interate the values through VArray... if any other options then its fine..
coding:
CREATE OR REPLACE
PROCEDURE dynamic_query_build(
vr_plan_sku_id IN VARCHAR2 )
IS
type plan_sku_id_array IS VARRAY(999) OF VARCHAR2(5000);
plan_sku_id plan_sku_id_array;
total INTEGER;
vrx_plan_sku_id VARCHAR2(3000);
BEGIN
vrx_plan_sku_id:= REPLACE(vr_plan_sku_id,',',chr(39)||','||chr(39));
plan_sku_id := plan_sku_id_array(chr(39)||vrx_plan_sku_id||chr(39));
total := plan_sku_id.count;
FOR i IN 1 .. total
LOOP
dbms_output.put_line(plan_sku_id(i));
END LOOP;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
END dynamic_query_build;
Execution:
set serveroutput on;
declare
vr_plan_sku_id varchar2(200) := '5863314,5863315';
BEGIN
dynamic_query_build(vr_plan_sku_id);
END;
/
My Output:
anonymous block completed
'5863314','5863315'
Expected output:
5863314
5863315
now it is considering as single value....
I created anonymous block with procedure dynamic_query_build. Added there code, that will split VARCHAR2 variable into varray.
I think, the key to your question is this line - plan_sku_id.EXTEND();
You can extend varray dynamically, but only till it reaches defined maximum (in your case - 999).
DECLARE
vr_plan_sku_id varchar2(200) := '5863314,5863315';
PROCEDURE dynamic_query_build(
vr_plan_sku_id IN VARCHAR2 )
IS
type plan_sku_id_array IS VARRAY(999) OF VARCHAR2(5000);
plan_sku_id plan_sku_id_array;
total INTEGER;
position PLS_INTEGER := 0;
last_position PLS_INTEGER := 1;
tmp VARCHAR2(5000);
counter PLS_INTEGER := 1;
BEGIN
plan_sku_id := plan_sku_id_array();
LOOP
position := INSTR(vr_plan_sku_id, ',', last_position);
IF position > 0 THEN
tmp := SUBSTR(vr_plan_sku_id, last_position, position - last_position);
last_position := position + 1;
ELSE
tmp := SUBSTR(vr_plan_sku_id, last_position);
END IF;
plan_sku_id.EXTEND();
plan_sku_id(counter) := tmp;
counter := counter + 1;
EXIT WHEN position = 0 OR counter > 10;
END LOOP;
total := plan_sku_id.count;
FOR i IN 1 .. total
LOOP
dbms_output.put_line(plan_sku_id(i));
END LOOP;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001,'An error was encountered - '||SQLCODE||' -ERROR- '||SQLERRM);
END dynamic_query_build;
BEGIN
dynamic_query_build(vr_plan_sku_id);
END;
/
Put a replace in the dbms_output statement this will eliminate the quotes from the string
....
dbms_output.put_line replace (replace (plan_sku_id(i), '''' ))',',chr(10);
.....