Remove non-date values from a varchar column in oracle - sql

In a table called customers I have a varchar2 column(financial_month).
This column is supposed to be a date column but since it is a varchar2 column and due to some wrong coding, there are many data which aren't in date format.
For eg: 0.15 has been stored in one record and many other wrong entries.
Now I want to eliminate all the wrong entries.
What process should I follow?
I have no clue whatsoever. I am not able to identify the wrong entries as well. The only thing I know is that there are more than 1000 wrong entries.
How to remove all the wrong entries from this column?

Assuming that the format of the "financial months" must be MM/YYYY:
CREATE TABLE fm_t (fm VARCHAR2(30));
INSERT INTO fm_t VALUES ('01/2015');
INSERT INTO fm_t VALUES ('0.5');
INSERT INTO fm_t VALUES ('whatsever');
INSERT INTO fm_t VALUES ('02/2015');
SELECT * FROM fm_t;
CREATE OR REPLACE FUNCTION fm_f(fm IN VARCHAR2) RETURN INTEGER IS
d DATE;
BEGIN
d := TO_DATE(fm, 'FMMM/YYYY');
RETURN 1;
EXCEPTION WHEN OTHERS THEN
RETURN 0;
END;
/
DELETE fm_t WHERE fm_f(fm) <> 1;
SELECT * FROM fm_t;
DROP FUNCTION fm_f;
DROP TABLE fm_t;

You may use something like
WHERE REGEXP_LIKE(financial_month, '^[0-9]{2}[.][0-9]{2}[.][0-9]{4}$')
to find rows with date values
(this is for date format DD.MM.YYYY or MM.DD.YYYY)
or more precise regexp
'^[0-3][0-9][.][0-1][0-9][.](19|20)[0-9]{2}$'
for DD.MM.YYYY format.

Related

How To Modify The Type of the :new Variable Inside a Create Trigger Before Insert in ORACLE/PLSQL

