Calculating overtime using rows from 3 different tables - sql

I'm fairly new to SQL and I am trying to create a trigger to calculate overtime worked by subtracting hours contracted (GRADE_HOURS) in the COMPANY_GRADE table from hours worked (TIMESHEET_HOURS) in the COMPANY_TIMESHEET table. This will then go into the TIMESHEET_OT column of the COMPANY_TIMESHEET table whenever a row is inserted into the COMPANY_TIMESHEET table.
The tables concerned are as follows:
CREATE TABLE COMPANY_TIMESHEET
(
timesheet_ID number(3) constraint timesheet_pk primary key,
Timesheet_emp number(3) constraint timesheet_empnotnull not null references company_employee,
Timesheet_wc date constraint timesheet_wcnotnull not null,
Timesheet_hours number(2),
Timesheet_OT number(2) default 0,
Timesheet_approved number(3) references company_employee
);
CREATE TABLE COMPANY_GRADE
(
grade_ID number(3) constraint grade_pk primary key,
Grade_rate number(5,2) constraint grade_ratenotnull not null,
Grade_hours number(2)
) ;
CREATE TABLE COMPANY_EMPLOYEE
(
emp_ID number(3) constraint emp_pk primary key,
Emp_firstname varchar2(50) constraint emp_firstnamenotnull not null,
Emp_surname varchar2(50),
Emp_department number(2) constraint employeeFKdepartment references company_department,
emp_street varchar2(50),
emp_town varchar2(50),
emp_district varchar2(50),
Emp_grade number(3) default 4 constraint checkempgrade check (Emp_grade between 1 and 9) references company_grade,
Emp_site varchar2(30) default 'LONDON'
);
I would appreciate any help as I have been trying for many hours now only to be met by error after error.
I have tried numerous variations of this as a starting block to try and pass the hours contracted into a variable to then subtract from another variable:
CREATE OR REPLACE TRIGGER trg_ot
BEFORE INSERT ON company_timesheet
FOR EACH ROW
DECLARE t_contracted NUMBER;
BEGIN
SELECT grade_hours INTO t_contracted
FROM company_grade
WHERE company_employee.emp_id = :new.timesheet_emp;
END;
/

CREATE OR REPLACE TRIGGER trg_ot
BEFORE INSERT ON company_timesheet
FOR EACH ROW
DECLARE t_contracted NUMBER;
BEGIN
SELECT g.grade_hours
INTO t_contracted
FROM company_grade g
INNER JOIN
company_employee e
ON ( e.emp_grade = g.grade_id )
WHERE e.emp_id = :new.timesheet_emp;
:new.timesheet_ot := :new.timesheet_hours - t_contracted;
END;
/

Related

I am trying to create a procedure in oracle pl/sql but cant really put in the exact logic for what I am trying to do

