Alternate to DECODE like function to be used in elsif statement in PLSQL procedure - oracle

I am trying to use a condition elsif where something like decode needs to be used so that the condition is true and insertion is made. I am doing this in a procedure and condition is like
elsif ((v_DIVIDEND/Divisor)-1 < ABS(0.2)) then
insert into table(Divisor,b,c) values(Dividend,y,z);
It works fine when the divisor is not zero but when the divisor is zero it fails. I want to rule out zeroes in divisor using like another nested if condition within elsif or something like decode. I tried another if but syntax seemed to be wrong. Using Decode says it can only be used in SQL statement. Any suggestions, please...

you can also handle the "divisor equal to zero" exception and made the insert if the error occurs, see samples code in handling "divisor equal to zero" exception,
Sample 1: This sample defines a divisor_equal_to_zero exception
DECLARE
divisor_equal_to_zero EXCEPTION;
PRAGMA EXCEPTION_INIT(divisor_equal_to_zero, -1476);
v_divisor NUMBER := 0;
v_quotient NUMBER;
BEGIN
v_quotient := 1/v_divisor;
DBMS_OUTPUT.PUT_LINE('Print A: '||v_quotient);
EXCEPTION
WHEN divisor_equal_to_zero THEN
v_divisor := 1;
v_quotient := 1/v_divisor;
DBMS_OUTPUT.PUT_LINE('Print B: '||v_quotient);
--you can put the insert statement here
END;
/
Sample 2: This sample use the pre-defined ZERO_DIVIDE exception
DECLARE
v_divisor NUMBER := 0;
v_quotient NUMBER;
BEGIN
v_quotient := 1/v_divisor;
DBMS_OUTPUT.PUT_LINE('Print A: '||v_quotient);
EXCEPTION
WHEN ZERO_DIVIDE THEN
v_divisor := 1;
v_quotient := 1/v_divisor;
DBMS_OUTPUT.PUT_LINE('Print B: '||v_quotient);
--you can put the insert statement here
END;
/

Well, depending on what you want to get as a result when divisor = 0 (i.e. something large or something small), you can use something like this:
For small result (divide by 1E99):
case when divisor = 0 then 1E99 else divisor end
For large result (divide by 1E-99)
case when divisor = 0 then 1E-99 else divisor end
As of you being unable to use DECODE: that's correct, it can be used ONLY within the SELECT statement. It means that you should rewrite your code and put everything into SELECT (which is probably a bad idea). So - try CASE.

Maybe you could add a condition like this to check if Divisor is zero.
elsif ( Divisor !=0 ) and ((v_DIVIDEND/Divisor)-1 < ABS(0.2))
THEN
insert into table(Divisor,b,c) values(Dividend,y,z)
elsif Divisor = 0
THEN
insert into table(Divisor,b,c) values(?,?,?);

You can try nullif:
declare
divisor constant integer := 0;
result number;
begin
result := 100 / nullif(divisor,0);
end;
Here divisor is replaced with null if it has the value 0, giving the result as null.

Related

Finding the exponent (without using POW or built in functions) of a number using a procedure and/or Function

