trigger in PL/SQL - sql

There are two tables given:
1)
employee(eno,ename,basic,da,gross)
da=basic*(5.0/100)
gross = basic+da
2)
sal_hist(eno, sys_dt, old_basic)
How to write a trigger to update the 'da' and 'gross' whenever I am updating basic salary of the employee?

PL/SQL tag suggests that you use Oracle database.
You said that there's yet another table, sal_hist, but - you didn't say what to do with it. I presume you'd want to save the old basic salary.
In that case, trigger would look like this:
SQL> create or replace trigger trg_biu_emp
2 before insert or update on employee
3 for each row
4 begin
5 insert into sal_hist(eno, sys_dt, old_basic) values
6 (:new.eno, sysdate, :old.basic);
7
8 :new.da := :new.basic * 5 / 100;
9 :new.gross := :new.basic + :new.da;
10 end;
11 /
Trigger created.
Let's see how it works:
SQL> select * From employee;
ENO ENAME BASIC DA GROSS
---------- ----- ---------- ---------- ----------
1 Scott 100 0 0
2 Tiger 500 0 0
SQL> select * From sal_hist;
no rows selected
SQL> update employee set basic = 200 where eno = 1;
1 row updated.
SQL> insert into employee (eno, ename, basic) values (3, 'King', 1000);
1 row created.
SQL> select * From employee;
ENO ENAME BASIC DA GROSS
---------- ----- ---------- ---------- ----------
1 Scott 200 10 210
2 Tiger 500 0 0
3 King 1000 50 1050
SQL> select * From sal_hist;
ENO SYS_DT OLD_BASIC
---------- ------------------- ----------
1 06.06.2020 11:10:49 100
3 06.06.2020 11:12:07
SQL>

CREATE DEFINER=`root`#`localhost` TRIGGER `TRIGGERNAME` AFTER UPDATE ON `employee` FOR EACH ROW
BEGIN
SET employee.da = employee.basic*(5.0/100)
SET employee.gross = employee.basic + (employee.basic*(5.0/100))
END

The way you have them defined both da and gross are derivable directly from base. However as a standard column I can update either of them directly. If this is not desirable, then you can declare them as virtual columns.
alter table employee drop (da, gross);
alter table employee add (
da generated always as base * 0.05 virtual
, gross generated always as base * 1.05 virtual
);
Now neither column can be updated independently and both are automatically updated when base is updated. You use them as normal columns of select, but DO NOT reference then in Insert or Update. And there is NO trigger needed.
See fiddle. Note: You did not specify what Oracle version you are using. This requires 11gR1 or higher.

Related

Oracle trigger multiple conditions in when clause

I'm trying to create a trigger that updates a column in a table when other columns are updated. but getting the following error while saving the trigger
ORA-25000: invalid use of bind variable in trigger WHEN clause
My trigger is as follows, I'm not sure what is wrong with the code.
CREATE OR REPLACE TRIGGER Employees_ARIU
BEFORE INSERT OR UPDATE ON Employees
FOR EACH ROW
WHEN ((nvl(:OLD.EMP_SAL,0) != nvl(:NEW.EMP_SAL,0)) OR (nvl(:OLD.SAL_LEVEL,0) != nvl(:NEW.SAL_LEVEL,0)))
BEGIN
:NEW.LAST_UPDATED = SYSDATE
END;
Although IF is an alternative to WHEN, I'd say that it is better to use WHEN clause whenever possible because it is a
SQL condition that must be satisfied for the database to fire the trigger
So, why would you even let the trigger fire and then conclude that oh, OK, I don't want to do anything, after all? Better not running it at all!
Yes, WHEN clause has its restrictions and you can't put anything you want in there, but - your case isn't one of those.
(more info in Documentation, search for "WHEN clause").
So, for a sample table
SQL> create table employees
2 (id number,
3 emp_sal number,
4 sal_level number,
5 last_updated date);
Table created.
trigger would looks like this:
SQL> create or replace trigger employees_ariu
2 before insert or update on employees
3 for each row
4 when ( nvl(old.emp_sal, 0) <> nvl(new.emp_sal, 0)
5 or nvl(old.sal_level, 0) <> nvl(new.sal_level, 0)
6 )
7 begin
8 :new.last_updated := sysdate;
9 end;
10 /
Trigger created.
Testing:
SQL> insert into employees (id, emp_sal, sal_level) values (1, 100, 1);
1 row created.
SQL> select * from employees;
ID EMP_SAL SAL_LEVEL LAST_UPDATED
---------- ---------- ---------- -------------------
1 100 1 12.06.2021 12:14:17
SQL> update employees set sal_level = 2 where id = 1;
1 row updated.
SQL> select * from employees;
ID EMP_SAL SAL_LEVEL LAST_UPDATED
---------- ---------- ---------- -------------------
1 100 2 12.06.2021 12:14:33
SQL>
I think you can try updating your WHEN condition to IF statement along with few other changes -
CREATE OR REPLACE TRIGGER Employees_ARIU
BEFORE INSERT OR UPDATE ON Employees
FOR EACH ROW
BEGIN
IF ((nvl(:OLD.EMP_SAL,0) != nvl(:NEW.EMP_SAL,0)) OR (nvl(:OLD.SAL_LEVEL,0) != nvl(:NEW.SAL_LEVEL,0))) then
:NEW.LAST_UPDATED := SYSDATE;
END IF;
END;
/
Here is the fiddle.

