How can I debug a "division by zero" error in postgresql - sql

I am using many different views and plpgsql functions/aggregates in a single SELECT. When I run this SELECT on certain data sets, I get a division by zero error. Unfortunately, I don't get any details where exactly the division by zero occurs.
Is there I good way to pinpoint the exact place where the problem occurs?

Running the same code in psql will yield more helpful information, like:
ERROR: division by zero
CONTEXT: PL/pgSQL function "mean_estimator_sfunc" line 10 during statement block local variable initialization

Post your function. Here's some good example from Oracle documentation on how to avoid your exception. Same can be done with SQL only:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28370/errors.htm
DECLARE
stock_price NUMBER := 9.73;
net_earnings NUMBER := 0;
pe_ratio NUMBER;
BEGIN
pe_ratio :=
CASE net_earnings
WHEN 0 THEN NULL
ELSE stock_price / net_earnings
end;
END;
/

Related

Modifying a value in a table using PL/SQL

I have only just begun to learn procedures in SQL. I have hit a bit of a wall and can't seem to come to a fix. I am trying to create a procedure that will allow me to pass the item description and a percentage of how much I would like to increase that items price. This is the code I have got so far:
CREATE OR REPLACE PROCEDURE ADJUST_PRICE(
pItemDesc IN ITEM.ItemDesc%TYPE,
percentage IN NUMBER)
IS
pItemPrice NUMBER;
incAmt NUMBER;
BEGIN
SELECT itemprice into pItemPrice
FROM item WHERE itemdesc LIKE '%pItemDesc%';
incAmt := (pItemPrice*percentage)/100;
pItemPrice := incAmt + pItemPrice;
UPDATE Item
SET ItemPrice = pItemPrice
WHERE ItemDesc LIKE '%pItemDesc%';
END;
The procedure will compile but will not accept my calling block:
BEGIN
ADJUST_PRICE('%Dish%', 10);
END;
The error report I receive:
Error report -
ORA-01403: no data found
ORA-06512: at "S270131.ADJUST_PRICE", line 8
ORA-06512: at line 2
01403. 00000 - "no data found"
*Cause: No data was found from the objects.
*Action: There was no data from the objects which may be due to end of fetch.
Any help would be greatly appreciated, Thankyou.
It's not advisable to hit the ITEM table twice. Firstly, in a multi-user system, the details could have changed between your initial query and the update. Secondly it's inefficient, as it does twice as much work as it needs to.
I would simplify it to something like this:
create or replace procedure adjust_price
( p_itemdesc in item.itemdesc%type
, p_percentage in number )
as
l_increase_factor number := p_percentage/100 + 1;
begin
update item i
set i.itemprice = i.itemprice * l_increase_factor
where itemdesc like '%'||p_itemdesc||'%' escape '\';
end;
I have included an escape character to allow callers to treat wildcard characters % and _ as literals if they need to.
I've converted the percentage to a multiplication factor, so for example 50 percent becomes 1.5, rather than multiplying the price by the percentage, dividing by 100, and adding the original price, as I find that clearer arithmetically, but that's just my personal preference.
You used a p prefix for one of your two parameters (pItemDesc) and also for a local variable (pItemPrice). Code becomes confusing if variables are named like parameters and parameters are named like variables, so I recommend choosing one naming strategy and sticking with it.
Notice that code is easier to follow, work with and fix if it is neatly laid out, so I strongly recommend formatting like a programmer.
PL/SQL doesn't support this kind of string interpolation that you have in mind. But just use your input parameter as a bind variable instead:
SELECT ItemPrice
INTO pItemPrice
FROM Item
WHERE ItemDesc LIKE pItemDesc;
You'll still get NO_DATA_FOUND exceptions if your procedure doesn't find anything. But you don't actually need the extra SELECT. Just run the UPDATE directly:
UPDATE Item
SET ItemPrice = (ItemPrice * percentage) / 100 + ItemPrice
WHERE ItemDesc LIKE pItemDesc;

Numeric overflow in plsql ORA-01426