I am trying to create a way to find the exponent of a number (in this case the base is 4 and the exponent 2 so the answer should be 16) using a procedure without using the POW Function or any built in functions to find the exponent. Eventually I would like to take input numbers from the user.
set serveroutput on;
CREATE OR REPLACE PROCEDURE Exponent(base number, exponent number) as
answer number;
BEGIN
base := 4;
exponent := 2;
LOOP
IF exponent > 1 THEN
answer := base * base;
END IF;
END LOOP;
dbms_output.put_line('Answer is: ' || answer);
END;
/
Error(7,25): PLS-00103: "expression 'BASE' cannot be used as an assignment target" and "expression 'EXPONENT' cannot be used as an assignment target"
Any ideas on how to solve the error and/or better ways of getting the exponent without using built-in functions like POW?
In your procedure base and exponent are input parameters and can't be changed. You've got a couple of options:
1) copy the parameters to variables internal to the procedure and manipulate those internal values, or
2) change the parameters to be input/output parameters so you can change them.
Examples:
1)
CREATE OR REPLACE PROCEDURE Exponent(pin_base number, pin_exponent number) as
base number := pin_base;
exponent number := pin_exponent;
answer number;
BEGIN
base := 4;
exponent := 2;
LOOP
IF exponent > 1 THEN
answer := base * base;
END IF;
END LOOP;
dbms_output.put_line('Answer is: ' || answer);
END;
2)
CREATE OR REPLACE PROCEDURE Exponent(base IN OUT number,
exponent IN OUT number) as
answer number;
BEGIN
base := 4;
exponent := 2;
LOOP
IF exponent > 1 THEN
answer := base * base;
END IF;
END LOOP;
dbms_output.put_line('Answer is: ' || answer);
END;
The best thing is that whatever Oracle provides as inbuilt functionality that serves the purpose in a best possible. (Almost all the times better then customized codes) Try to use EXP function. I have tried to make customized code per my understanding. Hope this helps.
CREATE OR REPLACE
FUNCTION EXP_DUMMY(
BASE_IN IN NUMBER,
EXPO_IN IN NUMBER)
RETURN PLS_INTEGER
AS
lv PLS_INTEGER:=1;
BEGIN
FOR I IN
(SELECT base_in COL1 FROM DUAL CONNECT BY level < expo_in+1
)
LOOP
lv:=lv*i.col1;
END LOOP;
RETURN
CASE
WHEN EXPO_IN = 0 THEN
1
ELSE
lv
END;
END;
SELECT EXP_DUMMY(2,4) FROM DUAL;

Oracle PL/SQL Bubble-Sorting - ORA-01403: no data found ORA-06512: at line 12

Here is the code:
declare
p_arr dbms_sql.Number_Table;
i pls_integer;
procedure do_sort(p_arr in out dbms_sql.Number_Table, p_asc in boolean default null, p_nulls_last in boolean default null) is
x pls_integer;
p_temp number;
begin
for i in 1..p_arr.COUNT-1
loop
for x in 2..p_arr.COUNT
loop
if p_arr(x) < p_arr(x-1)
then
p_temp := p_arr(x-1);
p_arr(x-1) := p_arr(x);
p_arr(x) := p_temp;
end if;
end loop;
end loop;
return;
end;
begin
p_arr(-1) := 0;
p_arr(0) := -2;
p_arr(1) := 10.1;
p_arr(2) := null;
p_arr(3) := 10.1;
p_arr(4) := -1;
do_sort(p_arr);
i := p_arr.first;
while i is not null loop
dbms_output.put_line('arr('||i||') = '||nvl(to_char(p_arr(i)), 'null')||';');
i := p_arr.next(i);
end loop;
end;
It gives me an error on line 12 - "No data found".
Respectively, the procedure "do_sort" on line 29 also fails.
Seems like the problem with nested loop, which I can't figure out for now.
When there is only "first-level" loop with some code in it, such as assigning new values to collection - it performs well.
Sorting block outside of procedure body also works.
Thanks in advance.
You're filling your number table with specific indexes:
p_arr(-1) := 0;
p_arr(0) := -2;
p_arr(1) := 10.1;
p_arr(2) := null;
p_arr(3) := 10.1;
p_arr(4) := -1;
But when you loop you are using index values from 1 to the count of elements, which is 6. There is no element with index 5 or 6, and when you try to refer to p_arr(5) there is no such element - hence the error. And you miss out those with indexes -1 and 0.
It works if you re-index your initial values:
p_arr(1) := 0;
p_arr(2) := -2;
p_arr(3) := 10.1;
p_arr(4) := null;
p_arr(5) := 10.1;
p_arr(6) := -1;
which then gets output:
arr(1) = -2;
arr(2) = 0;
arr(3) = 10.1;
arr(4) = null;
arr(5) = -1;
arr(6) = 10.1;
PL/SQL procedure successfully completed.
Notice what happens with your null value though... you cannot compare null with anything else, so p_arr(x) < p_arr(x-1) is undefined (but not true) when the null element is evaluated on both sides of that comparison. So, it doesn't move. You would need to decide where you want nulls to end up to determine how to modify the code to achieve that.
If you have a specific reason for starting your indexing at -1 instead of 1, you could still do that, and change the references inside your loop to be p_arr(x+2) etc., but it would be more confusing and error-prone. Or you coudl change your loop ranges to handle that instead:
for i in -1..p_arr.COUNT - 1 -- 2 less than previously, on each end of range
loop
for x in 0..p_arr.COUNT - 2 -- 2 less than previously, on each end of range
loop
... which gets the same result, using your original table population starting from index -1. Oracle Live SQL demo.
The indexes used inside the loops have to align with the indexes you use to populate the table, however you do it.