Oracle SQL Developer dynamic date table name

I'm hoping to dynamically reference last Fridays date in the weekly sales tables in Oracle SQL Developer i.e. SELECT * FROM Sales_DDMMYY
I can do this in SQL Server (DECLARE / SET / EXECUTE) but haven't had any joy with SQL Developer.
Even the ability to create a date variable to be referenced within the code would be a great start.
Stop!
I strongly suggest you not to do that. That's not the way to create a data model. If you have a table which contains values related to different dates, then date should be a column in that table, such as
create table sales
(id number,
datum date,
amount number
);
Insert rows as
insert into sales (id, datum, amount)
select 1, date '2020-06-01', 100 from dual union all
select 2, date '2020-05-13', 240 from dual union all
select 3, date '2020-05-13', 160 from dual;
and use it as
select sum(amount)
from sales
where datum = date '2020-05-13'
That is the way to do it. Naming columns by dates is ... well, close to a suicide.
Aha, now I see: it is a table name that contains dates. Doesn't really matter, my suggestion still stands. Do not do that. Use a date column within a single table.
If you want - and if you can afford it - partition the table on date value. Note that partitioning option exists in Oracle Enterprise Edition which is quite expensive. So - date column it is.
If there's nothing you can do about it, then dynamic SQL it is. For example:
Sample table:
SQL> create table sales_200620 as select * From dept;
Table created.
Function that accepts ddmmyy value as a parameter, composes table name and returns a refcursor:
SQL> create or replace function f_test (par_ddmmyy in varchar2)
2 return sys_refcursor
3 is
4 l_table_name varchar2(30) := 'sales_' || par_ddmmyy;
5 l_rc sys_refcursor;
6 begin
7 open l_rc for 'select * from ' || dbms_assert.sql_object_name(l_table_name);
8 return l_rc;
9 end;
10 /
Function created.
Testing:
SQL> select f_test('200620') from dual;
F_TEST('200620')
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
Without creating a function: use substitution variable:
SQL> select * From &tn;
Enter value for tn: sales_200620
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>

Learning SQL in Free Time

So i have the following table
EMPNO ENAME DEPTN JOB HIREDDATE SALARY
111 Narayan R22 Electrical 26-DEC-99 5000
108 Horen P69 PWD 10-DEC-95 10000
130 Roy A13 Security 15-SEP-01 25000
420 Roy D12 IT 15-SEP-99 2500
100 Allu A13 Security 26-JAN-15 15000
With datatypes
EMPNO NOT NULL NUMBER(38),
ENAME NOT NULL VARCHAR2(20),
DEPTNO VARCHAR2(5),
JOB VARCHAR2(20),
HIREDDATE DATE,
SALARY NUMBER(38)
I want to try to create a stored procedure
a) Changing the hire date of any employee.
b) Performing DML commands (Insert, Update and Delete)
c) Multiplying two numbers
I tried to use this code
CREATE PROCEDURE ChangeHired #EmpNo int, #Hired date
AS
ALTER TABLE Employee01 MODIFY HireDate = #Hired WHERE EmpNo = #EmpNo
GO;
But it doesn't work in Oracle 10g. Need some help.
Edit: UPDATE Employee01 SET HireDate = #Hired WHERE EmpNo = #EmpNo should be used that was a mistake from my side but still the code is not executed.
Please use below query,
update Employee01 set HireDate = #Hired WHERE EmpNo = #EmpNo
You have just created the procedure, to make it work you have to call it, Call the procedure using,
execute procedure_name(parameter1, parameter2);
Alter statement should be used only when modifying your structure of the table. While working with data, please use only DML's.
Syntax you used (or Jim suggested) isn't Oracle. Should be something like this (including that "multiplying" example you asked for):
Sample data (only minimum columns needed for the procedure to work):
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
SQL> select * From employee1;
EMPNO HIREDDATE
---------- ----------
111 08.06.2020
Procedure:
SQL> create or replace procedure changehired(par_empno in int, par_hired in date)
2 is
3 a number := 2;
4 b number := 3;
5 result number;
6 begin
7 update employee1 set
8 hireddate = par_hired
9 where empno = par_empno;
10
11 result := a * b;
12 dbms_output.put_line(a || ' * '|| b || ' = ' || result);
13 end;
14 /
Procedure created.
Testing: once the procedure is created, you have to execute it. In order to see result of dbms_output.put_line, enable serveroutput in tool you use (for SQL*Plus or SQL Developer, execute set serveroutput on).
SQL> set serveroutput on;
SQL> begin
2 changehired(111, date '2020-06-08');
3 end;
4 /
2 * 3 = 6
PL/SQL procedure successfully completed.
SQL> select * from employee1;
EMPNO HIREDDATE
---------- ----------
111 08.06.2020
SQL>

