pl/sql to get end date from start date - oracle

I'm trying to create a trigger to get value of validity from plan table and add it to the value of startdate to get the enddate into dblog table.I have written this trigger.
My dblog and plan table schema is like this
desc dblog;
Name Null? Type
---------------------------- -------- -------
PLANID NOT NULL NUMBER
STARTDATE DATE
ENDDATE NOT NULL DATE
desc plan;
Name Null? Type
---------------------------- -------- -------
PLANID NOT NULL NUMBER
VALIDITY NOT NULL NUMBER
By default column STARTDATE has SYSDATE value.
CREATE OR REPLACE TRIGGER trg2
BEFORE INSERT ON dblog FOR EACH ROW
DECLARE
l_startdate date := SYSDATE;
l_enddate date;
l_validity number;
BEGIN
SELECT validity INTO l_validity
FROM plan
WHERE planid = :NEW.planid;
l_endate := l_startdate + l_validity;
SET :NEW.enddate := l_enddate;
END;
/
it shows following error:
10/2 PL/SQL: SQL Statement ignored
10/6 PL/SQL: ORA-00922: missing or invalid option
Am I using wrong concept or what? How can i get this done?

l_startdate SYSDATE;
You have not declared the data type of the variable, SYSDATE is an inbuilt function and not a data type.
If you want to assign the value at the time of data type declaration, then do it as:
DECLARE
l_startdate DATE := SYSDATE;
For example,
SQL> set serveroutput on
SQL> DECLARE
2 start_dt DATE := SYSDATE;
3 BEGIN
4 dbms_output.put_line(start_dt);
5 END;
6 /
29-SEP-15
PL/SQL procedure successfully completed.
SQL>
UPDATE OP edited the question
The below query will fail:
SELECT validity INTO l_validity from plan where planid=:new.planid;
since your trigger is defined on dblog table, however, you are referring to the :new values for plan table.
Also,
SET :NEW.enddate := l_enddate;
You don't need the SET clause, simply do it as:
:NEW.enddate := l_enddate;

Related

ID get NULL when i try to update data

I created stored procedure in Oracle SQL to update data in my table:
create or replace procedure UpdateProduct(product_id int
,product_name VARCHAR2
,product_price int
,product_description varchar2)
as
begin
update product
set name = product_name,
price = product_price,
description = product_description
where id = product_id;
--
insert into product(update_date)
values (TO_CHAR(SYSDATE, 'DD.MM.YYYY hh24:mi:ss'));
end;
When I run the procedure, an error is thrown as the ID is NULL
begin
UpdateProduct(26, 'шщйыа', 9845, 'ыгаз');
end;
Why do you have that insert statement in the procedure? Are you trying to update the update_date in the same row? That's not what an insert does - insert inserts an entirely new row, which has nothing to do with the values you call the procedure with. Instead, add the assignment to update_date to the other assignments, in the update statement:
create or replace procedure UpdateProduct(product_id int
,product_name VARCHAR2
,product_price int
,product_description varchar2)
as
begin
update product
set name = product_name,
price = product_price,
description = product_description,
update_date = TO_CHAR(SYSDATE, 'DD.MM.YYYY hh24:mi:ss')
where id = product_id;
--
end;
As an aside, what is the data type of update_date? You are assigning a string to it. The data type should be date (in which case you should simply assign SYSDATE to it); the way you wrote it, either update_date is indeed of date data type, and then TO_CHAR is unnecessary (or, worse, it may cause problems), or update_date is of varchar2 data type, which is in itself a huge mistake, which perhaps you can correct.
It is the insert that causes the error. You're trying to insert a line with update_date column filled only. Like that
id
name
price
description
update_date
null
null
null
null
01.01.2001
I assume the insert statement has to be at least like this:
insert into product(id, update_date)
values (some_id_goes_here, TO_CHAR(SYSDATE, 'DD.MM.YYYY hh24:mi:ss'));
Or maybe you need an update statement, I'm not sure what your logic is

Trigger is not working when I'm trying to insert a value into a table

