ORA-01438: value larger than specified precision allowed for this column - oracle

Following is my code, I dont understand what I'm doing wrong. Any help will be greatly appreciated
CREATE OR REPLACE
PROCEDURE COMP_LATE_FEE(LATE_APT_FINE IN NUMBER, LATE_GRG_FINE IN NUMBER)
AS
DIFF NUMBER;
TYPE MBCUR IS REF CURSOR RETURN MONTHLY_BILL%ROWTYPE;
MONBILL MBCUR;
MBREC MONTHLY_BILL%ROWTYPE;
BEGIN
--DIFF := FLOOR(SYSDATE - (TRUNC(SYSDATE,'MM')));
--DBMS_OUTPUT.PUT_LINE(DIFF);
OPEN MONBILL FOR
-- checking the status of all last month's bills
SELECT * FROM MONTHLY_BILL
WHERE STATUS = 'PENDING' AND SYSDATE > ED_DT;
FETCH MONBILL INTO MBREC;
-- adding the late fee amount for any bills that are past the due date
-- due date = last day of the month
DIFF := FLOOR(ABS(MBREC.ED_DT - (TRUNC(SYSDATE,'MM'))));
UPDATE MONTHLY_BILL
SET LATE_FEE = DIFF * LATE_APT_FINE
WHERE BILL_NUM = MBREC.BILL_NUM;
-- if a garage is rented by the resident then the respective additional fee is included
IF (MBREC.GARAGE_RENT != 0) THEN
UPDATE MONTHLY_BILL
SET LATE_FEE = LATE_FEE + DIFF * LATE_GRG_FINE
WHERE BILL_NUM = MBREC.BILL_NUM;
END IF;
COMMIT;
CLOSE MONBILL;
END;
/
The procedure compiled without any err. But I get the following err when i call the proc
BEGIN
COMP_LATE_FEE(70,20);
END;
/
Error report:
ORA-01438: value larger than specified precision allowed for this column
ORA-06512: at "LALLURI.COMP_LATE_FEE", line 19
ORA-06512: at line 2
01438. 00000 - "value larger than specified precision allowed for this column"
*Cause: When inserting or updating records, a numeric value was entered
that exceeded the precision defined for the column.
*Action: Enter a value that complies with the numeric column's precision,
or use the MODIFY option with the ALTER TABLE command to expand
the precision.

Assuming that the statement at line 19 is this
UPDATE MONTHLY_BILL
SET LATE_FEE = DIFF * LATE_APT_FINE
WHERE BILL_NUM = MBREC.BILL_NUM;
the problem would appear to be that the result of the computation diff * late_apt_fine is too large for the late_fee column in the monthly_bill table. We know that late_apt_fine is 70 based on the value of the parameter that was passed in. Since we don't know the value of the diff variable when there is an error and we don't know the definition of the late_fee column in monthly_bill, it's hard to know whether the problem is that the definition of late_fee needs to be changed or whether the computed value is larger than you expect and the algorithm needs to be changed.

Related

hour must be between 1 and 12 - sql error or ORA-01830: date format picture ends before converting entire input string

I have a column with timestamp stored in 20-02-18 03:50:58.347000000 PM format. These timestamps are stored in multiple rows so I want to update only the milliseconds in this column by using random number generator so that it will be unique for every row. I tried to use below query for updating the time
UPDATE table
SET field = TO_TIMESTAMP('18-01-18 02:23:27.265000050 PM'|| ' ' || TO_TIMESTAMP(field, 'HH:MI:SS.FF'),'DD-MM-YY HH:MI:SS.FF')
where objid = XXX;
I'm getting an error - ORA-01849: hour must be between 1 and 12
if I change the query to below format -
ow query for updating the time
UPDATE table
SET field = TO_TIMESTAMP('18-01-18 02:23:27.265000050 PM'|| ' ' || TO_TIMESTAMP(field, 'hh12:MI:SS.FF'),'DD-MM-YY hh12:MI:SS.FF')
where objid = XXX;
I get below error -
ORA-01830: date format picture ends before converting entire input string. Can anyone check this please
Don't pfaff around with date masks. Just use INTERVAL to add milliseconds:
update t23
set ts = ts + interval '0.001' second * dbms_random.value(0,999)
Here is a demo on db<>fiddle.
using random number generator so that it will be unique for every row.
Not guaranteed. A random series can still contain duplicate numbers. However, if you have only a few timestamps per second it's unlikely you will get any clashes. Likelihood of collisions increases with the number of timestamps per second. So if uniqueness is the object of the exercise this is the wrong approach. You need a different key to uniquely identify your records (probably a technical key such as a sequence or identity column).