I defined the column YEAR in my table STORE with datatype int. The data is inside a csv. When inserting i pass the year as a string, because it is either a number or 'N/A'. I tried declaring a trigger to correct this
It should do the following:
If the inserted string is 'N/A' Insert NULL. ELSE insert the value of the string.
CREATE TRIGGER checkYear
BEFORE INSERT ON STORE
FOR EACH ROW BEGIN
IF :new.YEAR = 'N/A' THEN
:new.YEAR := NULL;
ELSE
:new.YEAR := CAST(:new.Year AS INT);
END IF;
END;
When inserting values i get a trigger error from checkYear (ORA-04098) inside SQL Developer compiling the trigger gives PLS-00382
The data type of the :new.year variable will always match the data type of the column in the table. You must filter or parse your strings into integers before placing them in the SQL statement.
You just can use a REPLACE() function in order to get rid of the case whenever upcoming year value equals to N/A such as
CREATE OR REPLACE TRIGGER checkYear
BEFORE INSERT ON store
FOR EACH ROW
BEGIN
:new.year := REPLACE(:new.year,'N/A');
END;
/
Otherwise(if it's a year literal which can be convertible to a numeric one such as '2022' ) no need an explicit casting to a numeric value which's already found out implicitly by the DB.
Btw,
you can prefer to adding that to your Insert statement such as
INSERT INTO store(...,year,...) VALUES(...,REPLACE(year,'N/A'),...)
rather than creating a trigger.
or
you can cast directly within the control file such as
...
year CHAR(4000) "REPLACE(:year,'N/A')"
...
if your importing tool is SQL loader.

Updating the value of other column while insertion in oracle

My task is pretty much simple let's say I have table having 3 columns i.e. temp(ids, name_c, UID_c) and I have first two column values and 3rd column is nullable. What I want to do is whenever these two value is inserted the value of third column must be updated (after insertion) with new value. i.e concatenation of both values.
For Ex.
insert into temp(ids, name_c) values(did.nextval,'Andrew');
The result should be
1 Andrew Andrew_1
So I am using trigger for this purpose
create or replace trigger triggerDemo
after INSERT
on temp
for each row
declare
/* pragma autonomous_transaction;*/
user_name varchar2(50);
current_val number;
begin
select did.currval into current_val from dual; /* did is sequence */
select names into user_name from temp where ids = current_val;
update temp set uid_c = user_name||'_'||ids where ids = current_val;
end;
When I am inserting the values I get this error
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.
First, you want a before insert trigger, not an after insert trigger. Second, decide whether you want to calculate the id on input or in the trigger. You can do something like this:
create or replace trigger triggerDemo before INSERT on temp
for each row
/* pragma autonomous_transaction;*/
begin
if :new.current_val is null then
select did.currval into :new.ids from dual; /* did is sequence */
end;
select :new.user_name || '_' || :new.ids into :new.uid_c from dual;
end;

Making columns optional in Oracle

I'm having a bit of an Oracle dilemma.
I have a table consisting of 5 columns: one PK, two FKs, one INT value and one Date.
I also have a sequence set up for my PK.
I set up a trigger that replaces the PK by an auto-incremented value and the Date by the current date so that you can enter the values (null, FK, FK, INT, null) but I was wondering if there was a way to modify my trigger so that I can enter simply (FK, FK, INT)? As it stands (obviously) if I enter only 3 values I get the ORA-00947: not enough values error.
CREATE or REPLACE trigger TRIG_new_product
before insert on product
for each row
BEGIN
SELECT sq_product.nextval, sysdate
into :new.productID, :new.productDate
FROM dual;
END TRIG_new_product;
If you don't want to supply values for a column, don't list it in the insert statement:
insert into product
(fk_column, fk_column, int_column)
values
(42, 24, 4224);
The error message "not enough" values has nothing to do with your trigger and probably stems from the fact that you didn't specify the columns in your insert statement. In that case you have to supply a value for each column. You probably did something like this:
insert into product -- no columns specified therefore all are required
values
(42, 24, 4224);
Of course leaving out columns during insert will only work if they are defined as nullable.
Btw: your trigger could be written a bit simpler:
CREATE or REPLACE trigger TRIG_new_product
before insert on product
for each row
BEGIN
:new.productID := sq_product.nextval;
:new.productDate := sysdate;
END TRIG_new_product;

INSERT ALL INTO and Sequence.nextval for a Surrogate Key

I'm trying to insert 40 rows using an INSERT ALL INTO and I'm not certain on how to insert the surrogate key. Here's what I have
BEGIN
INSERT ALL
INTO question(question_id)
VALUES (question_seq.nextval)
END
Now if I add another INTO VALUES then I get a unique constraint violation.
BEGIN
INSERT ALL
INTO question(question_id)
VALUES (question_seq.nextval)
INTO question(question_id)
VALUES (question_seq.nextval)
END
How can I update the sequences nextval value for each INTO VALUES so that I can avoid the unique constraint violation? I assumed that nextval would automatically update itself.
UPDATE: I don't know if this is the best way to handle this but here's the solution I came up with:
first I created a function that returns a value
then I called that function in the id field of the VALUES clause
create or replace
FUNCTION GET_QUESTION_ID RETURN NUMBER AS
num NUMBER;
BEGIN
SELECT UHCL_QUESTIONS_SEQ.nextval
INTO num
FROM dual;
return num;
END GET_QUESTION_ID;
INSERT ALL
INTO question(question_id)
VALUES (GET_QUESTION_ID())
INTO question(question_id)
VALUES (GET_QUESTION_ID())
Being from a SQL Server background, I've always created a trigger on the table to basically emulate IDENTITY functionality. Once the trigger is on, the SK is automatically generated from the sequence just like identity and you don't have to worry about it.
You can use something like this:
insert into question(question_id)
select question_seq.nextval from
(
select level from dual connect by level <= 40
);
Although it's not a very convenient format, especially if there are other columns you want to add. You'd probably need to create another UNION ALL query, and join it by the LEVEL or ROWNUM.
My first thought was to do something like this:
insert into question(question_id)
select question_seq.nextval value from dual
union all
select question_seq.nextval from dual;
But it generates ORA-02287: sequence number not allowed here, due to the restrictions on sequence values.
By the way, are you sure your INSERT ALL works without a subquery? I get the error ORA-00928: missing SELECT keyword, and the diagram from the 11.2 manual implies there must be a subquery:
I don't know if this is the best way to handle this but here's the solution I came up with:
first I created a function that returns a value
then I called that function in the id field of the VALUES clause
create or replace
FUNCTION GET_QUESTION_ID RETURN NUMBER AS
num NUMBER;
BEGIN
SELECT UHCL_QUESTIONS_SEQ.nextval
INTO num
FROM dual;
return num;
END GET_QUESTION_ID;
INSERT ALL
INTO question(question_id)
VALUES (GET_QUESTION_ID())
INTO question(question_id)
VALUES (GET_QUESTION_ID())

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;