I am trying to make an Insert Trigger for a table called Marks which has id, id_student, id_course, value, data_notation, created_at, updated_at.
I need to make an Update on the old value, if the value I want to insert is higher than the one already exists in the column, and if there are no values in the column you would do an Insert with the new value.
I created the Trigger and there are no compilation errors.
CREATE OR REPLACE TRIGGER insert_value
before INSERT ON Marks
FOR EACH ROW
BEGIN
IF (:OLD.value IS NULL) THEN
DBMS_OUTPUT.PUT_LINE('Inserting.. because value is null');
UPDATE Marks SET value = :NEW.value where id_student = :NEW.id_student;
ELSE
DBMS_OUTPUT.PUT_LINE('Updating old value.. if old value is smaller than the one we want');
IF (:OLD.value < :NEW.value) THEN
UPDATE Marks SET value = :NEW.value where :OLD.id_student = :NEW.id_student;
END IF;
END IF;
END;
I want to change the old value from an existing value 5 to null for a specific id.
update Marks set value = null where id = 692;
select * from Marks where id = 692;
But when I'm trying to insert a value into the table so I can change the value null into 6 via the trigger
INSERT INTO Marks
VALUES (692, 43, 12, 6, '13-02-2018', '13-02-2018', '13-02-2018');
I am receiving an error.
Error report -
SQL Error: ORA-00001: unique constraint (STUDENT.SYS_C007784) violated
00001. 00000 - "unique constraint (%s.%s) violated"
*Cause: An UPDATE or INSERT statement attempted to insert a duplicate key.
For Trusted Oracle configured in DBMS MAC mode, you may see
this message if a duplicate entry exists at a different level.
*Action: Either remove the unique restriction or do not insert the key.
And it prints one time:
Inserting.. because value is null
But when I'm trying to check if the trigger did its job, using:
SELECT * from Marks where id = 692;
It doesn't update anything.
It has to be a trigger triggered by an insert operation. So I can't make the insert into the table, but how else should I write it so it works?
You problem comes from recursive calling the trigger due to the insert. The following would work. It does not catch update statements. It only cares for inserts. If the row exists already the row gets deleted first and the existing value is used for the insert if the existing value is higher.
set lin 20000
drop table marks;
create table Marks(
id number,
id_student number,
id_course number,
value number,
data_notation varchar2(40),
created_at timestamp,
updated_at timestamp,
CONSTRAINT marks#u UNIQUE (id, id_student, id_course)
);
create or replace trigger mark_trigger
before insert on marks
for each row
declare
l_value number;
l_data_notation varchar2(40);
l_created_at timestamp;
begin
select value, data_notation, created_at
into l_value, l_data_notation, l_created_at
from
(select *
from marks
where marks.id = :new.id
and marks.id_student = :new.id_student
and marks.id_course = :new.id_course
order by created_at desc)
where rownum=1;
if l_value is null then
return;
end if;
if l_value > :new.value then
:new.value := l_value;
:new.data_notation := l_data_notation;
:new.created_at := l_created_at;
else
:new.updated_at := systimestamp;
end if;
delete from marks
where marks.id = :new.id
and id_student = :new.id_student
and id_course = :new.id_course;
exception
when no_data_found then
null;
end;
create or replace procedure marks_insert(
i_id number,
i_id_student number,
i_id_course number,
i_value number,
i_data_notation varchar2
)
is
begin
INSERT INTO marks
VALUES (i_id, i_id_student, i_id_course, i_value, i_data_notation, systimestamp, null);
END marks_insert;
begin
delete from marks;
marks_insert(1,1,1,5,'1 first entry');
marks_insert(1,1,1,6,'1 second entry');
marks_insert(1,1,2,3,'2 first entry');
marks_insert(1,1,2,2,'2 second entry');
end;
select * from marks;
Output:
Table dropped.
Table created.
Trigger created.
Procedure created.
PL/SQL procedure successfully completed.
ID ID_STUDENT ID_COURSE VALUE DATA_NOTATION CREATED_AT UPDATED_AT
---------- ---------- ---------- ---------- ---------------------------------------- -------------------------------------------------- --------------------------------------------------
1 1 1 6 1 second entry 07/05/2019 13:31:31.266817 07/05/2019 13:31:31.266928
1 1 2 3 2 first entry 07/05/2019 13:31:31.268032
2 rows selected.
You are inserting into the Marks when you insert into the Marks (the insert statement in the trigger before inserting) and so on in a recursive way. Hence the direct cause of error.

Appropriate Use of Bind Variables

In the following PL/SQL block, a bind variable is used in the WHERE clause:
declare
symbol varchar2(6) := 'EPIC';
begin
execute immediate 'delete from stock where symbol = :symbol'
using symbol;
end;
/
This block executes successfully, however, something like the following will fail:
declare
symbol varchar2(15) := 'employees';
begin
execute immediate 'delete from :symbol where last_name = ''Kochar'''
using symbol
end;
/
My question is: can we use bind variables in any other context besides passing a value to a WHERE clause like in the first example?
You can bind into your SQL statement only those expressions(literals,
variables, complex expressions) that replace placeholders for data
values inside the dynamic string. you cannot bind in the names of
schema elements(tables, columns, etc) or entrie chunks of the SQL
statement. For those parts of your string, you must use
concatenation(operator)
So Use as in the following :
SQL> create table employees(empid int,last_name varchar2(50));
Table created
SQL> insert into employees values(111,'Kochar');
1 row inserted
SQL> select * from employees;
EMPID LAST_NAME
----- ----------
111 Kochar
SQL>
SQL> declare
2 symbol varchar2(15) := 'employees';
3 begin
4 execute immediate 'delete '||symbol||' where last_name = ''Kochar''';
5 end;
6 /
PL/SQL procedure successfully completed
SQL> select * from employees;
EMPID LAST_NAME
----- ----------
-- i.e. no row(s) returned.
Bind Variables is only transmit values .
It allow to reuse the same query , but with different values .
A table name is not a value .

