update trigger issue in oracle pl sql - sql

I am creating an update trigger on a table called tax_table and inserting :new and :old values into update_audit_table.
How can I incorporate related fields (employee id from employee table) into update_audit_table in the same insert.
CREATE UPDAT_TAX_TRIG
AFTER UPDATE
ON tax_table
FOR EACH ROW
BEGIN
INSERT INTO update_audit_table(PREV_TAX_CODE, OLD_TAX_CODE)
VALUES(:NEW.PREV_TAX_CODE, OLD:PREV_TAX_CODE);
END;
update_audit_table:
employee_id,
employee_name,
prev_tax_code,
old_tax_code
employee table :
employee_id,
employee_name
I am trying to include fields from employee table into update_audit_table while inserting audit record.

As you have currently described the tables, it is impossible as there is no correlation that has been described between the tax_table and the employee table that would let you join the two.
However, if we assume that tax_table has an employee_id column then you can use that:
CREATE UPDAT_TAX_TRIG
AFTER UPDATE ON tax_table
FOR EACH ROW
BEGIN
INSERT INTO update_audit_table(
employee_id,
employee_name,
PREV_TAX_CODE,
OLD_TAX_CODE
) VALUES(
:NEW.employee_id,
(SELECT employee_name FROM employees WHERE employee_id = :NEW.employee_id),
:NEW.PREV_TAX_CODE,
:OLD.PREV_TAX_CODE
);
END;
/
If it does not then you need to work out how the tax_table relates to an employee.

Related

Create a trigger that connects the class teacher and its department to the sub teacher and its department and if they dont match raise an error,

Create a trigger before update that connects the class teacher and their department to the sub teacher and their department and if they dont have the same department raise an error
Teacher Table: tID, ClassteacherNo , subteacherNo (both FK of staffNo)
Staff Table: StaffNo, department
Using Oracle 18c
tried using a join but didnt know how to find the common department between the class teacher and sub
You need no INNER JOIN, you can use :new, to get the staff number and from that you can get the department
CREATE TABLe staff(StaffNo int, department int,
CONSTRAINT staff_pk PRIMARY KEY (StaffNo))
INSERT INTO staff VALUES(1,1)
1 rows affected
INSERT INTO staff VALUES(2,2)
1 rows affected
INSERT INTO staff VALUES(3,1)
1 rows affected
INSERT INTO staff VALUES(4,1)
1 rows affected
CREATE TABLE teachers (tID int , ClassteacherNo int , subteacherNo int
, CONSTRAINT fk_ClassTeacher
FOREIGN KEY (ClassteacherNo)
REFERENCES staff(StaffNo)
, CONSTRAINT fk_SubTeacher
FOREIGN KEY (subteacherNo)
REFERENCES staff(StaffNo))
INSERT INTO teachers VALUES(1,1,3)
1 rows affected
CREATE OR REPLACE TRIGGER teachers_before_update
BEFORE UPDATE
ON teachers
FOR EACH ROW
DECLARE
v_teacher_dep int;
v_subteacher_dep int;
BEGIN
-- Find department for teacher
SELECT department INTO v_teacher_dep
FROM staff
WHERE StaffNo = :new.ClassteacherNo;
-- Find department for subteacher
SELECT department INTO v_subteacher_dep
FROM staff
WHERE StaffNo = :new.subteacherNo;
IF v_subteacher_dep <> v_teacher_dep
THEN
RAISE_APPLICATION_ERROR(-20002,'The deaprtment differs between teacher and subteacher');
END IF;
END;
/
SELECT * FROM USER_ERRORS;
UPDATE teachers SET subteacherNo = 2 WHERE tID = 1
ORA-20002: The deaprtment differs between teacher and subteacher
ORA-06512: at "FIDDLE_UUXCZJDQDWCKIGQJJFBH.TEACHERS_BEFORE_UPDATE", line 19
ORA-04088: error during execution of trigger 'FIDDLE_UUXCZJDQDWCKIGQJJFBH.TEACHERS_BEFORE_UPDATE'
UPDATE teachers SET subteacherNo = 4 WHERE tID = 1
1 rows affected
fiddle

Inserting rows from one table to another starting from row 1?

