Write a program that gives all employees in Mechanical department,
(i) 15% pay increase.
(ii) display a message displaying how many employees were awarded the increase. if no Employees found then print the message 'No Records found'.
Employee :
columnname Data type constraints
EMPID NUMBER(5) PK
EMP_NAME VARCHAR(25) NOT NULL
SALARY NUMBER(10,2)
DEPT VARCHAR(25)
EMP_ID EMP_NAME SALARY DEPT
101 TOM 54000 MECH
102 WILLIAM 43000 CSE
103 JOHN 34560 MECH
104 SMITH 56000 CSE
105 STEVE 23450 IT
Sample Output : 2 Employee got increment.
and i did like this,
create or replace procedure empsal as
emp employee%rowtype;
sal number ;
cursor cr is select * from employees where dept='mech';
begin
open cr;
loop
fetch cr into emp;
exit when ce%notfound;
sal:=emp.salary+(emp.salary*15/100);
update employee set salary=sal where dept='mech';
end loop;
close cr;
if(sql%found) then
dbms_output.put_line(sql%rowcount);
else
dbms_output.put_line('no records found');
end;
but its shows the COMPILATION ERROR
No need to do make it that complex.
SQL> set serveroutput on
SQL> create or replace procedure empsal as
2 begin
3 update employee set
4 salary = salary * 1.15
5 where dept = 'MECH';
6
7 dbms_output.put_line(case when sql%rowcount = 0 then 'No records found'
8 else sql%rowcount || ' employees got increment'
9 end);
10 end;
11 /
Procedure created.
SQL> exec empsal;
2 employees got increment
PL/SQL procedure successfully completed.
SQL>
As of your compilation errors:
if table name is employee, don't use employees (while declaring a cursor)
if cursor name is cr, don't use ce for it (exit statement)
if misses end if
As of logical errors:
if department name is MECH, don't reference it as mech (letter case matters)
doing it in a loop - and without where clause in update statement - you're increasing salary for everyone as many times as there are employees in the MECH department.
in if, you're referencing a cursor that is already closed so ... no use of it
Related
Pretty new to Oracle, need help on this Procedure, using Oracle 11g
Sample table data (TAB1):
ID
Amount
Currency
10
300
GBP
15
500
GBP
20
100
GBP
Requirement is to select all the ID's from TAB1 based on currency and store it in a variable and later use these ID's in other select Queries within the same Stored Procedure.
CREATE OR REPLACE PROCEDURE myproc (i_id IN VARCHAR, i_curr IN VARCHAR)
AS
CURSOR GET_I IS
SELECT ID, CURRENCY
FROM TAB1
WHERE CURRENCY = 'GBP';
-- This will give me 3 ID's (10, 15 & 20) which I am storing in variable r_1 below.
r_1 VARCHAR (5) : NULL;
BEGIN
OPEN GET_I;
LOOP
FETCH GET_I INTO r_1;
IF GET_I%NOTFOUND
THEN
EXIT;
---In the below ELSE PART can we run a select query using the value stored in r_1??
--ELSE
--Data stored in r_1 to be used in further select queries in later part and output of the below
--be returned as SYS_REFCURSOR;
--BELOW two lines gives error
--FOR I in r_1 (
--SELECT ID FROM TAB2 WHERE TAB2.ID=r_1);
END IF;
END LOOP;
CLOSE GET_ID;
END;
It looks you want to use a nested FOR loop.
I'd suggest you to use cursor FOR loops - they are easier to maintain as you don't have to declare cursor variable (by the way, in your case it wouldn't work anyway as you'd like to store both ID and CURRENCY into a scalar R_1 variable), open the cursor, pay attention about exiting the loop and closing the cursor. In a cursor FOR loop, Oracle does all that for you.
Here's an example:
Sample table:
SQL> select * from tab1;
ID AMOUNT CUR
---------- ---------- ---
10 300 GBP
15 500 GBP
20 100 GBP
Procedure:
SQL> create or replace procedure myproc as
2 begin
3 for cur_id in (select id from tab1) loop
4 dbms_output.put_line('ID = ' || cur_id.id);
5 for cur_other in (select amount, currency
6 from tab1
7 where id = cur_id.id --> use ID fetched in outer loop
8 )
9 loop
10 dbms_output.put_line(cur_other.amount ||' - '|| cur_other.currency);
11 end loop;
12 end loop;
13 end;
14 /
Procedure created.
Testing:
SQL> set serveroutput on
SQL> exec myproc;
ID = 10
300 - GBP
ID = 15
500 - GBP
ID = 20
100 - GBP
PL/SQL procedure successfully completed.
SQL>
How to return a refcursor?
SQL> create or replace procedure myproc (par_id in tab1.id%type,
2 par_rc out sys_refcursor) as
3 begin
4 for cur_id in (select id
5 from tab1
6 where id = par_id
7 ) loop
8
9 open par_rc for select amount, currency
10 from tab1
11 where id = cur_id.id;
12 end loop;
13 end;
14 /
Procedure created.
SQL> var l_rc refcursor
SQL>
SQL> exec myproc(10, :l_rc);
PL/SQL procedure successfully completed.
SQL> print l_rc
AMOUNT CUR
---------- ---
300 GBP
SQL>
I want to retrieve all the information about each department from the DEPARTMENT table and display the information on the screen.
Column name Data type Constraints
DEPARTMENT_ID NUMBER(5) PK
DEPARTMENT_NAME VARCHAR2(25) NOT NULL
LOCATION_ID VARCHAR2(15)
Sample Output:
Department Details are :
1000, ADMIN, HQ-101
1010, DEVELOPMENT, CBE-103
1020, TESTING, CHN-102
I have a code which is as follows-
set serveroutput on;
declare
v_dno department.department_id%type;
v_dname department.department_name%type;
v_loc department.location_id%type;
begin
dbms_output.put_line('Department Details are :');
loop
dbms_output.put_line(v_dno || ', ' || v_dname || ', ' || v_loc);
end loop;
commit;
end;
/
But this isn't producing any output, please help. Thanks in advance!
Yes, a simple way to do that is to use a loop. But, you're looping through nothing (never fetch anything into those variables) and never exit the loop. Besides, what exactly are you committing?
Here's how you might have done it (based on Scott's DEPT table which is similar to yours):
SQL> set serveroutput on
SQL> begin
2 for cur_r in (select deptno, dname, loc from dept) loop
3 dbms_output.put_line(cur_r.deptno ||' '|| cur_r.dname ||' '|| cur_r.loc);
4 end loop;
5 end;
6 /
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
PL/SQL procedure successfully completed.
SQL>
I want to create a procedure that deletes rows from the Employee table. It should accept 1 parameter that is department name; only delete the employee records belonging to that department. Display how many employees were deleted else raise "DeptNotFoundException" and print the message 'No Records found.'.
EMPLOYEE:
Column name Data type Constraints
EMP_ID NUMBER(5) PK
EMP_NAME VARCHAR2(25) NOT NULL
SALARY NUMBER(10,2)
DEPT VARCHAR2(25)
Sample Output:
2 Employee record(s) got deleted.
The code I tried is -
create or replace procedure DELETE_EMPLOYEE(v_dept IN EMPLOYEE.dept%TYPE)
is
begin
delete EMPLOYEE where dept = v_dept;
commit;
end;
/
It works fine to delete the rows, but I don't know how to find the count of the deleted rows and display it in the result, please help regarding this. Thank you for help.
Hint: sql%rowcount.
SQL> create or replace procedure DELETE_EMPLOYEE(v_dept IN EMPLOYEE.dept%TYPE)
2 is
3 begin
4 delete EMPLOYEE where dept = v_dept;
5 dbms_output.put_Line('deleted ' || sql%rowcount || ' row(s)');
6 end;
7 /
Procedure created.
SQL> set serveroutput on
SQL> exec delete_employee(10);
deleted 3 row(s)
PL/SQL procedure successfully completed.
SQL>
Also, I wouldn't commit in the procedure; let the caller decide and commit (or not).
lets say employee table is as below
EID ENAME DEPTNO SALARY
1 john 10 100
2 jau 10 300
3 cau 10 200
4 cha 20 200
5 cwea 20 500
6 dan 20 200
7 an 20 300
I have to check if any new employee is added, the new employee salary should be greater than the average salary in that department, and this should be done in triggers.
so I have created trigger as below
create or replace trigger tg_emp before insert on employee for each row
declare
avgsal number;
highsalary EXCEPTION;
BEGIN
select avg(salary) into avgsal from employee where deptno = :NEW.deptno;
if :NEW.salary < avgsal
then
raise highsalary;
end if;
EXCEPTION
when highsalary then
Raise_Application_Error (-20343, 'salary is less than the avg salary in this
department');
WHEN others THEN
Raise_Application_Error (-20353, 'other error probably table mutation
error');
END;
as you know with this code it works for only individual inserts like below
insert into employee values (8, 'jj', 10, 500);
but if it is a multiple inserts at once like
insert into employee
select seq_emp.next, 'ffgg', 10, 400 from all_tab_columns where rownum < 5;
it throws table mutation error(I know the above insert does not make sense but I am using it as just an example for multi insert in one statement).
so how can we resolve this using global temporary tables?
I think I was able to solve it using 1 GTT and 1 before statement trigger and 1 before row trigger as below
CREATE GLOBAL TEMPORARY TABLE employee_GTT (
id NUMBER,
name VARCHAR2(20),
deptno number,
salary number
)
ON COMMIT DELETE ROWS;
statement level before trigger
create or replace trigger emp_avg_load before insert on employee
begin
insert into dept_avg
select deptno, avg(salary), count(deptno) from employee group by deptno;
dbms_output.put_line('getting data from GTT');
end;
row level before trigger
create or replace trigger tg_emp before insert on employee for each row
declare
avgsal number;
ct number;
highsalary EXCEPTION;
BEGIN
avgsal := :new.salary;
select avgsal, count into avgsal, ct from dept_avg where deptno =
:NEW.deptno;
if :NEW.salary < avgsal
then
raise highsalary;
else
update dept_avg
set count = count +1,
avgsal = (avgsal+:NEW.salary)/(count+1)
where deptno = :NEW.deptno;
end if;
EXCEPTION
when highsalary then
Raise_Application_Error (-20343, 'salary is less than the avg salary in this
department');
WHEN others THEN
Raise_Application_Error (-21343, 'some other error');
END;
Please correct me if I get it wrong.
The way you use the temporary table is good idea at first.
But the way you update the average salary during the transaction looks wrong to me. Indeed, depending on how Oracle handles the update (order of inserts), you won't have the same results.
First, since you only insert employee when salary is above average, then average can only increase.
Now , if highest salary is inserted first, then average might increase too much for the next employee to be inserted.
You are having difficulties because your requirement isn't clear enough.
I do not think it is real use case to change the average salary while you are inserting the rows. You must think of it at a transaction level: thus, average salary is defined before transaction happens. So no need to have the average change while you are inserting. Just let the average stay the same during transaction.
I would remove this part:
else
update dept_avg
set count = count +1
, avgsal = (avgsal+:NEW.salary)/(count+1)
where deptno = :NEW.deptno;
What am I doing wrong here?
Create a procedure that accepts a department number, computes the total basic salaries for that department, and displays both the department number and the total basic salaries.
CREATE OR REPLACE PROCEDURE DEPT_TOTAL_SALARY AS
BEGIN
SELECT DEPARTMENTS.DEPARTMENTS_ID, SUM(SALARY) AS TOTAL_SALARIES
FROM EMPLOYEES, DEPARTMENTS
WHERE EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID
DBMS_OUTPUT.PUT_LINE (‘*** TOTAL SALARY CALCULATED ***’);
END;
/
Error at line 6: PL/SQL: ORA-00933: SQL command not properly ended
FROM EMPLOYEES, DEPARTMENTS
WHERE EMPLOYEES.DEPARTMENT_ID = DEPARTMENTS.DEPARTMENT_ID
DBMS_OUTPUT.PUT_LINE ('* TOTAL SALARY CALCULATED *');
END;
/
Table: EMPLOYEES
EMPLOYEE_ID EMPLOYEE_NAME JOB_ID JOB_DESCRIPTION HOD HIREDATE SALARY DEPARTMENT_ID
1 SMITH J1 CLERK 5 17-Dec-07 5,800.00 20
2 ALLEN J2 SLAESMAN 3 20-Feb-08 7,600.00 30
3 WARD J2 SALESMAN 5 22-Feb-08 8,250.00 30
4 JONES J3 MANAGER 7 2-Apr-08 4,900.00 20
5 JACK J2 SALESMAN 7 28-Sep-08 6,700.00 10
6 BLAKE J3 MANAGER 7 1-May-08 12,850.00 30
7 CLARK J3 MANAGER NULL 9-Jun-08 22,450.00 10
8 SCOTT J4 ANALYST 5 9-Dec-08 13,000.00 20
9 TURNER J2 SALESMAN 3 8-Sep-07 5,500.00 30
10 ADAMS J1 CLERK 5 12-Jan-07 9,100.00 20
Table: DEPARTMENTS
DEPARTMENT_ID DEPARTMENT_NAME LOCATION
10 ACCOUNTING NEW YORK
20 PRODUCTION MINNESOTA
50 SALES CHICAGO
40 OPERATIONS BOSTON
Will the query code below execute the following if the DeptNum is 30? If not, how can I write a code to calculate all the salary of each department according to the EMPLOYEES table above?
DeptNum Total_Salary
-------- ------------
30 34,200.00
CREATE OR REPLACE PROCEDURE Dept_Total_Salary (deptNum NUMBER)
AS
total_salary NUMBER(12, 2);
BEGIN
SELECT SUM(Salary)
INTO total_salary
FROM Employees
WHERE Department_ID = deptNum;
DBMS_OUTPUT.PUT_LINE('Department ' || deptNum || ' salary is ' || total_salary);
END;
The salary and the department number are both in the EMPLOYEES table, so there's no need to include the DEPARTMENTS table in your query.
The errors in your query are:
You're not accepting the department number as a parameter to the stored procedure.
You're including the DEPARTMENTS table when you don't have to.
You're not filtering by department number with a WHERE clause.
You need to select the total salary into a variable to print it.
You need a semicolon after the SELECT query.
Try something like this:
CREATE OR REPLACE PROCEDURE Dept_Total_Salary (deptNum NUMBER)
AS
total_salary NUMBER(12, 2);
BEGIN
SELECT SUM(Salary)
INTO total_salary
FROM Employees
WHERE Department_ID = deptNum;
DBMS_OUTPUT.PUT_LINE('Department ' || deptNum || ' salary is ' || total_salary);
END;
Edit: forgot the semicolon after END, and right after I'd pointed out how you missed a semicolon - ouch!
accepts a department number
The first thing you're doing wrong is ignoring this aspect of the requirements. You need to write a procedure that takes a department number and uses a where clause to limit what you retrieve from the table.
Here is how I solved this.
CREATE TABLE proTable -- create new table to store sum of sal for each dept
(deptno NUMBER,
sumSal NUMBER);
CREATE OR REPLACE PROCEDURE display_dept_sal_sum (v_deptno NUMBER)
AS
BEGIN
INSERT INTO proTable (deptno,SumSal)
SELECT v_deptno,SUM(sal)
FROM employees
WHERE deptno = v_deptno;
END;
EXECUTE display_dept_sal_sum(40);
SELECT * FROM proTable;