ora-06502 on writing into LONG variable

Here's a simplified request:
declare
long_var long;
begin
select long_column into long_var from my_table where id = 1;
end;
It works for most cases but when the long_column value is too big (I've found at least one entry - it has a value of about 5 megabytes) causes
ORA-06502: PL/SQL: numeric or value error
From the Oracle PL/SQL documentation
You cannot retrieve a value longer than 32,760 bytes from a LONG or LONG RAW column into a LONG or LONG RAW variable.
https://docs.oracle.com/cd/E11882_01/appdev.112/e25519/datatypes.htm#CJAEGDEB
You can use a CLOB but you must alter the column from LONG to CLOB
ALTER TABLE my_table MODIFY ( long_column CLOB );
Then you use:
declare
clob_var clob;
begin
select long_column into clob_var from my_table where id = 1;
end;
If you cannot change the table copy it over:
1) Create table with a CLOB
create table my_table_2 (clob_column clob, id number);
2) Copy it over
insert into my_table_2
select to_lob(long_column), id from my_table;
3) Access the new table
declare
clob_var clob;
begin
select clob_column into clob_var from my_table_2 where id = 1;
end;

Implicit conversion of date field in a pl/sql trigger?

I need to develop a trigger in PL/SQL (Oracle) before INSERT.
In this table there is a column (cdat) of type DATE.
Let's say i do INSERT INTO myTbl (123,'12/05/2011');
In my trigger the :NEW.CDAT is converted in the final date system or it's still a varchar?
Do I need to do a TO_DATE(:NEW.CDAT) to get the date value?
:NEW.CDAT will be a date. The :new and :old variables in triggers are always the type of the destination field.
I wasn't able to find anything in the Oracle documentation that confirms my statement, but I was able to devise some experimental proof:
CREATE TABLE test2 (a DATE);
CREATE OR REPLACE TRIGGER bu_test2
BEFORE INSERT OR UPDATE
ON test2
FOR EACH ROW
DECLARE
PROCEDURE type_test(in_type DATE) IS
BEGIN
DBMS_OUTPUT.put_line('date');
END;
PROCEDURE type_test(in_type VARCHAR2) IS
BEGIN
DBMS_OUTPUT.put_line('varchar2');
END;
BEGIN
type_test(:new.a);
END;
INSERT INTO test2
VALUES ('24-Mar-2011');
Since type_test is overloaded, Oracle will choose which procedure to use based on the type being passed in. The results of this script are:
Table created.
Trigger created.
date
1 row created.
You need to do a conversion if your session parameter 'NLS_DATE_FORMAT' is not 'mm/dd/yyyy'.
For example:
create table myTbl (id number, cdat date);
select *
from nls_session_parameters ns
where ns.parameter = 'NLS_DATE_FORMAT';
PARAMETER VALUE
-------------------------------------------------------
NLS_DATE_FORMAT DD-MON-RR
In this case, without a to_Date you'll get an error:
insert into myTbl
values
(123, '12/05/2011');
ORA-01843: not a valid month
You can change this paramter at session level, system level, etc.
zep#dev> alter session set NLS_DATE_FORMAT = 'mm/dd/yyyy';
Session altered
select *
from nls_session_parameters ns
where ns.parameter = 'NLS_DATE_FORMAT';
PARAMETER VALUE
-------------------------------------------------------------------------------------
NLS_DATE_FORMAT mm/dd/yyyy
insert into myTbl
(id, cdat)
values
(123, '12/05/2011');
1 row inserted
zep#dev> select *
2 from myTbl;
ID CDAT
---------- -----------
123 05/12/2011
Test on a row level trigger
truncate table Mytbl;
alter session set NLS_DATE_FORMAT = 'DD-MON-RR';
create or replace trigger befins_myTbl
before insert on myTbl
for each row
declare
begin
-- demo
:new.cdat := :new.cdat + numtoyminterval(1,'YEAR');-- (demo trigger add 1 year )
end;
insert into myTbl
(id, cdat)
values
(123, '12/05/2011');
Output: ORA-01843: not a valid month
alter session set NLS_DATE_FORMAT = 'mm/dd/yyyy';
insert into myTbl
(id, cdat)
values
(123, '12/05/2011');
commit;
select *
from myTbl;
Output
ID CDAT
---------- -----------
123 12/05/2012

Resources