Not able to handle ORA-01476: “divisor is equal to zero”

I have a simple procedure in which I am using a cursor to fetch some valid items. Using these valid items, I am obtaining some values and doing a calculation on them. The code looks like this:
PROCEDURE p_loadanalyse
IS
qty_1 NUMBER;
qty_2 NUMBER := 0;
V_CALC Number := 0;
Item_No_Rec valid_items%Rowtype;
CURSOR c_validitem
IS
SELECT DISTINCT item_no
From valid_items A;
Begin
For Item_No_Rec In C_Validitem
Loop
SELECT ROUND(SUM(Qty1),2)
INTO qty_1 -- FCST_QTY_PCD
FROM qty1_table
WHERE item_No = item_No_rec.item_No;
SELECT SUM(Qty2)
INTO qty_2
FROM qty2_table
WHERE A.Item_No = Item_No_Rec.Item_No;
V_CALC := (QTY_1 /QTY_2)*100;
Dbms_Output.Put_Line('deviation' ||V_Deviation);
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
qty_1 := 0;
qty_2 := 0;
WHEN OTHERS THEN
DBMS_output.put_line('There is an error');
END;
The problem is obviously in the statement V_CALC := (QTY1 /QTY2)*100; when QTY2 is 0. So I try to handle it like:
IF QTY_2 <> 0 THEN
V_CALC := (QTY_1 /QTY_2)*100;
ELSE
V_CALC := 0;
END IF;
This surprisingly hangs my SQL Developer when I try to compile the procedure. And gives a timeout occurred while trying to lock object after 10-11 mins. What I did was not rocket science and Oracle should have behaved the way I wanted, but instead I had to think and try all other ways of handling this divide by zero thingy like:
Including EXCEPTION WHEN ZERO_DIVIDE THEN QTY_2 := 0;
Using CASE instead of IF
After trying all permutations and combinations for 3 days, I am out of ideas and reasons and still with no output. Any hints or suggestions are welcome.
I doubt SQL developer is hanging because of your if statement or a divide by zero error. You may want to see if other users are using the procedure or if there are any processes in the database that need to be killed.
This can be shown if you can recreate the procedure but with another name..
Close the procedure before recreating it. SQL Developer block opened procedures.
Additionaly, try create it with different name of package/procedure.
And to be sure it's not zero division problem, create procedure empty...

Replicating Excel's CHIDIST function in Oracle 11g