When it takes input , email is given and all other parameters are null it should show all records of that email. Similarly with other three input paramters.
If all inputs are null then it should show all records of the student table.
create or replace procedure get_student_records(
pis_email in varchar2,
pis_rollno in number,
pis_dept in varchar,
pi_class_id in number
)
IS
begin
end get_student_records;
Tables to be accessed :
CREATE TABLE student (
s_id NUMBER(10) NOT NULL,
s_rollno NUMBER(10) NOT NULL,
s_name VARCHAR2(20) NOT NULL,
s_dept VARCHAR2(10),
s_email VARCHAR2(25),
class_id NUMBER(10),
CONSTRAINT student_pk PRIMARY KEY ( s_rollno ),
CONSTRAINT fk_class FOREIGN KEY ( class_id )
REFERENCES class ( class_id )
);
CREATE TABLE class (
class_id NUMBER(10) NOT NULL,
class_name VARCHAR2(10) NOT NULL,
dept_id NUMBER(10),
CONSTRAINT class_pk PRIMARY KEY ( class_id ),
CONSTRAINT fk_depts FOREIGN KEY ( dept_id )
REFERENCES dept ( dept_id )
);
CREATE TABLE dept (
dept_id NUMBER(10) NOT NULL,
dept_name VARCHAR2(50) NOT NULL,
CONSTRAINT depts_pk PRIMARY KEY ( dept_id )
);
There is a trick you can use -- lets say in_email, in_name are parameters and the fields are name and email -- the query looks like this:
SELECT *
FROM table as t
WHERE COALESCE(in_name,t.name) = name AND COALESCE(in_email,t.email) = email
The way this works -- when the parameter is null the statement is true (so all are found -> it is a wildcard. When there is a value, it only matches on the exact value.

Alter table structure without violating primary key and unique constraints

Here is a logical model that I have.
Below are table structures and their constraints
Doctor
CREATE TABLE doctor (
doctor_id NUMBER(4) NOT NULL,
doctor_title VARCHAR2(2) NOT NULL,
doctor_fname VARCHAR2(50),
doctor_lname VARCHAR2(50),
doctor_phone CHAR(10) NOT NULL
);
ALTER TABLE doctor ADD CONSTRAINT doctor_pk PRIMARY KEY ( doctor_id );
Procedure
CREATE TABLE procedure (
proc_code NUMBER(5) NOT NULL,
proc_name VARCHAR2(100) NOT NULL,
proc_description VARCHAR2(300) NOT NULL,
proc_time NUMBER(3) NOT NULL,
proc_std_cost NUMBER(7, 2) NOT NULL
);
ALTER TABLE procedure ADD CONSTRAINT procedure_pk PRIMARY KEY ( proc_code );
ALTER TABLE procedure ADD CONSTRAINT proc_name_unq UNIQUE ( proc_name );
Admission
CREATE TABLE admission (
adm_no NUMBER(6) NOT NULL,
adm_date_time DATE NOT NULL,
adm_discharge DATE,
patient_id NUMBER(6) NOT NULL,
doctor_id NUMBER(4) NOT NULL
);
ALTER TABLE admission ADD CONSTRAINT admission_pk PRIMARY KEY ( adm_no ); --surrogate key
ALTER TABLE admission ADD CONSTRAINT admission_nk UNIQUE ( patient_id,
adm_date_time );
Admission Procedure
CREATE TABLE adm_prc (
adprc_no NUMBER(7) NOT NULL,
adprc_date_time DATE NOT NULL,
adprc_pat_cost NUMBER(7, 2) NOT NULL,
adprc_items_cost NUMBER(6, 2) NOT NULL,
adm_no NUMBER(6) NOT NULL,
proc_code NUMBER(5) NOT NULL,
request_dr_id NUMBER(4) NOT NULL,
perform_dr_id NUMBER(4)
);
ALTER TABLE adm_prc ADD CONSTRAINT adm_prc_pk PRIMARY KEY ( adprc_no ); --surrogate key
ALTER TABLE adm_prc ADD CONSTRAINT adm_prc_nk UNIQUE ( adprc_date_time,
adm_no );
ALTER TABLE adm_prc
ADD CONSTRAINT admission_admprc FOREIGN KEY ( adm_no )
REFERENCES admission ( adm_no );
ALTER TABLE adm_prc
ADD CONSTRAINT doctor_performadmprc FOREIGN KEY ( perform_dr_id )
REFERENCES doctor ( doctor_id );
ALTER TABLE adm_prc
ADD CONSTRAINT doctor_requestadmprc FOREIGN KEY ( request_dr_id )
REFERENCES doctor ( doctor_id );
Item Treatment
CREATE TABLE item_treatment (
adprc_no NUMBER(7) NOT NULL,
item_code CHAR(5) NOT NULL,
it_qty_used NUMBER(2) NOT NULL,
it_item_total_cost NUMBER(8, 2) NOT NULL
);
ALTER TABLE item_treatment
ADD CONSTRAINT item_treatment_pk PRIMARY KEY ( adprc_no,item_code);
ALTER TABLE item_treatment
ADD CONSTRAINT admprc_itemtreatment FOREIGN KEY ( adprc_no )
REFERENCES adm_prc ( adprc_no );
ALTER TABLE item_treatment
ADD CONSTRAINT admprc_itemtreatment FOREIGN KEY ( adprc_no )
REFERENCES adm_prc ( adprc_no );
ALTER TABLE item_treatment
ADD CONSTRAINT item_itemtreatment FOREIGN KEY ( item_code )
REFERENCES item ( item_code );
In a fictitious hospital, every time an admission procedure is completed for a patient admission, the lead doctor who performed the procedure is recorded under the perform_dr_id in adm_prc table. Even if a team of doctors perform the procedure, only the lead doctor is recorded.
The hospital now wishes to record all the doctors who performed in the admission procedure including an Ancillary doctor (assisting doctor).
I have altered the adm_prc table to include ancillary doctors
ALTER TABLE ADM_PRC ADD ANCILLARY_DR_ID NUMBER(4);
ALTER TABLE ADM_PRC
ADD CONSTRAINT DOCTOR_PERFORM_ANCILLARY FOREIGN KEY ( ANCILLARY_DR_ID )
REFERENCES DOCTOR ( DOCTOR_ID );
I need to change the structure of the database is such a way that, there should be multiple records for the same admission procedure as there can be many doctors assisting in a single admission procedure. For example, I should be able to insert the following records in adm_prc table
adprc_no adprc_date_time adprc_pat_cost adprc_items_cost adm_no proc_code
request_dr_id perform_dr_id ancillary_dr_id
-----------------------------------------------------------------------------------------------------------------------------
1 14/03/2019 100 100 1234 1234
10 10 12
1 14/03/2019 100 100 1234 1234
10 10 13
However, this violates primary key constraint adm_prc_pk
I'm stuck at this point and unable to proceed further. Would appreciate if someone could point me in the right direction.
You should create a new satellite table (say ADM_PRC_DR_DTLS) to hold adprc_no and corresponding dr_id details.
adprc_no of ADM_PRC_DR_DTLS would refer to the ADM_PRC's adprc_no as a Foreign Key.

Foreign key missing parenthesis

CREATE TABLE EMPLOYEE (
EID CHAR(3) NOT NULL PRIMARY KEY,
ENAME VARCHAR2(50) NOT NULL,
JOB_TYPE VARCHAR2(50) NOT NULL,
MANAGER CHAR(3) FOREIGN KEY REFERENCES EMPLOYEE(EID),
HIRE_DATE DATE NOT NULL,
DNO INTEGER FOREIGN KEY REFERENCES DEPARTMENT(DNO),
COMMISSION DECIMAL(10,2),
SALARY DECIMAL(7,2) NOT NULL,
);
CREATE TABLE DEPARTMENT (
DNO INT NOT NULL PRIMARY KEY,
DNAME VARCHAR(50),
LOCATION VARCHAR(50) DEFAULT('NEW DELHI')
);
in creation of the employee table
this is giving me an error of right parenthesis
and the department table is already created
You have an extra comma in the line
SALARY DECIMAL(7,2) NOT NULL,
Delete that comma and the Employee table should be created.
You need to Create the Department Table first to use one of its
Columns as FOREIGN KEY.
Also, check your database. There might already be a Department Table. To avoid getting that error when the table needed is already created, use the keyword IF NOT EXISTS
CREATE TABLE IF NOT EXISTS Department(
DNO INT NOT NULL PRIMARY KEY,
DNAME VARCHAR(50),
LOCATION VARCHAR(50) DEFAULT('NEW DELHI')
);
The error is caused by the trailing comma, but you have other issues:
The FOREIGN KEY is not needed for an inline reference.
You need to define the tables in the right order.
So . . .
CREATE TABLE DEPARTMENT (
DNO INT NOT NULL PRIMARY KEY,
DNAME VARCHAR(50),
LOCATION VARCHAR(50) DEFAULT('NEW DELHI')
);
CREATE TABLE EMPLOYEE (
EID CHAR(3) NOT NULL PRIMARY KEY,
ENAME VARCHAR2(50) NOT NULL,
JOB_TYPE VARCHAR2(50) NOT NULL,
MANAGER CHAR(3) REFERENCES EMPLOYEE(EID),
HIRE_DATE DATE NOT NULL,
DNO INTEGER REFERENCES DEPARTMENT(DNO),
COMMISSION DECIMAL(10,2),
SALARY DECIMAL(7,2) NOT NULL
);
Here is an example of it working.

SQL Creating Table Dependencies

I've been stuck at creating tables.
Here is the code I am stuck at how to make the code work in loan_type to write office worker or non office worker
create table loaner
(
loan_id number(5) primary key,
loan_type VARCHAR2 (16),
loan_start_date date,
loan_end_date date,
)
create table office_worker
(
worker_id number(5) primary_key,
loan_id number(5) references loaner(loan_id),
worker_name varchar2(50)
)
create table nonoffice_worker
(
nonworker_id number(5) primary_key,
loan_id number(5) references loaner(loan_id),
nonworker_name varchar2(50)
);
commit;
You can't create a constrain to check that with the existing table structures. A common way to do it is like this:
create table loaner (
loan_id number(5) primary key,
loan_type VARCHAR2 (16),
loan_start_date date,
loan_end_date date,
constraint loaner_uk unique (loan_id, loan_type)
);
create table office_worker (
worker_id number(5) primary_key,
loan_id number(5),
loan_type VARCHAR2 (16),
worker_name varchar2(50),
constraint office_worker_loaner_fk foreeign key (loan_id, loan_type) references loaner (loan_id, loan_type),
constraint office_worker_loan_type_chk check (loan_type = 'OFFICE')
);
create table nonoffice_worker (
nonworker_id number(5) primary_key,
loan_id number(5),
loan_type VARCHAR2 (16),
nonworker_name varchar2(50),
constraint nonoffice_worker_loaner_fk foreeign key (loan_id, loan_type) references loaner (loan_id, loan_type),
constraint nonoffice_worker_loan_type_chk check (loan_type = 'NONOFFICE')
);
That is:
Create a redundant UNIQUE constraint in (load_id, loan_type) in the first table.
Add loan_type to the subtype tables and base the foreign key on (loan_id, loan_type).
Add a check constraint to each subtype table to ensure the correct loan_type is used.
You can also add a check constraint to your table, but you must check that column loan_type only contains desired values(office worker or non office worker), else this won't work:
alter table loaner add (CONSTRAINT chk_loan_type CHECK
(loan_type='office worker' or loan_type='non office worker'));

On update triggering in oracle?

I create 2 tables employees and customers.The employees table code is here:
create table employees(
employeeNumber number not null,
lastName varchar2(30) not null,
firstName varchar2(30) not null,
email varchar2(50) not null,
officeCode varchar2(10) not null,
assignTo number default null,
jobTitle varchar2(100) not null,
primary key (employeeNumber),
foreign key (officeCode) references offices(officeCode),
foreign key (assignTo) references employees(employeeNumber)
);
here assignTo is a foreign key of his own table employees and employeeNumber is auto increment.Some insertion sample is here:
insert into employees (lastName,firstName,email,officeCode,jobTitle)
values ('hasan','rumy','md.rejaulhasanrumy#gmail.com','123','manager');
insert into employees (lastName,firstName,email,officeCode,assignTo,jobTitle)
values ('hasan','rakib','kalorakib#gmail.com','123', 1 ,'assistant manager');
The customer table code is here:
create table customers (
customerNumber number not null,
customerName varchar2(50) not null,
phone varchar2(20) not null,
address varchar2(70) not null,
city varchar2(50) not null,
postalCode varchar2(15) not null,
country varchar2(40) not null,
salesRepEmployeeNumber number default null,
primary key(customerNumber),
foreign key (salesRepEmployeeNumber) references employees (employeeNumber)
);
customerNumber is auto increment.some sample insertion is here:
insert into customers
(customerName,phone,address,city,postalCode,country,salesRepEmployeeNumber)
values ('roxy','017456','holy park','kolia','Z143','something',1);
Now I create a trigger which execute before update employeeNumber column of employees table for on update cascade and the code is here:
create or replace trigger employees_update
before update of employeeNumber on employees
for each row
begin
update employees
set
assignTo = :new.employeeNumber
where assignTo = :old.employeeNumber;
update customers set
salesRepEmployeeNumber = :new.employeeNumber
where salesRepEmployeeNumber = :old.employeeNumber;
end;
/
above all is right in oracle but the problem is when I update employees table.The update code is here:
update employees set employeeNumber = 134 where employeeNumber = 1;
the problem is here:
ORA-04091: table RUMY.EMPLOYEES is mutating, trigger/function may not see it
ORA-06512: at "RUMY.EMPLOYEES_UPDATE", line 2
ORA-04088: error during execution of trigger 'RUMY.EMPLOYEES_UPDATE'
1. update employees set employeeNumber = 134 where employeeNumber = 1;
As far I know it's a system problem so where I make mistake?Can not I make foreign key assignTo of employees table?Also notice that same thing work properly in mysql.Advance thanks for answering this long question.
As you have discovered, you cannot select from the same table that a row-level trigger is defined against; it causes a table mutating exception.
Then - assuming at least Oracle 11, this will need to be split into individual triggers in earlier versions
CREATE OR REPLACE TRIGGER employees_update
FOR UPDATE ON employees
COMPOUND TRIGGER
TYPE employeeNumberRec IS RECORD
(oldEmployeeNumber employees.employeeNumber%TYPE
,newEmployeeNumber employees.employeeNumber%TYPE);
TYPE employeeNumbersTbl IS TABLE OF employeeNumberRec;
g_employeeNumbers employeeNumbersTbl;
BEFORE STATEMENT
IS
BEGIN
-- Reset the internal employees table
g_employeeNumbers := employeeNumbersTbl();
END BEFORE STATEMENT;
AFTER EACH ROW
IS
BEGIN
-- Store the updated employees
IF :new.employeeNumber <> :old.employeeNumber THEN
g_employeeNumbers.EXTEND;
g_employeeNumbers(g_employeeNumbers.LAST).oldEmployeeNumber := :old.employeeNumber;
g_employeeNumbers(g_employeeNumbers.LAST).newEmployeeNumber := :new.employeeNumber;
END IF;
END AFTER EACH ROW;
AFTER STATEMENT
IS
BEGIN
-- Now update the child tables
FORALL l_index IN 1..g_employeeNumbers.COUNT
UPDATE employees
SET assignTo = g_employeeNumbers(l_index).newEmployeeNumber
WHERE assignTo = g_employeeNumbers(l_index).oldEmployeeNumber;
FORALL l_index IN 1..g_employeeNumbers.COUNT
UPDATE customers
SET salesRepEmployeeNumber = g_employeeNumbers(l_index).newEmployeeNumber
WHERE salesRepEmployeeNumber = g_employeeNumbers(l_index).oldEmployeeNumber;
END AFTER STATEMENT;
END;
EDIT
In addition you will need to make the foreign key constraints that reference this table deferred e.g.
CREATE TABLE employees
(employeeNumber NUMBER NOT NULL
,lastName VARCHAR2(30) NOT NULL
,firstName VARCHAR2(30) NOT NULL
,email VARCHAR2(50) NOT NULL
,officeCode VARCHAR2(10) NOT NULL
,assignTo NUMBER DEFAULT NULL
,jobTitle VARCHAR2(100) NOT NULL
,PRIMARY KEY (employeeNumber)
,FOREIGN KEY (officeCode)
REFERENCES offices (officeCode)
,FOREIGN KEY (assignTo)
REFERENCES employees (employeeNumber)
DEFERRABLE
INITIALLY DEFERRED
)
and
CREATE TABLE customers
(customerNumber NUMBER NOT NULL
,customerName VARCHAR2(50) NOT NULL
,phone VARCHAR2(20) NOT NULL
,address VARCHAR2(70) NOT NULL
,city VARCHAR2(50) NOT NULL
,postalCode VARCHAR2(15) NOT NULL
,country VARCHAR2(40) NOT NULL
,salesRepEmployeeNumber NUMBER DEFAULT NULL
,PRIMARY KEY (customerNumber)
,FOREIGN KEY (salesRepEmployeeNumber)
REFERENCES employees (employeeNumber)
DEFERRABLE
INITIALLY DEFERRED
)
Note: if the constraint is violated this will cause an error at COMMIT not after an individual DML statement.
You can re-write the above trigger in following ways to avoid the issues:
create or replace trigger employees_update
before update of employeeNumber on employees
for each row
begin
emp_upd_trg_proc(:new.employeeNumber,:old.employeeNumber);
update customers set
salesRepEmployeeNumber = :new.employeeNumber
where salesRepEmployeeNumber = :old.employeeNumber;
end;
/
create or replace procedure emp_upd_trg_proc
(new number, old number)
is
pragma autonomous_transaction;
begin
update employees
set
assignTo = new
where assignTo = old;
commit;
end;
/