How to print record that caused ORA-01843 error? - sql

I have a table with millions of records. I am trying to format one column data to DATE format
which is currently in VARCHAR2. But, I am getting ORA-01843 not a valid month error.
I am trying to get those records which are causing ORA-01843
CREATE OR REPLACE PROCEDURE pr_TEST_CUSTOM_ORA1843 AS
v_company MyTable.MyColumn%TYPE;
BEGIN
BEGIN
SELECT to_char(to_date(TRIM(MyColumn), 'YYMMDD'), 'MM/DD/YYYY')
INTO v_company FROM MyTable;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ORA-01843 caused by'||v_company);
END;
END pr_TEST_CUSTOM_ORA1843;
But, the value of v_company is not printed.
How to get the records which are causing ORA-01843 error?

I just commented on your previous question : How to format only those records for which ORA-01843 is not thrown? but you did not pay attention to it.
Create a function which checks if it is a valid date like this.
CREATE OR REPLACE FUNCTION validmmddyyyy (p_str IN VARCHAR2)
RETURN NUMBER
AS
V_date DATE;
BEGIN
V_Date := TO_DATE (p_str, 'MM/DD/YYYY');
RETURN 1;
EXCEPTION
WHEN OTHERS
THEN
RETURN 0;
END;
Then, to select the records which fail, you can run a query
select MyColumn FROM MyTable where validmmddyyyy(MyColumn) = 0
When you are lucky enough to use Oracle 12c R2, you could make use of DEFAULT..ON..CONVERSION ERROR clause of TO_DATE
SELECT *
FROM MyTable
WHERE TO_DATE (MyColumn default null on conversion error,'MM/DD/YYYY') IS NULL
An important advice as always, don't use a VARCHAR2 / CHAR type for DATEs in database.

Related

Oracle PL/SQL error - store procedure - ORA-00979 and ORA-06512

I am using following store procedure to insert data to a table.
create or replace PROCEDURE PM
(
date1 in varchar2
,date2 in varchar2
,date3 in varchar2
) AS
cursor cur_cd is
(
select to_date(date1,'DD-MON-YY') as date1
,trim(t.DEPT_CODE) DEPT_CODE
,count(t.DEPT_CODE) count_dept
,sum(t.amount) amount
from department t
where t.date >= to_date(date2,'DD-MON-YY')
and t.date <= to_date(date2,'DD-MON-YY')
and t.dept_name like 'finance%'
and (trim(t.DT_code)='TR_01' or t.DT_file like 'DTF_20%')
and t.DEPT_CODE not in ('HR','ADMIN','ACADEMIC')
group by t.DEPT_CODE
);
Type rec_set is table of dept_file%rowtype;
v_rec_set record_set;
begin
open cur_cd;
loop
fetch cur_cd
bulk collect into v_rec_set limit 100;
exit when v_rec_set.count()=0;
begin
forall i in v_rec_set.first..v_rec_set.last
insert into dept_file
values v_rec_set(i);
end;
end loop;
close cur_cd;
exception when others then raise;
end PM;
It's giving me a runtime error when execute procedure. But the query execute without error manually.
ORA-000979 : not a GROUP BY expression
ORA-006512 : at "ABS.PM", line 9
Also, when hard code the parameters (date1, date2 and date3) the procedure working without error.
Can you please help me to resolve this error?
All non-aggregated columns must be specified in the GROUP BY clause. Therefore:
GROUP BY TO_DATE (date1, 'DD-MON-YY'), TRIM (t.dept_code)
By the way, are you really storing date values as strings? Why do I ask? Because you're using TO_DATE functions all over the code. If you are, then try not to do it in the future. Oracle offers DATE datatype, you should use it.

PLS00215: String length constraints must be in range (1..32767)