I'm attempting to replicate the functionality of an Excel workbook in Oracle. One of the formulae on the worksheet uses CHIDIST, which "Returns the one-tailed probability of the chi-squared distribution". I can't see a similar function in Oracle anywhere! Am I going to have to write my own CHIDIST function?
Here's a link to Excel's documentation on CHIDIST: http://office.microsoft.com/en-gb/excel-help/chidist-HP005209010.aspx
I've already worked out the Chi Square and Degrees of Freedom.
Thanks
If you're using CHIDIST to do a chi-squared test, the STATS_CROSSTAB function might be a different means to the same end. It will calculate the chi-squared value, significance, or degrees of freedom for two sets of paired observations.
Right tail probability... nice...
In oracle you can embedded java code into stored procedures and functions. The best way is to
use proper java class and call it from oracle's function. It is not that hard:
http://docs.oracle.com/cd/B19306_01/java.102/b14187/chfive.htm
http://jwork.org/scavis/api/doc.php/umontreal/iro/lecuyer/probdist/ChiDist.html
That is all I can do for you:
DECLARE
l_value_to_evaluate NUMBER (10, 3) := 18.307; /* X; Value at which you want to evaluate the distribution */
l_degrees_freedom NUMBER := 10; /* Degrees of freedom */
l_number NUMBER;
FUNCTION is_number(str_in IN VARCHAR2) RETURN NUMBER
IS
n NUMBER;
BEGIN
n := TO_NUMBER(str_in);
RETURN 1;
EXCEPTION
WHEN VALUE_ERROR THEN
RETURN 0;
END;
BEGIN
-- If either argument is nonnumeric, CHIDIST returns the #VALUE! error value.
l_number := is_number(l_value_to_evaluate);
l_number := is_number(l_degrees_freedom);
-- If x is negative, CHIDIST returns the #NUM! error value.
IF SIGN(l_value_to_evaluate) = -1 THEN
RAISE_APPLICATION_ERROR(-20998, '#NUM!');
END IF;
-- If degrees_freedom is not an integer, it is truncated.
l_degrees_freedom := TRUNC(l_degrees_freedom);
-- If degrees_freedom < 1 or degrees_freedom > 10^10, CHIDIST returns the #NUM! error value.
IF l_degrees_freedom < 1
OR l_degrees_freedom > POWER(10, 10) THEN
RAISE_APPLICATION_ERROR(-20997, '#NUM!');
END IF;
-- CHIDIST is calculated as CHIDIST = P(X>x), where X is a χ2 random variable.
/* Here the integral's implementation */
EXCEPTION
WHEN VALUE_ERROR THEN
RAISE_APPLICATION_ERROR(-20999, '#VALUE!');
END;

Concatenation with Zero is not occurring properly in code?

I was trying to reverse a number in PL/SQL. It's working fine, but when my number contains any 0, the output is unexpected. For example:
1234 output 4321
1000 output 1
1203 ouput 3021
10001 output 1
DECLARE
r number(9);
num number(9):=&p_num;
BEGIN
WHILE num>=1 LOOP
IF mod(num,10)=0 THEN -- extracting last digit of a number .
r:=r||0; --if end digit is 0 then concat r with 0
ELSE
r:=r||mod(num,10);--if mod is not 0 then it would be last digit.
END IF;
num:=trunc(num/10);--Removing last digit from number
END LOOP;
dbms_output.put_line(r);
END;
Try changing the type of the variable "r" to varchar2.
Since it's declared as number, leading zeros are discarded.
'Reversing a number' is fundamentally a string operation, not a numerical one. Numerically, the reverse of 10, 100, 1000, etc are all 1 - the leading zeroes in the result don't count. And the operation is not, therefore, invertible; all numbers with the same leading (significant) digits and with zero or more trailing zeroes produce the same output when reversed.
So, you need to revise your code to generate a character string, not a number.
You can't preserve leading zeros with numbers; you must use strings (varchar2). Try something like this to see:
DECLARE
r varchar2(9);
num varchars(9):=&p_num;
BEGIN
r := REVERSE(num);
dbms_output.put_line(r);
END;
I'm not sure what's going wrong in your code Vineet but perhaps this will work.
DECLARE
newStr varchar2(9) = "";
numStr varchar2(9) := to_char(&p_num);
i number;
BEGIN
i = length(numStr);
WHILE i>0 LOOP
newStr := newStr || substr(numStr, i, i + 1);
i = i - 1;
END LOOP;
dbms_output.put_line(r);
END;
Edit: Or as gabe correctly points out, just use the REVERSE function.
The problem is you're dealing with a NUMBER value. When you reverse 1000, you get 0001, which when output unformatted is 1.
What you really need is something akin to:
CREATE OR REPLACE FUNCTION rev(p_num NUMBER)
RETURN VARCHAR2 IS
v_chr VARCHAR2(50);
BEGIN
v_chr := p_num;
IF LENGTH(v_chr) > 1 THEN
RETURN SUBSTR(v_chr, -1, 1)||rev(SUBSTR(v_chr, 1, LENGTH(v_chr)-1));
END IF;
RETURN v_chr;
EXCEPTION
WHEN OTHERS THEN
RETURN 'Bad Input';
END;
/
Function created
SQL> SELECT rev(100000) FROM dual;
REV(100000)
--------------------------------------------------------------------------------
000001
SQL>

Resources