Update trigger column based on date in ddmmyyy format in Oracle - oracle

I have created a trigger based on which I want to assign column with some value.
For example: I have a column name BILL_CALCULATED_DATE with value 19-10-21 which is in dd-mm-yy format. I want to extract mm and if it is 10 then I want to assign it as October
Below is my trigger
CREATE OR REPLACE TRIGGER TRG_UPD_GTL_BILL
BEFORE INSERT OR UPDATE ON IPFEE_MST_INSRT_GTL
FOR EACH ROW
BEGIN
:new.BILL_CALCULATED_DATE := case :NEW.UPLOADED_MONTH
when '01' then 'January'
when '02' then 'February'
when '03' then 'March'
when '04' then 'April'
when '05' then 'May'
when '06' then 'June'
when '07' then 'July'
when '08' then 'August'
when '09' then 'September'
when '10' then 'October'
when '11' then 'November'
when '12' then 'December'
end;
end;
But the problem is, before assigning month in the trigger I want to extract the month. How should I achieve it ?

Assuming that your BILL_CALCULATED_DATE column is of the DATE data type and you want the UPLOADED_MONTH column to be the name of the month corresponding to BILL_CALCULATED_DATE's month then:
Do not use a trigger; use a virtual column then the column values will always be synchronised.
Either create the table using:
CREATE TABLE IPFEE_MST_INSRT_GTL(
BILL_CALCULATED_DATE DATE,
UPLOADED_MONTH VARCHAR2(9)
GENERATED ALWAYS AS (
CAST(
TO_CHAR(BILL_CALCULATED_DATE, 'fmMonth', 'NLS_DATE_LANGUAGE=English')
AS VARCHAR2(9)
)
)
);
Or modify the existing table to drop the non-virtual column and add a virtual column:
ALTER TABLE IPFEE_MST_INSRT_GTL DROP COLUMN UPLOADED_MONTH;
ALTER TABLE IPFEE_MST_INSRT_GTL
ADD UPLOADED_MONTH VARCHAR2(9)
GENERATED ALWAYS AS (
CAST(
TO_CHAR(BILL_CALCULATED_DATE, 'fmMonth', 'NLS_DATE_LANGUAGE=English')
AS VARCHAR2(9)
)
);
If you must use a trigger and a real column then:
CREATE OR REPLACE TRIGGER TRG_UPD_GTL_BILL
BEFORE INSERT OR UPDATE ON IPFEE_MST_INSRT_GTL
FOR EACH ROW
BEGIN
:NEW.UPLOADED_MONTH := TO_CHAR(
:new.BILL_CALCULATED_DATE,
'fmMonth',
'NLS_DATE_LANGUAGE=English'
);
END;
/
If your BILL_CALCULATED_DATE column is a CHAR or VARCHAR2 data type then:
Change that column to a DATE.
If you cannot change it to a DATE then go and talk to someone who can change it to a DATE.
If you really, really cannot change it to a DATE then spend time despairing about the inability to follow best practice, try to convince people who may have the ability to do something about it that you should change things and then replace BILL_CALCULATED_DATE with TO_DATE(BILL_CALCULATED_DATE, 'DD-MM-YY') or :new.BILL_CALCULATED_DATE with TO_DATE(:new.BILL_CALCULATED_DATE, 'DD-MM-YY') in the snippets above.
db<>fiddle here

As per my understanding your column is not in date format. First convert this into date and then use EXTRACT function -
SELECT EXTRACT(month FROM TO_DATE(BILL_CALCULATED_DATE, 'dd-mm-yy'))
FROM IPFEE_MST_INSRT_GTL;

Something like this?
SELECT EXTRACT(month FROM SYSDATE) from dual

Related

Sysdate+days as default value in table column - Oracle