I am new to pl/sql. I want to create a procedure that has three parameters called 'startMonth', 'endMonth', 'thirdMonth'. In the procedure, I am executing a sql query which is in 'run_sql' column in table_query. Values for 'startMonth', 'endMonth', 'thirdMonth' are needed to this query. This is how I wrote the procedure. My plan is to put all the sql queries in a separate table and execute in the for loop in the procedure. There I am creating a table called table1 and in the next month I want to drop it and create the table again. This is how I have written the procedure.
CREATE OR REPLACE procedure schema.sixMonthAverage (startMonth varchar,endMonth varchar ,thirdMonth varchar )
IS
start_date varchar := startMonth;
end_date varchar := endMonth;
begin
for c_rec in(select run_sql from table_query)
loop
dbms_output.put_line(startmonth);
dbms_output.put_line(endmonth);
execute immediate c_rec.run_sql using start_date, end_date;
Execute IMMEDIATE 'commit';
END LOOP;
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('Exception');
END;
This is the query in the run_sql column in table_query.
create table table1
as
select account_num,bill_seq,bill_version,
to_char(start_of_bill_dtm,''YYYYMM-DD'') st_bill_dtm,
to_char(bill_dtm - 1,''YYYYMM-DD'') en_bill_dtm,
to_char(actual_bill_dtm,''YYYYMM-DD'') act_bill_dtm,
round((invoice_net_mny + invoice_tax_mny)/1000,0) mon_bill,
bill_type_id,bill_status
from billsummary
where to_char(bill_dtm - 1,''YYYYMM'') between'||chr(32)||
startMonth ||chr(32)||'and'|| chr(32)||endMonth ||chr(32)||
'and cancellation_dtm is null;
But when I try to compile the procedure it gives me the error 'PLS00215: String length constraints must be in range (1..32767). Though I searched for the error I could not find the exact reason. It seems to be a problem in variable assigning. But I could not resolve it.
--Update
As it is given in the answer I converted the strings to dates.
CREATE OR REPLACE procedure REPO.sixMonthAverage (startMonth varchar2,endMonth varchar2 ,thirdMonth varchar2 )
IS
start_date date := TO_DATE(startMonth, 'yyyymm');
end_date date := TO_DATE(endMonth, 'yyyymm');
But when executing the query it gives the error message that ORA-00904: "END_DATE": invalid identifier. But it does not show any error message for the start_date and what would be the reason for this error message?
The error is pointing you to where the problem is. String declarations (char, varchar, varchar2 - but you should only be using varchar2, not varchar) need a length; so for example:
CREATE OR REPLACE procedure sixMonthAverage (startMonth varchar2,endMonth varchar2 ,thirdMonth varchar2 )
IS
start_date varchar2(10) := startMonth;
end_date varchar2(10) := endMonth;
...
Notice the procedure arguments do not specify a length; only the local variable declarations.
If those represent dates then they, and passed-in arguments, should probably be dates, not strings. It depends what your dynamic SQL is expecting though - if that is converting the strings to dates and specifying the format mask then I guess it's OK; otherwise you should be passed dates, or convert the strings to dates. The example you showed doesn't seem to have any bind variables to populate, though.
Dropping and recreating tables is generally not something you want to be doing though. You could delete/truncate and repopulate a table; or use partitioning if you want to keep more than one month; or use a view (or materialized view).

Wrong date format for input parameter?

CREATE OR REPLACE PROCEDURE PROC1(
V_STARTTIME IN DATE,
V_ENDTIME IN DATE)
BEGIN
INSERT INTO TAB1
SELECT COINS FROM TAB2
WHERE DATE BETWEEN TO_DATE(V_STARTTIME,'MM/DD/YYYY') AND TO_DATE(V_ENDTIME,'MM/DD/YYYY');
COMMIT;
END;
SAMPLE DATE in Tab2 IS TIMESTAMP DATATYPE 5/5/2014 9:46:38.000000 AM
When I try to execute
Execute PROC1(TO_DATE('5/5/2014','MM/DD/YYYY'),TO_DATE('5/6/2014','MM/DD/YYYY'));
the procedure is successfully completed but my Insert into was ignored.
I tried printing the input date through dbms_output.put_line and the date did not return.
This is very, very similar to the question you asked yesterday.
If v_starttime and v_endtime are of type date, it makes no sense to call to_date on them. to_date does not take an argument of type date. It takes a parameter of type varchar2. If you try to pass a date to to_date, Oracle has to implicitly cast the date to a varchar2 using the session's NLS_DATE_FORMAT. If that doesn't match the format mask you're passing to to_date, you may get an error or you may get an incorrect result. As in yesterday's question, you want to avoid implicit conversions.
A date in Oracle has both a day and a time component (to the second). If you are doing the to_date in order to ensure that the time component is midnight, use the trunc function instead.
INSERT INTO TAB1( column_name )
SELECT COINS
FROM TAB2
WHERE <<timestamp column>> BETWEEN trunc( v_starttime ) AND trunc( v_endtime );
You say that your "insert was ignored". That seems highly unlikely. It's much more likely that your SELECT statement returned 0 rows so your INSERT inserted 0 rows. That's not an error. If you want to treat it as an error, you'd need to check SQL%ROWCOUNT after the INSERT and throw an error if the INSERT statement inserts 0 rows.
If the SELECT was not selecting any rows because of an implicit conversion error, then getting rid of the to_date and potentially adding the trunc would fix the problem.
The function TO_DATE requires string as first parameter.
CREATE OR REPLACE PROCEDURE PROC1(
V_STARTTIME IN DATE,
V_ENDTIME IN DATE)
BEGIN
INSERT INTO TAB1
SELECT COINS FROM TAB2 WHERE DATE BETWEEN V_STARTTIME AND V_ENDTIME;
COMMIT; --You should not use commit in procedure.
END;