Starting to learn ORACLE SQL and here is something i dont get.
I have EMPLOYEES table that look like this (hire_date values are nulls)
And another table called EMPHD with hire_date
How can i add HIRE_DATE values from EMPHD to EMPLOYEES table starting from the first row and all the way down?
If i use something like this
INSERT INTO EMPLOYEES (HIRE_DATE)
SELECT HIRE_DATE FROM EMPHD
obviously i end up with hire dates added at the end of the table with all other values as null.
Sry for my english and please help!
One option would be using merge. By using merge you can update hire_date column if ID values are matched. Otherwise only two columns' value might be inserted into employees table as new records but seems it's not the case due to all mutually matching 19 ID values :
merge into employees e
using (select id, hire_date from emphd) h
on ( e.id = h.id )
when matched then update set e.hire_date = h.hire_date
when not matched then insert( e.id, e.hire_date )
values( h.id, h.hire_date );
Demo 1
Alternatively, you can use update statement directly by equality among ID values of those tables :
update employees e
set e.hire_date = (select hire_date from emphd where id = e.id);
Demo 2

I want to replicate an existing record for a new employee ID

I want to replicate an existing employees record for a new employee.
EG.
If my present employee details are:
EmpID: 100
EmpDept: Accounts
EmpEndDate: 12-12-2018
So when I am adding a new employee I dont want to manually write an insert statement, all I want is a procedure that can replicate all the other values present for the new employee.
One approach is to increment the max EmpID by 1 so as to maintain uniqueness of rows - with a CONNECT BY LOOP, if you want multiple rows.
INSERT INTO Employee (
EmpID
,EmpDept
,EmpEndDate
)
WITH m(max_id) AS (
SELECT MAX(EmpID)
FROM Employee
)
SELECT max_id + LEVEL
,EmpDept
,EmpEndDate
FROM Employee
CROSS JOIN m
WHERE EmpID = p_emp_id CONNECT BY LEVEL <= p_numof_rows;
Put this query in a procedure and pass p_emp_id and p_numof_rows as parameter to replicate as many rows as you want. Hopefully you have one row currently in the table for each p_emp_id you would pass.
If you always want a single row, just use max_id + 1 and remove CONNECT BY LEVEL <= p_numof_rows
Demo
Assuming you are using a sequence named Employees__EmpID__Seq to generate the unique employee IDs then:
INSERT INTO Employees ( EmpID, EmpDept, EmpEndDate )
SELECT Employees__EmpID__Seq.NEXTVAL,
EmpDept,
EmpEndDate
FROM Employees
WHERE EmpID = :id_of_the_employee_you_want_to_copy;
You can then run that statement and it will copy an existing record.
all I want is a procedure that can replicate all the other values present for the new employee.
If you really need to wrap it in a procedure:
CREATE PROCEDURE copy_employee(
i_empid IN Employees.EmpID%TYPE,
o_empid OUT Employees.EmpID%TYPE
)
IS
BEGIN
o_empid := Employees__EmpID__Seq.NEXTVAL;
INSERT INTO Employees ( EmpID, EmpDept, EmpEndDate )
SELECT o_empid,
EmpDept,
EmpEndDate
FROM Employees
WHERE EmpID = i_empid;
IF SQL%ROWCOUNT <> 1 THEN
o_empid := NULL;
END IF;
END;
/
SQLFiddle
you can also do it in PL/SQL,
DECLARE
v_emp_rec employee%ROWTYPE;
BEGIN
SELECT *
INTO v_emp_rec
FROM employee
WHERE emp_id = 'emp_id_to_replicate';
v_emp_rec.emp_id := '101';
v_emp_rec.first_name := 'Alfie';
INSERT INTO employee
VALUES
v_emp_rec;
v_emp_rec.emp_id := '102';
v_emp_rec.first_name := 'Alf';
INSERT INTO employee
VALUES
v_emp_rec;
END;
/
COMMIT;

Create a Stored Procedure to Delete Records from employee(first table) and orders(second table) based on input params (id, arrayOfNo [])