I'm working on my table which is supposed to store data about rented cars.
And there are 3 important columns:
RENT_DATE DATE DEFAULT TO_DATE (SYSDATE, 'DD-MM-YYYY'),
DAYS NUMBER NOT NULL,
RETURN_DATE DATE DEFAULT TO_DATE(SYSDATE+DAYS, 'DD-MM-YYYY')
My problem is that RETURN_DATE column is giving me error:
00984. 00000 - "column not allowed here"
What i want is that RENT_DATE set automatically date when record is added.
DAYS column is to store for how much days someone is renting car.
And the last column should store date of when car should be returned.
Thank you for any type of help.
This doesn't make sense:
DEFAULT TO_DATE (SYSDATE, 'DD-MM-YYYY')
SYSDATE is already a date. TO_DATE requires a char, so this takes a date, Oracle implicitly turns the date into a char, and then TO_DATE converts it back to a date. This is risky/unreliable because it uses a hardcoded date format to operate on a date that has been implicitly turned to a string using the system default format, which might one day not be DD-MM-YYYY (you're building a latent bug into your software)
If you want a date without a time on it use TRUNC(SYSDATE)
The other problem doesn't make sense either: you're storing a number of days rented for and also the return date, when one is a function of the other. Storing redundant data becomes a headache because you have to keep them in sync. My person class stores my birthdate, and I calculate how old I am. I don't store my age too and then update my table every day/year etc
Work out which will be more beneficial to you to store, and store it, then calculate the other whenever you want it. Personally I would store the return date as it's absolute, rather than open to interpretation of "is that working days, calendar days? what about public holidays? if the start date is jan 1 and the rental is for 10 days, is the car brought back on the 10th or the 11th?"
If you're desperate to have both columns in your DB consider using a view to calculate it or a function based column (again, to calculate one from the other) so they stay in sync
All in, you could look at this:
create table X(
RENT_DATE DATE DEFAULT TRUNC(SYSDATE) NOT NULL,
RETURN_DATE DATE NOT NULL,
DAYS AS (TRUNC(RETURN_DATE - RENT_DATE) + 1)
)
I put the days as +1 because to me, a car taken on the 1st and returned on the second is 2 days, but you might want to get more accurate - if it's taken on the first and returned before 10am on the second then it's one day otherwise it's 2 etc...
Use a virtual column:
CREATE TABLE table_name (
RENT_DATE DATE
DEFAULT TRUNC( SYSDATE )
CONSTRAINT table_name__rent_date__nn NOT NULL
CONSTRAINT table_name__rent_date_chk CHECK ( rent_date = TRUNC( rent_date ) ),
DAYS NUMBER
DEFAULT 7
CONSTRAINT table_name__days__nn NOT NULL,
RETURN_DATE DATE
GENERATED ALWAYS AS ( RENT_DATE + DAYS ) VIRTUAL
);
Then you can insert values:
INSERT INTO table_name ( rent_date, days ) VALUES ( DEFAULT, DEFAULT );
INSERT INTO table_name ( rent_date, days ) VALUES ( DATE '2020-01-01', 1 );
And:
SELECT * FROM table_name;
Outputs:
RENT_DATE | DAYS | RETURN_DATE
:------------------ | ---: | :------------------
2020-09-12T00:00:00 | 7 | 2020-09-19T00:00:00
2020-01-01T00:00:00 | 1 | 2020-01-02T00:00:00
db<>fiddle here

Check if input date not between date1 and date2 in first row to last row plsql

I am trying to create a procedure to insert a new start_date into my table but i want it not between date1 and date2 in first row to last row. it mean i want to compare then new insert date from first row to to last row if it is not between then insert in into table. how can i check that, please anyone know tell me.
my table name is resister
and this is my table
You can take advantage of the NOT EXISTS as following:
SELECT &&NEW_INPUT_DATE
FROM DUAL
WHERE NOT EXISTS
(SELECT 1
FROM RESISTER
WHERE &&NEW_INPUT_DATE BETWEEN START_DATE AND END_DATE)
Cheers!!
As I see it from data next date range always continues the previous. So you might just check if the new start_date is greater that max(end_date). Like this
create or replace procedure insert_date(p_start_date date, p_end_date date)
is
l_min_date date;
l_max_date date;
begin
select min(start_date), max(end_date)
into l_min_date, l_max_date
from registers;
if (p_start_date > l_max_date) and (p_end_date > p_start_date) then
insert into registers(start_date, end_date) values(p_start_date, p_end_date);
end if;
end;
If it is possible for the user to enter a date prov the past (e.g. 01/01/1980 - 31/01/1980) then the "if" clause would be looking similar to this
... if (p_start_date > l_max_date or p_end_date < l_min_date) and (p_end_date > p_start_date) then ...

Split Date from Oracle Form to DB

I have a table with columns(year,day-month) -date type- in my database.
and a form with a text field for the user to enter a date.
how can I split the entered date to save it on db as following
year day_month
---- ---------
2018 03-04
I tried SUBSTR(TO_CHAR(TO_DATE(block.field)) in a trigger ,
but it didn't work bcz the column type is date, and I tried to add TO_DATE() as outer but the result was
year day_month
---------- ----------
03-04-2018 03-04-2018
How can I do it without changing my columns type?
I'd suggest you NOT to do that. Always store DATE values into DATE datatype columns. ALWAYS.
Later, if you want to present them differently, apply appropriate functions (such as TO_CHAR) to those values and display them any way you want.
In your example, that would be
TO_CHAR(date_column, 'yyyy') year
or
EXTRACT (year from date_column) year
and
TO_CHAR(date_column, 'dd-mm') day_month
[EDIT]
Once again (to repeat what I've said in a comment): the fact that you named columns in the database "year" (whose datatype is DATE) and "day_month" (whose datatype is also DATE) is completely useless.
Right now is (dd.mm.yyyy hh24:mi) 03.04.2018 10:32.
DATE datatype contains both date and time, so - how do you plan to put "2018" into the "year" column? What will you do with its month/day/hour/minutes/seconds component? It can't just "vanish", has to have some value. Is it the first of January at 00:00:00? Or what?
The same goes to your "day_month" column - it'll contain year, as well as hours/minutes/seconds, whether you want it or not.
Let's start with the "year": if you want to extract it from the Form item, that would be TO_CHAR, such as
to_char(:block.some_item, 'yyyy')
which results in a string, '2018'. You can't store it into a DATE datatype column, so you have to apply TO_DATE to it:
to_date(to_char(:block.some_item, 'yyyy'), 'yyyy')
and it will result in 01.04.2018 00:00:00 >>> see? Day, month, hours ... everything is here.
The alternative is to create those columns as VARCHAR2, but that's even worse.
Seriously, don't do that.
Try the following and make the necessary changes in Oracle Forms, substitute block and columns names instead of variables.
DECLARE
p_year VARCHAR2 (8);
p_date VARCHAR2 (8);
BEGIN
SELECT TO_CHAR (SYSDATE, 'YYYY') INTO p_year FROM DUAL;
SELECT TO_CHAR (SYSDATE, 'DD-MM') INTO p_date FROM DUAL;
DBMS_OUTPUT.put_line ('p_year --> ' || p_year || ' p_date --> ' || p_date);
END;
If your column is a DATE type, expect that it will require you to input a date data also.
In your case, you don't need to split a date. For the YEAR column, if the year value only matters to you, then you can use the TRUNC function
:BLK.YEAR_DATE_FIELD := TRUNC(:BLK.DATE_VALUE, 'YYYY');
and for the MONTH column, just save the date value there.
:BLK.MONTH_DATE_FIELD := :BLK.DATE_VALUE;
Also, maybe you just need to set the format mask of those two fields in Oracle forms. You can set the Format Mask of YEAR field to YYYY and MM-DD to the MONTH field.
The DATE data type is stored dates in tables as 7-bytes
byte 1 - century + 100
byte 2 - (year MOD 100 ) + 100
byte 3 - month
byte 4 - day
byte 5 - hour + 1
byte 6 - minute + 1
byte 7 - seconds+ 1
You CANNOT have a DATE data type that just stores year or month + day; it will always store all the components of the date/time.
So you either store the correct values in each column or you will have to make up values for the components you are not storing and will need to make sure that all the made up values are appropriate for the real values. It is just easier to use the real values in both columns.
So just do:
INSERT INTO your_table(
year,
day_month
) VALUES (
:BLK1.T_DATE,
:BLK1.T_DATE
);
Without splitting the date because a day_month without a year does not make sense (is 29th February a valid date? For the majority of years, no it isn't).
When you want to output it with a format then just format it as a string on output:
SELECT TO_CHAR( year, 'yyyy' ) AS year,
TO_CHAR( day_month, 'dd-mm' ) AS day_month
FROM your_table;

Oracle SQL Developer- How to force 00:00:00 hour when inserting a new DATE value

In my Oracle SQL Developer, i have a table with a column with DATE format. When i insert a new row into this table, and insert a new value in this column, it automatically suggestes me the current date with the current hour.
I would like that it automatically suggestes me current date, but with 00:00:00 hour . Is there some setting or parameter that i can set in my SQL Developer to have this result?
We can't able to insert 00:00:00 hours ... the hour value should be between 1 to 12...
we can use below query to insert 00:00:00 hours but the value will be changed to 12:00:00
INSERT INTO TABLE (DATE_COL) VALUES
( TO_DATE ('11/16/2017 00:00:00 ', 'MM/DD/YYYY HH24:MI:SS '));
It seems to me that your DATE column is set with a DEFAULT of SYSDATE. This means, for any INSERT operations which do not specify a value in your DATE column, the current date and time will populate for that row. However, if INSERT operations do specify a value in your DATE column, then the specified date value will supersede the DEFAULT of SYSDATE.
If an application is controlling INSERT operations on that table, then one solution is to ensure the application utilizes the TRUNC() function to obtain your desired results. For example:
INSERT INTO tbl_target
(
col_date,
col_value
)
VALUES
(
TRUNC(SYSDATE, 'DDD'),
5000
)
;
However, if there are multiple applications or interfaces where users could be inserting new rows into the table, (e.g. using Microsoft Access or users running INSERT statements via SQL Developer) and you can't force all of those interfaces to utilize the TRUNC() function on that column during insertion, then you need to look into other options.
If you can ensure via applications that INSERT operations will not actually reference the DATE, then you can simply ALTER the table so that the DATE column will have a DEFAULT of TRUNC(SYSDATE). A CHECK CONSTRAINT can be added for further integrity:
ALTER TABLE tbl_target
MODIFY
(
col_date DATE DEFAULT TRUNC(SYSDATE, 'DDD') NOT NULL
)
ADD
(
CONSTRAINT tbl_target_CHK_dt CHECK(col_date = TRUNC(col_date, 'DDD'))
)
;
However, if users still have the freedom to specify the DATE when inserting new rows, you will want to use a TRIGGER:
CREATE OR REPLACE TRIGGER tbl_target_biu_row
BEFORE INSERT OR UPDATE OF col_val
ON tbl_target
FOR EACH ROW
BEGIN
:NEW.col_date := TRUNC(SYSDATE, 'DDD');
END tbl_target_biu_row
;
This will take of needing to manage the application code of all external INSERT operations on the table. Keep in mind, the above trigger is also modifying the DATE column if a user updates the specified value column.

Using date in a check constraint, Oracle

I am trying to check add the following constraint but Oracle returns the error shown below.
ALTER TABLE Table1
ADD (CONSTRAINT GT_Table1_CloseDate
CHECK (CloseDate > SYSDATE),
CONSTRAINT LT_Table1_CloseDate
CHECK (CloseDate <= SYSDATE + 365)),
CONSTRAINT GT_Table1_StartDate
CHECK (StartDate > (CloseDate + (SYSDATE + 730))));
Error:
Error report:
SQL Error: ORA-02436: date or system variable wrongly specified in CHECK constraint
02436. 00000 - "date or system variable wrongly specified in CHECK constraint"
*Cause: An attempt was made to use a date constant or system variable,
such as USER, in a check constraint that was not completely
specified in a CREATE TABLE or ALTER TABLE statement. For
example, a date was specified without the century.
*Action: Completely specify the date constant or system variable.
Setting the event 10149 allows constraints like "a1 > '10-MAY-96'",
which a bug permitted to be created before version 8.
A check constraint, unfortunately, cannot reference a function like SYSDATE. You would need to create a trigger that checked these values when DML occurs, i.e.
CREATE OR REPLACE TRIGGER trg_check_dates
BEFORE INSERT OR UPDATE ON table1
FOR EACH ROW
BEGIN
IF( :new.CloseDate <= SYSDATE )
THEN
RAISE_APPLICATION_ERROR( -20001,
'Invalid CloseDate: CloseDate must be greater than the current date - value = ' ||
to_char( :new.CloseDate, 'YYYY-MM-DD HH24:MI:SS' ) );
END IF;
IF( :new.CloseDate > add_months(SYSDATE,12) )
THEN
RAISE_APPLICATION_ERROR( -20002,
'Invalid CloseDate: CloseDate must be within the next year - value = ' ||
to_char( :new.CloseDate, 'YYYY-MM-DD HH24:MI:SS' ) );
END IF;
IF( :new.StartDate <= add_months(:new.CloseDate,24) )
THEN
RAISE_APPLICATION_ERROR( -20002,
'Invalid StartDate: StartDate must be within 24 months of the CloseDate - StartDate = ' ||
to_char( :new.StartDate, 'YYYY-MM-DD HH24:MI:SS' ) ||
' CloseDate = ' || to_char( :new.CloseDate , 'YYYY-MM-DD HH24:MI:SS' ) );
END IF;
END;
You cannot use SYSDATE in check constraint. According to documentation
Conditions of check constraints cannot
contain the following constructs:
Subqueries and scalar subquery expressions
Calls to the functions that are not deterministic (CURRENT_DATE,
CURRENT_TIMESTAMP, DBTIMEZONE,
LOCALTIMESTAMP, SESSIONTIMEZONE,
SYSDATE, SYSTIMESTAMP, UID, USER, and
USERENV)
Calls to user-defined functions
Dereferencing of REF columns (for example, using the DEREF function)
Nested table columns or attributes
The pseudocolumns CURRVAL, NEXTVAL, LEVEL, or ROWNUM
Date constants that are not fully specified
For 10g Release 2 (10.2), see constraint, and for 11g Release 2 (11.2) see constraint.
Remember that an integrity constraint is a statement about table data that is always true.
Anyway: I don't know exactly what you are trying to achieve but I think you can use triggers for this purpose.
Each and every time the record is updated SYSDATE will have a different value. Therefore the constraint will validate differently each time. Oracle does not allow sysdate in a constraint for that reason.
You may be able to solve your problem with a trigger that checks if CloseDate has actually changed and raise an exception when the new value is not within range.
And: What is (StartDate > (CloseDate + (SYSDATE + 730))))? You cannot add dates.
And: StartDate needs to be after CloseDate? Is that not weird?
Write sysdate into a column and use it for validation. This column might be your audit column (For eg: creation date)
CREATE TABLE "AB_EMPLOYEE22"
(
"NAME" VARCHAR2 ( 20 BYTE ),
"AGE" NUMBER,
"SALARY" NUMBER,
"DOB" DATE,
"DOJ" DATE DEFAULT SYSDATE
);
Table Created
ALTER TABLE "AB_EMPLOYEE22" ADD CONSTRAINT
AGE_CHECK CHECK((ROUND((DOJ-DOB)/365)) = AGE) ENABLE;
Table Altered
I don`t recommend sing triggers as constraint and to raise exceptions, Instead you can use a column to store SYSDATE as register date(if you already have it then you can use it) and then your constraint compares this column instead of SYSDATE
ALTER TABLE Table1
ADD (REGISTER_DATE DATE);
CREATE OR REPLACE TRIGGER trg_check_dates
BEFORE INSERT OR UPDATE ON table1
FOR EACH ROW
BEGIN
:new.REGISTER_DATE := SYSDATE;
END;
ALTER TABLE Table1
ADD (CONSTRAINT GT_Table1_CloseDate
CHECK (CloseDate > REGISTER_DATE),
CONSTRAINT LT_Table1_CloseDate
CHECK (CloseDate <= REGISTER_DATE + 365)),
CONSTRAINT GT_Table1_StartDate
CHECK (StartDate > (CloseDate + (REGISTER_DATE + 730))));

Resources