Updating more than 1 row - Oracle SQL Procedure

I was wondering if it was possible to update more than 1 row with a procedure, im not sure why this one isnt working. Its working only if theres only 1 row in my table. But if there's more than 1 row i get the usual error message :
ORA-01422: exact fetch returns more than requested number of rows
I'm honestly not sure why this isnt working. Is it possible a procedure cannot update more than 1 row at once ?
create or replace procedure TP3_SP_ARCHIVER_ENCAN
is
V_CURRENT_DATE date;
V_DATE_ENCAN date;
begin
select sysdate, DATE_FIN_ENC into V_CURRENT_DATE, V_DATE_ENCAN
from
TP2_ENCAN;
update TP2_ENCAN
set EST_ARCHIVEE_ENC = 1,
STATUT_ENC = 'Archivé'
where V_CURRENT_DATE - V_DATE_ENCAN > 60;
end TP3_SP_ARCHIVER_ENCAN;
/
I'm excepting to archive every ENCAN that has been closed for a duration of 60+ days. everytime i run this procedure i just want to update those.
Full error message :
Error report -
ORA-01422: exact fetch returns more than requested number of rows
ORA-06512: at "C##JALAC144.TP3_SP_ARCHIVER_ENCAN", line 8
ORA-06512: at line 1
01422. 00000 - "exact fetch returns more than requested number of rows"
*Cause: The number specified in exact fetch is less than the rows returned.
*Action: Rewrite the query or change number of rows requested
This line is your problem:
select sysdate, DATE_FIN_ENC into V_CURRENT_DATE, V_DATE_ENCAN from TP2_ENCAN;
You are selecting DATE_FIN_ENC into a scalar variable that can hold exactly one value. You can select "1" into "x". You can't select "1" and "2" into "x" at the same time. So you get the error you're getting.
If I understand your problem correctly, you probably want this, with no initial select:
update TP2_ENCAN
set EST_ARCHIVEE_ENC = 1,
STATUT_ENC = 'Archivé'
where SYSDATE - DATE_FIN_ENC > 60;
Via your code you just want to update record base on current date. Therefore, you do not need to use parameter. Using the update script is enough.
Create or replace procedure TP3_SP_ARCHIVER_ENCAN is:
begin
update TP2_ENCAN
set EST_ARCHIVEE_ENC = 1,
STATUT_ENC = 'Archivé'
where sysdate - DATE_FIN_ENC > 60;
end
TP3_SP_ARCHIVER_ENCAN;

Store big number in Oracle -- Please give an example that can store 9e125