I have two tables, employees (company_id, employee_num) and orders (employee_num, order_description).
If user sends procedure params company_id, employe_num (1, [911,912]) then if employees table has more employee numbers with id 1 then we should delete additional employee numbers rows like if it has [911,912,913] employee numbers with 1 then it should delete the row from 913 as emp_num and delete all the records in orders (table two) where emp_num is 913
ex(Procedure):
CREATE OR REPLACE FUNCTION deleteEmployeeOrders(P_company_id bigint, orders_array arrayType[] ) RETURNS INT AS $$
DECLARE
orders_array arrayType;
DECLARE
order arrayType;
DECLARE
status_code integer;
BEGIN
status_code:= -1;
FOR line IN (SELECT * FROM employees where company_id = p_company_id)
LOOP
if exists(select * from employees where emp_num = order.emp_num not in orders_array)
THEN
delete from employees where employee_num = order.employee_num;
DELETE FROM orders WHERE employee_num = order.employee_num;
END LOOP;
status_code = 1;
return status_code;
END;
$$ LANGUAGE plpgsql;
For example
:select deleteEmployeeOrders(1,[911,912])
it should delete 913 as emp no in row table1, and delete all the rows in table2 where emp_num 913
In your example, it sounds like you want to delete all employee (and related order) records for a given company_id. Is that correct? If so, you can use two simple DELETEs:
-- Delete all orders related to employees for given "company_id"
DELETE FROM orders
WHERE employee_num IN (
SELECT employee_num
FROM employees
WHERE company_id = p_company_id
);
-- Delete employees for given "company_id"
DELETE FROM employees
WHERE company_id = p_company_id;
They should be done in the same transaction, so they either all work or all fail. Also you should do the "orders" DELETE first so you can reference the employees in the employees table.
If this isn't what you need, then please explain a little more exactly what you're looking for. I don't quite follow the logic in your example for deleting employee 913 if you only specify employees (911,912).
Updated
Modified the DELETE statements to run against employee_num values NOT present in arrayOfNo array:
-- Delete all orders related to employees for given "company_id"
DELETE FROM orders
WHERE employee_num IN (
SELECT employee_num
FROM employees
WHERE company_id = p_company_id
AND employee_num <> ALL(arrayOfNo) -- Get employees NOT IN arrayOfNo list
);
-- Delete employees for given "company_id"
DELETE FROM employees
WHERE company_id = p_company_id
AND employee_num <> ALL(arrayOfNo) -- Get employees NOT IN arrayOfNo list
;
Again, the two statements should be run as a single transaction. Let me know if that works.

Re-write oracle query to avoid multiple table scan

I hope everyone is fine and learning more. I need some advice on select statement and on its fine tuning. I am using Oracle 11gR2. Please find below table and data scripts.
create table employee (emp_id number, emp_name varchar2(50), manager_id number);
create table department (dept_id number, dept_name varchar2(50), emp_name varchar2(50), manager_level varchar2(20));
create table manager_lookup (manager_level_id number, manager_level varchar2(20));
insert into employee values (1, 'EmpA',3);
insert into employee values (2, 'EmpB',1);
insert into employee values (3, 'EmpC',1);
insert into employee values (4, 'EmpD',2);
insert into employee values (5, 'EmpE',1);
insert into employee values (6, 'EmpF',3);
insert into department values (1, 'DeptA','EmpD','Level3');
insert into department values (2, 'DeptB','EmpC','Level2');
insert into department values (3, 'DeptC','EmpA','Level1');
insert into department values (4, 'DeptD','EmpF','Level1');
insert into department values (5, 'DeptD','EmpA','Level3');
insert into department values (6, 'DeptA',NULL,'Level3');
insert into manager_lookup values (1, 'Level1');
insert into manager_lookup values (2, 'Level2');
insert into manager_lookup values (3, 'Level3');
commit;
Below query is returning me dept_id by passing some emp_name. I need those dept_id where manager_level is same as emp_name passed but don't need to have that same emp_name in result data set.
SELECT b.dept_id
FROM (SELECT DISTINCT manager_level
FROM department dpt
WHERE emp_name = 'EmpA'
and emp_name is not null) a,
department b
WHERE a.manager_level = b.manager_level
AND NVL (b.emp_name, 'ABC') <> 'EmpA';
Above query is returning me data set as below:
dept_id
--------
1
4
6
I want same result set but need to rewrite above query in a way to avoid department table scan two times. This is just sample query but in real time scanning big table two times is giving performance issues. I want to re-write this query in a way to perform better and avoid same table scan two times.
Can you please help on providing your wonderful suggestions or solutions? I will really appreciate all responses.
Thank you for going over the question.
If you want your query to be more efficient, then use indexes:
create index idx_department_name_level on department(emp_name, manager_level)
and
create index idx_department_name_level on department(manager_level, emp_name, dept_id)
also, you have a redundant null check which may be avoiding indexes...
SELECT b.dept_id
FROM (SELECT manager_level
FROM department dpt
WHERE emp_name = 'EmpA') a,
department b
WHERE a.manager_level = b.manager_level
AND NVL (b.emp_name, 'ABC') <> 'EmpA';
post your explain plan for more help
This should work:
SELECT a.*
FROM
(
SELECT d.*,
SUM(CASE WHEN emp_name = 'EmpA' THEN 1 ELSE 0 END)
OVER (PARTITION BY manager_level) AS hits
FROM department d
) a
WHERE hits > 0
AND NVL(emp_name, 'Dummy') <> 'EmpA'
ORDER BY dept_id
;
The query does the following:
Calculate how many times EmpA appears in a given manager_level
Keep all records with a manager_level that has at least one occurrence of EmpA in it
Excluding the EmpA records themselves
SQL Fiddle of the query in action: http://sqlfiddle.com/#!4/a9e03/7
You can verify that the execution plan contains only one full table scan.