I have to calculate this
select power((1+(100000/365)),365)-1 from dual
but always gets numeric overflow no matter what i have changed.
i tried with pls_integer, cast, to round the result.
Please help me
The formula is calculating the interest based on a daily compound rate. (I am assuming it is interest - it could equally be population growth or something else).
So the flat rate is 100,000 which equates to 10,000,000%. This is being divided by 365 to get a daily rate (about 27,000%) and then the power function causes compound interest to be applied.
But your initial interest rate is so huge that any compound growth is bound to blow up in a short period of time; 27,000% per day is a lot.
In this case the answer is about 10^890, if my calcuator serves me correctly.
So, in short, your formula is correct. Your parameters are wrong.
Rounding the returned value will not help as the error is occurring before returning the value to you (which is inside the power function). So there is no way that you can get rid if it until unless you Reduce the operands.
power((1+(100000/365)),365) reaches out to infinity. The variables used in power function cannot handle this bigger value.
The below version shows how it is reaching to infinity. Run it as a script.
SET SERVEROUTPUT ON
WITH FUNCTION to_power(A VARCHAR2,b VARCHAR2) RETURN varchar2 IS
pow VARCHAR2(32767):=1;
BEGIN
dbms_output.put_line('START..');
-----------------------------------------------------------
FOR I IN 1 .. ABS(b)
LOOP
dbms_output.put_line('IN ABS LOOP '||I);
pow:=A*pow;
IF pow='~' THEN
dbms_output.put_line('REACHED INFINITE..');
EXIT;
END IF;
END LOOP;
-----------------------------------------------------------
IF b < 0 THEN
dbms_output.put_line('FOUND NEGATIVE');
IF pow='~' THEN
dbms_output.put_line('WILL NOT DEVIDE 1 BY INFINITE, HENCE RETURNING 0');
RETURN 0;
END IF;
BEGIN
dbms_output.put_line('BEFORE DEVIDE');
RETURN 1/pow;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('ERROR OCCURED IN NEGATIVE DEVIDE');
RETURN 0;
END;
dbms_output.put_line('END OF NEGATIVE PROCESSING');
ELSE
dbms_output.put_line('RETURNING + VALUE');
RETURN pow;
END IF;
----------------------------------------------------------
END;
-------------------------------------
SELECT to_power(274,365) FROM dual

Trying to use a where statement in cursor creation (PL/SQL)

I'm trying to create a block that accepts input from a prompt and uses that input to filter the result set for the cursor. Keep in mind I'm a novice here so I maybe making a very routine mistake, and thank you for your help. My current code is below.
Set serveroutput on
DECLARE
ACCEPT a PROMPT “Please Enter a Date, eg. Format - 01 or 30"
datev char
datev := &a;
CURSOR cur_day_cursor IS
SELECT Arrival_Date Adate
FROM FLIGHT
WHERE TO_CHAR(Arrival_Date, ‘DD’) = datev;
cur_day_cursor_var cur_day_cursor%ROWTYPE;
BEGIN
OPEN Cur_day_cursor;
LOOP
Fetch Cur_day_cursor
INTO cur_day_cursor_var;
EXIT WHEN cur_day_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE (cur_day_cursor_var.Adate);
END LOOP;
IF cur_day_cursor%ISOPEN THEN
CLOSE cur_day_cursor;
END IF;
END;
The where statement is causing my errors, so I was thinking that I may have to let the cursor collect all the data and then filter it when displaying, but I'm not sure if I can even do that.
The error I keep receiving ERROR at line 9:-
ORA-06550: line 9, column 1:
PLS-00103: Encountered the symbol "WHERE" when expecting one of the following:
begin function pragma procedure subtype type
current cursor delete
exists prior
I don't know exactly why Oracle is reporting the error at the WHERE. Sometimes the parser gets pretty confused by bad syntax and doesn't point to the real problem. You have several syntax errors before the cursor definition.
ACCEPT is a SQLPlus command, not a PL/SQL statement. Move your ACCEPT line above the DECLARE.
Also, your variable declaration and initialization are incorrect. The assignment should be part of the declaration line; you need to provide a length for the CHAR datatype; and the substitution value should be in quotes to be treated as a string. A valid version of your lines would be:
datev char(2) := '&a';
I ran the same query as above, and got the results perfectly fine.
You have few syntax as well as logical error which I corrected in your query. The syntax error(s) are -
datev char
datev := &a;
You can't do such an initialization in PL/SQL. You probably have to complete it in a single line like below -
datev char := &a;
The logical mistake(s) are -
Why use a CHAR variable to store data when you know that the value being returned is NUMBER.
You expect numbers from 1-31; then why do you choose the default size of char which as 1. It will fail if you provide a 2-digit number
Even if you increase the size of CHAR to CHAR(2), you will not get results when the users enters a number like 1 or 01, because for character wise comparison, '1' != '1 '(Mark the extra space at the end, because of char(2)); and also '1' != '01'.
The only solution for above is to use a NUMBER datatype.
Now here I am posting my query which is similar to your query, with a change of column name and table name. Please replace with your required names and try -
(Take care not to execute the ACCEPT....) with the PL/SQL block. It should be done in the SQL prompt first and then the other DECLARE section should be run.
--ACCEPT a NUMBER PROMPT 'Please Enter a Date, eg. Format - 01 or 30 :'
--Run the above line first in SQL Prompt and then execute the rest as whole
DECLARE
datev NUMBER(2) := &a;
CURSOR cur_day_cursor IS
SELECT Ename, HireDate Adate
FROM Emp
WHERE TO_CHAR(HireDate, 'D') = datev;
cur_day_cursor_var cur_day_cursor%ROWTYPE;
BEGIN
OPEN Cur_day_cursor;
LOOP
Fetch Cur_day_cursor
INTO cur_day_cursor_var;
EXIT WHEN cur_day_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE (cur_day_cursor_var.Adate);
END LOOP;
IF cur_day_cursor%ISOPEN THEN
CLOSE cur_day_cursor;
END IF;
END;
/
It appears that the problem is that the single-quotes around ‘DD’ aren't single-quotes. It looks like the code was created in an editor which changes apostrophes into those special "look kind of like single quotes but aren't really" characters. Replace the original version of the WHERE clause with the following:
WHERE TO_CHAR(Arrival_Date, 'DD') = datev;
and I suspect you'll be fine.
And get yourself a good code editor. :-)