The Oracle doc says one can store a number up to 9.99...9 x 10125 with up to 38 significant digits: https://docs.oracle.com/cd/B28359_01/server.111/b28318/datatype.htm#i16209.
I tried this:
create table bigtest (t number(38,2));
insert into bigtest values (5e40);
But I got
[Error] Execution (8: 29): ORA-01438: value larger than specified precision allowed for this column
It is supposed to be able to store 9.99e125, right? Could any one give an example on how to store 9.99e125?
See DBfiddle here (Oracle 18c).
create table T1 (
anumber number
) ;
insert into t1 ( anumber ) values ( 9.99e125 ) ;
select * from t1 ;
ANUMBER
999000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
One way is to use the number data type without precision and scale specified.
You can specify precision and scale for very large (and also for very small) numbers though. Just keep in mind that negative scale means "that many zeros at the end of an integer" - the total number of digits can be up to precision + absolute value of scale.
In the example below, note that 38 + 84 = 122. The scale must be between -84 and 127, which means that if you do use precision and scale, you can only store numbers < 1e123 - a smaller range than for the full number data type, but still storing very large numbers
create table tbl(x number(38,-84));
insert into tbl values (3.493e121);
select x from tbl;
X
----------
3.4930E+121

Force Oracle to process one row at a time

I have a query that in the select statement uses a custom built function to return one of the values.
The problem I have is every now and then this function will error out because it returns more than one row of information. SQL Error: ORA-01422: exact fetch returns more than requested number of rows
To further compound the issue I have checked the table data within the range that this query should be running and can't find any rows that would duplicate based on the where clause of this Function.
So I would like a quick way to identify on which Row of the original query this crashes so that I can take the values from that query that would be passed into the function and rebuild the Functions query with these values to get it's result and see which two or more rows are returned.
Any ideas? I was hoping there could be a way to force Oracle to process one row at a time until it errors so you can see the results UP to the first error.
Added the code:
FUNCTION EFFPEG
--Returns Effective Pegged Freight given a Effdate, ShipTo, Item
DATE1 IN NUMBER -- Effective Date (JULIANDATE)
, SHAN IN NUMBER -- ShipTo Number (Numeric)
, ITM IN NUMBER -- Short Item Number (Numeric)
, AST IN VARCHAR -- Advance Pricing type (varchar)
, MCU IN VARCHAR Default Null --ShipFrom Plant (varchar)
) RETURN Number
IS
vReturn Number;
BEGIN
Select ADFVTR/10000
into vReturn
from PRODDTA.F4072
where ADEFTJ <= DATE1
and ADEXDJ >= DATE1
and ADAN8 = SHAN and ADITM = ITM
and TRIM(ADAST) = TRIM(AST)
and ADEXDJ = (
Select min(ADEXDJ) ADEXDJ
from PRODDTA.F4072
where ADEFTJ <= DATE1
and ADEXDJ >= DATE1
and ADAN8 = SHAN
and ADITM = ITM
and TRIM(ADAST) = TRIM(AST));
Query that calls this code and passes in the values is:
select GLEXR, ORDTYPE,
EFFPEG(SDADDJ, SDSHAN, SDITM, 'PEGFRTT', SDMCU),
from proddta.F42119
I think the best way to do it is trough Exceptions.
What you need to do is to add the code to handle many rows exception in your function:
EXCEPTION
WHEN TOO_MANY_ROWS THEN
INSERT INTO ERR_TABLE
SELECT your_columns
FROM query_that_sometimes_returns_multiple_rows
In this example the doubled result will go to separated table or you can decide to simply print out with dbms_output.
An easy page to start can be this, then just google exception and you should be able to find all you need.
Hope this can help.

How to handle to_date exceptions in a SELECT statment to ignore those rows?