Create a trigger on Delete customer that displays a message, "Customer Number 217 is deleted"

This is a homework problem.
The customer table consists of: customer_num,customer_name,street,city,state,postal_code,balance,credit_limit,rep_num. This table would be linked to the rep table via rep_num, and the orders table via customer_num. I have inserted a row into the customer table, just so I will not be deleting actual data, so my code to insert the row is:
insert into customer
values('217','Big Bird','123 Sesame
Street','Pittsburgh','PA','15301',1.00,100.00,'15');
I then set the server output on:
set serveroutput on;
Here is my code that is giving me the rejection of, "Warning: Trigger created with compilation errors.":
create or replace trigger print_customer_deleted
after delete on customer for each row
begin
delete customer_num
set on customer = on.customer - new.customer_num
where customer_num = :new.customer_num;
dbms_output.put_line('Customer number '||customer_num||
' is deleted');
end;
/
Also, would this be how my trigger would be executed? :
accept item_num prompt 'Enter customer number to be deleted: ';
execute print_customer_deleted('&customer_num');
I've created a TEST table based on Scott's DEPT. Trigger (after delete on DEPT) deletes a row from TEST and displays a message.
SQL> select * From dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL> create table test as select * From dept;
Table created.
SQL> create or replace trigger trg_ad_dept
2 after delete on dept
3 for each row
4 begin
5 delete from test
6 where deptno = :old.deptno;
7
8 dbms_output.put_line('Department #' || :old.deptno ||' is deleted');
9 end;
10 /
Trigger created.
Let's see how it works:
SQL> delete from dept where deptno = 30;
Department #30 is deleted
1 row deleted.
SQL> select * From dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
40 OPERATIONS BOSTON
SQL> select * From test;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
40 OPERATIONS BOSTON
SQL>
Seems to be OK - department #30 has been deleted from both tables & the message was displayed.
Apply it to your situation (I don't have your tables, and you didn't provide test case).

Update column value by 1 in Oracle table conditionally

I have an Oracle table with 4 columns (Name, Phone, Email, Count).
If the user is updating value of Name column, then I need to increment Count column value by 1.
If the user is updating values other than Name column, then I don't need to increment Count column value by 1.
Initially, when the record is inserted, Count should be 0. And every time when Name column is updated, the Count should be incremented by 1 (like 1, 2, 3 .....).
How we can achieve this? I am very new to databases.
Thanks a lot for your help.
You can do that in an update and insert trigger or in your program. The later however requires all possible programs to cooperate. The former (triggers) is black art.
A program can do it like this:
UPDATE Person SET Count=Count+1, Phone='123' WHERE name=`csr` and Phone <> '123';
This will update one or no record (i.e. if phone was already 123 it will do nothing).
BTW: there is no nice solution to insert it if it was missing.
Hmm, you changed your question, updating the Name is problematic if you do not have another primary key, is that really what you want?
If you are looking at plsql procedure, then you can use this method,
I am using 3 input variables,
1.) column name to edit
2.) old value
3.) New value to be updated
SQL> create or replace procedure updateval (colname varchar2,oldval varchar2,newval varchar2) is
2 l_prop varchar2(10);
3 l_newval varchar2(10);
4 l_old_val varchar2(10);
5 begin
6 l_prop:=colname;
7 l_newval:=newval;
8 l_old_val:=oldval;
9 IF (upper(l_prop)='NAME') THEN
10 update TESTING123 set name=l_newval,count=count+1 where name=l_old_val;
11 elsif (upper(l_prop)='PHONE') THEN
12 update TESTING123 set PHONE=l_newval ,count=count+1where PHONE=l_old_val;
13 elsif (upper(l_prop)='EMAIL') THEN
14 update TESTING123 set EMAIL=l_newval ,count=count+1 where EMAIL=l_old_val;
15 END IF;
16 end;
17 /
Procedure created.
SQL>
SQL> select * from testing123;
NAME PHONE EMAIL COUNT
---------- ---------- ---------- ----------
abc1 12345 ABC#a.COM 1
xyz 3435 xyz#a.COM 0
SQL> exec updateval ('NAME','abc1','newabc1');
PL/SQL procedure successfully completed.
SQL>
SQL>
SQL> select * from testing123;
NAME PHONE EMAIL COUNT
---------- ---------- ---------- ----------
newabc1 12345 ABC#a.COM 2
xyz 3435 xyz#a.COM 0