Oracle Procedure error (PLS-00428)

This is the error message: PLS-00428: an INTO clause is expected in this SELECT statement. Meanwhile, this is the procedure for testing displaying the system date:
CREATE OR REPLACE
PROCEDURE "TEST_PROCEDURE"
AS
BEGIN
SELECT SYSDATE FROM DUAL;
END;
In the first place I don't need to use that INTO Oracle is insisting for me to do. Is there other way around beside using a cursor (I've seen it here https://stackoverflow.com/a/6029963/1983024)? I think it should not be like that, this does run normally just like in MS SQL without using INTO or cursor.
In the first place I don't need to use that INTO Oracle is insisting
for me to do.
The fact is, Oracle is correct: you do need to use an INTO to take the return value.
After all, if you want to display the result of the query you're going to need a variable to put it in first.
you can write
CREATE OR REPLACE
PROCEDURE "TEST_PROCEDURE"
AS
BEGIN
for r_row in ( SELECT SYSDATE s FROM DUAL)
dbms_output.put_line(r_row.s);
end loop;
END;
or you have to have a variable.
CREATE OR REPLACE
PROCEDURE "TEST_PROCEDURE"
AS
v_Date date;
BEGIN
SELECT SYSDATE into v_date FROM DUAL;
dbms_output.put_line(v_date );
END;
output format is dictated by your NLS_DATE_FORMAT setting, or you can be explicit like to_char(v_date, 'dd-mm-yyyy') etc.
Finally found a solution to the output I want (based on your responses) :D
CREATE OR REPLACE
PROCEDURE "TEST_PROCEDURE"
RET_DATE CHAR(10);
BEGIN
SELECT TO_CHAR(SYSDATE, 'MM/DD/YYYY') INTO RET_DATE FROM DUAL;
DBMS_OUTPUT.PUT_LINE(RET_DATE);
END;
This displays the SYSDATE with format of MM/DD/YYYY. Still, thanks for the replies and ideas (mostly #Jeffrey Kemp). Oracle just lengthens what MS SQL can normally do in one line :D
You can use like
CREATE OR replace PROCEDURE Test_procedure
IS
date CHAR(10);
BEGIN
SELECT To_char(SYSDATE, 'MM/DD/YYYY')
INTO date
FROM dual;
dbms_output.Put_line(date);
END;
it will return date into char format.
If you want to get date into date format just declare the date type variable then assign the sysdate value INTO that variable.Then use DBMS_OUTPUT.PUT_LINE(variable) to print the DATE.
If you wanted to do it in one line, you could also use:
CREATE OR replace PROCEDURE Test_procedure
IS
BEGIN
dbms_output.put_line(to_char(sysdate, 'MM/DD/YY'));
END;

Want to insert timestamp through procedure in oracle

I have this procedure
PROCEDURE insertSample
(
return_code_out OUT VARCHAR2,
return_msg_out OUT VARCHAR2,
sample_id_in IN table1.sample_id%TYPE,
name_in IN table1.name%TYPE,
address_in IN table1.address%TYPE
)
IS
BEGIN
return_code_out := '0000';
return_msg_out := 'OK';
INSERT INTO table1
sample_id, name, address)
VALUES
(sample_id_in, name_in, address_in);
EXCEPTION
WHEN OTHERS
THEN
return_code_out := SQLCODE;
return_msg_out := SQLERRM;
END insertSample;
I want to add 4th column in table1 like day_time and add current day timestamp in it.. ho can i do that in this procedure.. thank you
Assuming you you have (or add) a column to the table outside of the procedure, i.e.
ALTER TABLE table1
ADD( insert_timestamp TIMESTAMP );
you could modify your INSERT statement to be
INSERT INTO table1
sample_id, name, address, insert_timestamp)
VALUES
(sample_id_in, name_in, address_in, systimestamp);
In general, however, I would strongly suggest that you not return error codes and error messages from procedures. If you cannot handle the error in your procedure, you should let the exception propagate up to the caller. That is a much more sustainable method of writing code than trying to ensure that every caller to every procedure always correctly checks the return code.
Using Sysdate can provide all sorts of manipulation including the current date, or future and past dates.
http://edwardawebb.com/database-tips/sysdate-determine-start-previous-month-year-oracle-sql
SYSDATE will give you the current data and time.
and if you add the column with a default value you can leave your procedure as it is
ALTER TABLE table1 ADD when_created DATE DEFAULT SYSDATE;