I have the following query that I am attempting to use as a COMMAND in a crystal report that I am working on.
SELECT * FROM myTable
WHERE to_date(myTable.sdate, 'MM/dd/yyyy') <= {?EndDate}
This works fine, however my only concern is that the date may not always be in the correct format (due to user error). I know that when the to_date function fails it throws an exception.. is it possible to handle this exception in such a way that it ignores the corresponding row in my SELECT statement? Because otherwise my report would break if only one date in the entire database is incorrectly formatted.
I looked to see if Oracle offers an isDate function, but it seems like you are supposed to just handle the exception. Any help would be greatly appreciated. Thanks!!
Echoing Tony's comment, you'd be far better off storing dates in DATE columns rather than forcing a front-end query tool to find and handle these exceptions.
If you're stuck with an incorrect data model, however, the simplest option in earlier versions is to create a function that does the conversion and handles the error,
CREATE OR REPLACE FUNCTION my_to_date( p_date_str IN VARCHAR2,
p_format_mask IN VARCHAR2 )
RETURN DATE
IS
l_date DATE;
BEGIN
l_date := to_date( p_date_str, p_format_mask );
RETURN l_date;
EXCEPTION
WHEN others THEN
RETURN null;
END my_to_date;
Your query would then become
SELECT *
FROM myTable
WHERE my_to_date(myTable.sdate, 'MM/dd/yyyy') <= {?EndDate}
Of course, you'd most likely want a function-based index on the MY_TO_DATE call in order to make this query reasonably efficient.
In 12.2, Oracle has added extensions to the to_date and cast functions to handle conversions that error
SELECT *
FROM myTable
WHERE to_date(myTable.sdate default null on conversion error, 'MM/dd/yyyy') <= {?EndDate}
You could also use the validate_conversion function if you're looking for all the rows that are (or are not) valid dates.
SELECT *
FROM myTable
WHERE validate_conversion( myTable.sdate as date, 'MM/DD/YYYY' ) = 1
If your data is not consistent and dates stored as strings may not be valid then you have 3 options.
Refactor your DB to make sure that the column stores a date datatype
Handle the exception of string to date in a stored procedure
Handle the exception of string to date in a (complex) record selection formula
I would suggest using the first option as your data should be consistent.
The second option will provide some flexibility and speed as the report will only fetch the rows that are needed.
The third option will force the report to fetch every record in the table and then have the report filter down the records.
I have the same problem... an old legacy database with varchar fields for dates and decades of bad data in the field. As much as I'd like to, I can't change the datatypes either. But I came up with this solution to find if a date is current, which seems to be what you're doing as well:
select * from MyTable
where regexp_like(sdate, '[0-1][0-9].[0-3][0-9].[0-9][0-9][0-9][0-9]')
-- make sure it's in the right format and ignore rows that are not
and substr(sdate,7,10) || substr(sdate,1,2) || substr(sdate,4,5) >= to_char({?EndDate}, 'YYYYMMDD')
-- put the date in ISO format and do a string compare
The benefit of this approach is it doesn't choke on dates like "February 30".
Starting from Oracle 12c there is no need to define a function to catch the conversion exception.
Oracle introduced an ON CONVERSION ERROR clause in the TO_DATE function.
Basically the clause suppress the error in converting of an invalid date string (typical errors are ORA-01843, ORA-01841, ORA-011861, ORA-01840) and returns a specified default value or null.
Example of usage
select to_date('2020-99-01','yyyy-mm-dd') from dual;
-- ORA-01843: not a valid month
select to_date('2020-99-01' default null on conversion error,'yyyy-mm-dd') from dual;
-- returns NULL
select to_date('2020-99-01' default '2020-01-01' on conversion error,'yyyy-mm-dd') from dual;
-- 01.01.2020 00:00:00
Solution for the Legacy Application
Let's assume there is a table with a date column stored as VARCHAR2(10)
select * from tab;
DATE_CHAR
----------
2021-01-01
2021-99-01
Using the above feature a VIRTUAL DATE column is defined, that either shows the DATE or NULL in case of the conversion error
alter table tab add (
date_d DATE as (to_date(date_char default null on conversion error,'yyyy-mm-dd')) VIRTUAL
);
select * from tab;
DATE_CHAR DATE_D
---------- -------------------
2021-01-01 01.01.2021 00:00:00
2021-99-01
The VIRTUAL column can be safely used because its format is DATE and if required an INDEX can be set up on it.
select * from tab where date_d = date'2021-01-01';
Since you say that you have "no access" to the database, I am assuming that you can not create any functions to help you with this and that you can only run queries?
If that is the case, then the following code should get you most of what you need with the following caveats:
1) The stored date format that you want to evaluate is 'mm/dd/yyyy'. If this is not the case, then you can alter the code to fit your format.
2) The database does not contain invalid dates such as Feb 30th.
First, I created my test table and test data:
create table test ( x number, sdate varchar2(20));
insert into test values (1, null);
insert into test values (2, '01/01/1999');
insert into test values (3, '1999/01/01');
insert into test values (4, '01-01-1999');
insert into test values (5, '01/01-1999');
insert into test values (6, '01-01/1999');
insert into test values (7, '12/31/1999');
insert into test values (8, '31/12/1999');
commit;
Now, the query:
WITH dates AS (
SELECT x
, sdate
, substr(sdate,1,2) as mm
, substr(sdate,4,2) as dd
, substr(sdate,7,4) as yyyy
FROM test
WHERE ( substr(sdate,1,2) IS NOT NAN -- make sure the first 2 characters are digits
AND to_number(substr(sdate,1,2)) between 1 and 12 -- and are between 0 and 12
AND substr(sdate,3,1) = '/' -- make sure the next character is a '/'
AND substr(sdate,4,2) IS NOT NAN -- make sure the next 2 are digits
AND to_number(substr(sdate,4,2)) between 1 and 31 -- and are between 0 and 31
AND substr(sdate,6,1) = '/' -- make sure the next character is a '/'
AND substr(sdate,7,4) IS NOT NAN -- make sure the next 4 are digits
AND to_number(substr(sdate,7,4)) between 1 and 9999 -- and are between 1 and 9999
)
)
SELECT x, sdate
FROM dates
WHERE to_date(mm||'/'||dd||'/'||yyyy,'mm/dd/yyyy') <= to_date('08/01/1999','mm/dd/yyyy');
And my results:
X SDATE
- ----------
2 01/01/1999
The WITH statement will do most of the validating to make sure that the sdate values are at least in the proper format. I had to break out each time unit month / day / year to do the to_date evaluation because I was still getting an invalid month error when I did a to_date on sdate.
I hope this helps.
Trust this reply clarifies...
there is no direct EXCEPTION HANDLER for invalid date.
One easy way is given below once you know the format like DD/MM/YYYY then below given REGEXP_LIKE function will work like a charm.
to_date() also will work, when invalid_date is found then cursor will goto OTHERS EXCEPTION. given below.
DECLARE
tmpnum NUMBER; -- (1=true; 0 = false)
ov_errmsg LONG;
tmpdate DATE;
lv_date VARCHAR2 (15);
BEGIN
lv_date := '6/2/2018'; -- this will fail in *regexp_like* itself
lv_date := '06/22/2018'; -- this will fail in *to_date* and will be caught in *exception WHEN OTHERS* block
lv_date := '07/03/2018'; -- this will succeed
BEGIN
tmpnum := REGEXP_LIKE (lv_date, '[0-9]{2}/[0-9]{2}/[0-9]{4}');
IF tmpnum = 0
THEN -- (1=true; 0 = false)
ov_errmsg := '1. INVALID DATE FORMAT ';
DBMS_OUTPUT.PUT_LINE (ov_errmsg);
RETURN;
END IF;
tmpdate := TO_DATE (lv_date, 'DD/MM/RRRR');
--tmpdate := TRUNC (NVL (to_date(lv_date,'DD/MM/RRRR'), SYSDATE));
tmpnum := 1;
EXCEPTION
WHEN OTHERS
THEN
BEGIN
tmpnum := 0;
ov_errmsg := '2. INVALID DATE FORMAT ';
DBMS_OUTPUT.PUT_LINE (ov_errmsg || SQLERRM);
RETURN;
END;
-- continue with your other query blocks
END;
-- continue with your other query blocks
DBMS_OUTPUT.PUT_LINE (tmpnum);
END;

Resources