Package Errors. What Am I doing wrong?

I'm trying to create a package that works out the number of days between two dates, I'm aware I have probably got this miles wrong, I am really struggling to troubleshoot this. My knownledge is low on oracle, I'm still quite new to this. The package I've written is below, but I am getting the error shown at the bottom.
How do I resolve this?
CREATE OR REPLACE PACKAGE PACKNAME
AS
FUNCTION TIME_SCALE RETURN NUMBER;
END;
/
CREATE OR REPLACE PACKAGE BODY PACKNAME
AS closed_date := '28-APR-14'
FUNCTION TIME_SCALE RETURN NUMBER;
IS BEGIN
TRUNC(mrc.closed_date - mrc.open_date) AS days_difference FROM TASKS mrc;
END;
​
Error at line 2: PLS-00103: Encountered the symbol "=" when expecting one of the following: constant exception <an identifier> <a double-quoted delimited-identifier> table long double ref char time timestamp interval date binary national character nchar
I have substituted some of the names to make them clearer for you to read.
The function is to output the number of days it has taken for a task to be completed. The columns basically include the date_opened and date_closed in simple terms as well as a few others and a unique ID which I believe is a sequence.
Try this:
CREATE OR REPLACE PACKAGE PACKNAME
AS
FUNCTION TIME_SCALE
RETURN NUMBER;
END;
/
CREATE OR REPLACE PACKAGE BODY PACKNAME
AS
closed_date VARCHAR2(50):= '28-APR-14';
days_difference NUMBER;
FUNCTION TIME_SCALE
RETURN NUMBER
IS
BEGIN
SELECT TRUNC(mrc.closed_date - mrc.open_date) INTO days_difference
FROM TASKS mrc;
RETURN days_difference;
END;
END;
What was wrong:
1) You have missed the type for closed_date
2) You had an ';' after RETURN NUMBER in function declaration
3) You have missed SELECT clause inside function
4) You have missed END for the package

oracle stored procedure - local variable behaving mysteriously

This is how I am delcaring the local variables:
team_counter number (38) := 0;
username varchar2(50) := '';
This is how I am trying to use/see their value after using some select into statement:
dbms_output.put_line(team_counter||'.'||username);
if team_counter< 30 AND username <>'' then
begin
dbms_output.put_line('yuhj');
end;
end if;
The second output is not being printed!
The first output is being printed as '1.tuser' which I was expecting.
This is because you're trying to compare a string with a 0 length string using an inequality operator.
Oracle assumes that 0 length strings are equivalent to NULL and will not evaluate comparisons that don't use the NULL specific conditional. To quote:
Oracle Database currently treats a character value with a length of
zero as null. However, this may not continue to be true in future
releases, and Oracle recommends that you do not treat empty strings
the same as nulls.
Simply put this means that your IF statement should be:
if team_counter < 30 and username is not null then
...
As an additional note there's no need for the begin ... end around the dbms_output.put_line. As you're not catching any exceptions explicitly related to this call or declaring additional variables etc there's no real need.