PL SQL Query for grouping Department names - sql

I am new to PL/SQL and I am stuck at this one point where I am unable to group data by department names. my query is as follows
DECLARE
CURSOR dept_cur IS
select department_name , NVL(employee_name , 'N/A') , NVL(employee.JOB , 'N/A') , NVL(to_char(hire_date, 'DD-MON-YYYY') , 'N/A')
FROM department FULL OUTER JOIN employee ON department.department_id = employee.department_id
order by department_name , employee_name;
v_department_name department.department_name%TYPE;
v_employee_name employee.employee_name%type;
v_employee_job employee.job%type;
v_hire_date VARCHAR(20);
BEGIN
OPEN dept_cur;
fetch dept_cur into v_department_name , v_employee_name , v_employee_job , v_hire_date ;
IF dept_cur%FOUND THEN
DBMS_OUTPUT.PUT_LINE('DEPARTMENT_NAME EMPLOYEE NAME , EMPLOYEE JOB , HIRE DATE');
DBMS_OUTPUT.PUT_LINE('-----------------------------------------------------');
WHILE dept_cur%FOUND LOOP
DBMS_OUTPUT.PUT_LINE(lpad(v_department_name, 11) || lpad(v_employee_name, 16) || lpad(v_employee_job, 22)|| lpad(v_hire_date,15));
fetch dept_cur into v_department_name , v_employee_name , v_employee_job , v_hire_date ;
END LOOP;
ELSE
DBMS_OUTPUT.PUT_LINE('No output.');
END IF;
CLOSE dept_cur;
END;
I want the output to be something like this , but my query returns the entire list .
The number of employees in ACCOUNTING is 2
EMPLOYEE_NAME JOB HIRE DATE
--------------------------------------------------------------
JONES PUBLIC ACCOUNTANT 02-APR-81
STEEL PUBLIC ACCOUNTANT 02-MAR-83
and the same for other departments as well. I understand its a minor change in the PL SQL block logic , but I am unable to figure it out.

DECLARE
CURSOR dept_cur IS
SELECT department_name,
COUNT(employee.employee_id) OVER (PARTITION BY department_name),
NVL(employee_name, 'N/A'),
NVL(employee.JOB, 'N/A'),
NVL(to_char(hire_date, 'DD-MON-YYYY'), 'N/A')
FROM department
FULL OUTER JOIN employee ON department.department_id = employee.department_id
ORDER BY department_name, employee_name;
v_department_name department.department_name%TYPE;
last_department_name department.department_name%TYPE;
v_department_count NUMBER;
v_employee_name employee.employee_name%TYPE;
v_employee_job employee.job%TYPE;
v_hire_date VARCHAR(20);
BEGIN
OPEN dept_cur;
FETCH dept_cur INTO v_department_name, v_department_count, v_employee_name, v_employee_job, v_hire_date;
last_department_name := NULL;
IF dept_cur%FOUND THEN
WHILE dept_cur%FOUND LOOP
IF last_department_name IS NULL OR last_department_name <> v_department_name THEN
DBMS_OUTPUT.PUT_LINE('The number of employees in ' || v_department_name || ' is ' || to_char(v_department_count));
DBMS_OUTPUT.PUT_LINE('EMPLOYEE NAME EMPLOYEE JOB HIRE DATE');
DBMS_OUTPUT.PUT_LINE('--------------------------------------------------------------');
last_department_name := v_department_name;
END IF;
DBMS_OUTPUT.PUT_LINE(rpad(v_employee_name, 24) || rpad(v_employee_job, 24)|| lpad(v_hire_date,14));
FETCH dept_cur INTO v_department_name, v_department_count, v_employee_name, v_employee_job, v_hire_date;
END LOOP;
ELSE
DBMS_OUTPUT.PUT_LINE('No output.');
END IF;
CLOSE dept_cur;
END;
It uses an analytic function COUNT() OVER () to count the number of employees in the result for each department, and inserts that on every row for that department.
Since the query is already ordered by department, it just checks for when that changes to another value, and then prints the headers.

Related

Cursor and procedure/function pl sql to display employees name

I want to create a procedure/function, when user input a year and a department name, i need to display the employees' name (first and last name). This is my code so far. Any idea? Thanks!
DECLARE
CURSOR emp_cur (my_date int,my_dept departments.department_name%type) IS
SELECT e.first_name,e.last_name
FROM employees e
join departments d
on e.department_id=d.department_id
where d.department_name=my_dept and my_date=extract(year from e.hire_date);
PROCEDURE emp_excesspaid IS
v_first employees.first_name%TYPE;
v_last employees.last_name%TYPE;
BEGIN
LOOP
FETCH emp_cur INTO v_first, v_last;
EXIT WHEN emp_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_first||' '||v_last);
END LOOP;
END;
end;
You can make it simple as follows:
CREATE OR REPLACE PROCEDURE PROC_NAME(
P_IN_DEPT_NAME IN VARCHAR2,
P_IN_YEAR IN NUMBER
) AS
BEGIN
For D in (SELECT e.first_name, e.last_name
FROM employees e
join departments d
on e.department_id = d.department_id
where d.department_name = P_IN_DEPT_NAME
and extract(year from e.hire_date) = P_IN_YEAR)
LOOP
DBMS_OUTPUT.PUT_LINE(D.FIRST_NAME || ' ' || D.LAST_NAME);
END LOOP;
END;
/

Oracle: How to wrap a Cursor inside A Function?

I am working on an assignment and it requires me to place a cursor inside a function.
Now my cursor works fine, but I am having a hard time placing it inside a a function, how can I do this?
The function is supposed to have the same argument as the cursor.
Code:
DECLARE
CURSOR c_emp_salary (v_job_title VARCHAR2)
IS
SELECT employee_id, first_name, salary, min_salary FROM employees e JOIN jobs j ON
e.job_id = j.job_id
AND j.job_title = v_job_title;
v_emp_id employees.employee_id%TYPE;
v_emp_first_name employees.first_name%TYPE;
v_emp_salary employees.salary%TYPE;
v_min_salary jobs.min_salary%TYPE;
BEGIN
OPEN c_emp_salary('Shipping Clerk'); --I WANT THIS VALUE TO BE FROM A FUNCTION.
LOOP
FETCH c_emp_salary INTO v_emp_id, v_emp_first_name, v_emp_salary, v_min_salary;
EXIT WHEN c_emp_salary%NOTFOUND;
dbms_output.put_line('Employee ID: ' || v_emp_id);
dbms_output.put_line('Employee First Name: ' || v_emp_first_name);
dbms_output.put_line('Employee Salary: ' || v_emp_salary);
dbms_output.put_line('Job Min Salary: ' || v_min_salary);
dbms_output.put('Result? ');
IF v_emp_salary = v_min_salary THEN dbms_output.put('Yes');
ELSE dbms_output.put('No');
END IF;
dbms_output.put_line(NULL);
dbms_output.put_line('++++++++++++++++++++++++');
END LOOP;
END;
The assignment description: Write a function that takes job_title and checks all employees in
that job, if an employee has salary equal to min_salary print
“yes” else print “no”. Use a cursor
"Use a cursor" is pointless because every DML or SELECT statement is a cursor.
Anyway, I assume you are looking for this:
CREATE OR REPLACE FUNCTION WRITE_SALARY(v_job_title IN VARCHAR2) AS
CURSOR c_emp_salary IS
SELECT employee_id, first_name, salary, min_salary
FROM employees e
JOIN jobs j ON e.job_id = j.job_id
WHERE j.job_title = v_job_title;
v_emp_id employees.employee_id%TYPE;
v_emp_first_name employees.first_name%TYPE;
v_emp_salary employees.salary%TYPE;
v_min_salary jobs.min_salary%TYPE;
BEGIN
OPEN c_emp_salary;
...
I assume you need something as:
(It needed substitute SCHEMA_NAME and FUNCTION_NAME)
(If return does not needed procedure can be used, and return statements should be removed)
(I don't run this example)
CREATE OR REPLACE function SCHEMA_NAME.FUNCTION_NAME( v_job_title VARCHAR2) return number is
CURSOR c_emp_salary
IS
SELECT employee_id, first_name, salary, min_salary FROM employees e JOIN jobs j ON
e.job_id = j.job_id
AND j.job_title = v_job_title;
v_emp_id employees.employee_id%TYPE;
v_emp_first_name employees.first_name%TYPE;
v_emp_salary employees.salary%TYPE;
v_min_salary jobs.min_salary%TYPE;
result number;
BEGIN
OPEN c_emp_salary('Shipping Clerk'); --I WANT THIS VALUE TO BE FROM A FUNCTION.
LOOP
FETCH c_emp_salary INTO v_emp_id, v_emp_first_name, v_emp_salary, v_min_salary;
EXIT WHEN c_emp_salary%NOTFOUND;
dbms_output.put_line('Employee ID: ' || v_emp_id);
dbms_output.put_line('Employee First Name: ' || v_emp_first_name);
dbms_output.put_line('Employee Salary: ' || v_emp_salary);
dbms_output.put_line('Job Min Salary: ' || v_min_salary);
dbms_output.put('Result? ');
IF v_emp_salary = v_min_salary THEN
dbms_output.put('Yes');
result := 1;
ELSE dbms_output.put('No');
result := 0;
END IF;
dbms_output.put_line(NULL);
dbms_output.put_line('++++++++++++++++++++++++');
return result;
END LOOP;
END;
The OP originally asked:
I am having a hard time placing [the cursor] inside a a function, how can I do this?
and
OPEN c_emp_salary('Shipping Clerk'); --I WANT THIS VALUE TO BE FROM A FUNCTION.
The code below solves the problem originally asked in the question; it is not intended to solve the assignment question that was edited in later as they are not one-and-the-same (that is left as an exercise to the OP).
Oracle Setup:
CREATE TABLE employees (
employee_id NUMBER,
first_name VARCHAR2(50),
salary NUMBER(10,2),
job_id NUMBER
);
CREATE TABLE jobs (
job_id NUMBER,
job_title VARCHAR2(50),
min_salary NUMBER(10,2)
);
INSERT INTO employees VALUES ( 1, 'Alice', 300, 1 );
INSERT INTO jobs VALUES ( 1, 'Shipping Clerk', 200 );
PL/SQL Block:
DECLARE
v_emp_id employees.employee_id%TYPE;
v_emp_first_name employees.first_name%TYPE;
v_emp_salary employees.salary%TYPE;
v_min_salary jobs.min_salary%TYPE;
c_emp_cursor SYS_REFCURSOR;
FUNCTION get_cursor(
v_job_title IN JOBS.JOB_TITLE%TYPE
) RETURN SYS_REFCURSOR
IS
c_emp_salary SYS_REFCURSOR;
BEGIN
OPEN c_emp_salary FOR
SELECT employee_id,
first_name,
salary,
min_salary
FROM employees e
JOIN jobs j
ON ( e.job_id = j.job_id
AND j.job_title = v_job_title );
RETURN c_emp_salary;
END;
BEGIN
c_emp_cursor := get_cursor('Shipping Clerk');
LOOP
FETCH c_emp_cursor INTO v_emp_id, v_emp_first_name, v_emp_salary, v_min_salary;
EXIT WHEN c_emp_cursor%NOTFOUND;
dbms_output.put_line('Employee ID: ' || v_emp_id);
dbms_output.put_line('Employee First Name: ' || v_emp_first_name);
dbms_output.put_line('Employee Salary: ' || v_emp_salary);
dbms_output.put_line('Job Min Salary: ' || v_min_salary);
dbms_output.put('Result? ');
IF v_emp_salary = v_min_salary THEN dbms_output.put('Yes');
ELSE dbms_output.put('No');
END IF;
dbms_output.put_line(NULL);
dbms_output.put_line('++++++++++++++++++++++++');
END LOOP;
END;
/
Output:
Employee ID: 1
Employee First Name: Alice
Employee Salary: 300
Job Min Salary: 200
Result? No
++++++++++++++++++++++++
db<>fiddle here

Issue when creating a stored procedure

I'm trying to create a stored procedure as follows:
CREATE OR REPLACE PROCEDURE storedprocedure(emp number) AS
BEGIN
DECLARE
-- create the cursor based on a query
cursor emp_cursor is
select e.employeeid, firstname, lastname, e.departmentid, e.title,
salary, d.departmentname, billrate
from employees e
full join departments d on e.departmentID = d.departmentID
full join employeeproject p on e.employeeID = p.employeeID where e.employeeID = emp;
BEGIN
open emp_cursor;
-- first display information about the employee
dbms_output.put_line('- -');
dbms_output.put_line('- -');
dbms_output.put_line('Employee#' || e.employeeid
|| ' Name:' || TRIM(e.firstname) || ' ' || TRIM(e.lastname)
|| ' Dept: ');
dbms_output.put_line('_________________________________________________________');
dbms_output.put_line('- -');
dbms_output.put_line('- - Title: ' || e.title
|| ' Salary: ' || to_char(e.salary,'$999,999,999.99'));
dbms_output.put_line('- - Billing Rate: ' || to_char(billrate,'$999,999.99'));
-- next call the stored procedure to show department information
END;
END;
/
But it compiles with errors. When I show errors it tells me e.employeeID, e.title, and billrate must all be declared, but they are the original query. What am I doing wrong here? Am I misunderstand what it means to have them declared?
These are all columns that exist within the tables being queried and running the query as SQL gets results.
You are opening the cursor, but you are not fetching it into anything - either a series of scalar variables or a record type - which would normally be done in a loop. Then when you are in the loop you refer to the variables/record, not the table used in the cursor query - that is out of scope outside the cursor declaration.
There's a slightly simpler implicit cursor loop that you might find a bit easier in this case:
CREATE OR REPLACE PROCEDURE storedprocedure(emp number) AS
BEGIN
FOR rec IN (
select e.employeeid, firstname, lastname, e.departmentid, e.title,
salary, d.departmentname, billrate
from employees e
full join departments d on e.departmentID = d.departmentID
full join employeeproject p on e.employeeID = p.employeeID where e.employeeID = emp
)
LOOP
-- first display information about the employee
dbms_output.put_line('- -');
dbms_output.put_line('- -');
dbms_output.put_line('Employee#' || rec.employeeid
|| ' Name:' || TRIM(rec.firstname) || ' ' || TRIM(rec.lastname)
|| ' Dept: ');
dbms_output.put_line('_________________________________________________________');
dbms_output.put_line('- -');
dbms_output.put_line('- - Title: ' || rec.title
|| ' Salary: ' || to_char(rec.salary,'$999,999,999.99'));
dbms_output.put_line('- - Billing Rate: ' || to_char(rec.billrate,'$999,999.99'));
-- next call the stored procedure to show department information
END LOOP;
END;
/
Notice that all the references to columns inside the loop are of the form rec.<field> - they are taken from the record for this time round the loop, not directly from the underlying tables.
You also had a nested block which isn't doing any harm but isn't useful, so I took that out.
If you particularly wanted to use the explicit open-fetch-close cursor handling it would look something like this:
CREATE OR REPLACE PROCEDURE storedprocedure(emp number) AS
-- create the cursor based on a query
cursor emp_cursor is
select e.employeeid, firstname, lastname, e.departmentid, e.title,
salary, d.departmentname, billrate
from employees e
full join departments d on e.departmentID = d.departmentID
full join employeeproject p on e.employeeID = p.employeeID where e.employeeID = emp;
-- record variable based on cursor definition
rec emp_cursor%ROWTYPE;
BEGIN
OPEN emp_cursor;
LOOP
-- fetch the next row result from the cursor into the record vairable
FETCH emp_cursor INTO rec;
-- break out of the loop if there are no more results to fetch
EXIT WHEN emp_cursor%NOTFOUND;
-- first display information about the employee
dbms_output.put_line('- -');
dbms_output.put_line('- -');
dbms_output.put_line('Employee#' || rec.employeeid
|| ' Name:' || TRIM(rec.firstname) || ' ' || TRIM(rec.lastname)
|| ' Dept: ');
dbms_output.put_line('_________________________________________________________');
dbms_output.put_line('- -');
dbms_output.put_line('- - Title: ' || rec.title
|| ' Salary: ' || to_char(rec.salary,'$999,999,999.99'));
dbms_output.put_line('- - Billing Rate: ' || to_char(rec.billrate,'$999,999.99'));
-- next call the stored procedure to show department information
END LOOP;
CLOSE emp_cursor;
END;
/

SumSalary 0 does not show

I'd like to know how can I get a message if my SUM salary is 0. Please check my code:
DECLARE
v_sum_sal NUMBER(10,2);
v_deptno NUMBER NOT NULL := 10;
BEGIN
SELECT SUM(salary) -- group function
INTO v_sum_sal FROM employees
WHERE department_id = v_deptno;
IF v_sum_sal = 0 THEN
DBMS_OUTPUT.PUT_LINE ('You have selected 0 employees ');
else
DBMS_OUTPUT.PUT_LINE ('The sum of salary is ' || v_sum_sal);
end if;
END;
If there are no matches, then the sum() returns NULL, not 0. Here are two approaches.
Change the query to:
SELECT COALESCE(SUM(salary), 0) -- group function
INTO v_sum_sal FROM employees
WHERE department_id = v_deptno;
Alternatively, change the if to:
IF v_sum_sal IS NULL THEN
Here what is happening in your code, if your table doesnt contain the dept id you provided. It will give a NULL output. So if you put a exception handling condition it will work like a charm.
DECLARE
v_sum_sal NUMBER(10,2);
v_deptno NUMBER NOT NULL := 10;
BEGIN
BEGIN
SELECT SUM(salary) -- group function
INTO v_sum_sal
FROM employees
WHERE department_id = v_deptno;
EXCEPTION
WHEN NO_DATA_FOUND THEN
v_sum_sal:=0;
END;
IF v_sum_sal = 0 THEN
DBMS_OUTPUT.PUT_LINE ('You have selected 0 employees ');
ELSE
DBMS_OUTPUT.PUT_LINE ('The SUM OF salary
IS
' || v_sum_sal);
END IF;
END;

Return every employees department name

Write function returning every employee's department name.
Write a block printing all the employees name and department name in 20 dept.
Function header:
Create or replace function empdnm (empno number) return varchar2 is
Try to write exception if there is no such empno.
I have tried the following code, but it shows me some errors, namely
SQL statment ignored and missing expression. What is the problem?
create or replace function empdnm (empno1 number) return varchar2 is
deptname varchar (30);
BEGIN
Select into deptname(select d.dname from dept d
join emp e on e.deptno=d.deptno
Where e.empno= empno1
)
exception
WHEN no_data_found THEN
dbms_output.put_line('no employee with no:'|| empno1);
return(deptname);
end;
BEGIN
FOR r IN (SELECT * FROM emp where dept_id = 20) loop
dbms_output.put_line( 'employee '|| r.emp_name || ' is in department '|| empdnm(empno1) );
END loop;
end;
Try like this:
create or replace function empdnm (empno1 number) return varchar2 is
deptname varchar (30);
BEGIN
SELECT d.dname
INTO deptname
FROM dept d
JOIN emp e on e.deptno = d.deptno
WHERE e.empno = empno1;
return(deptname);
exception
WHEN no_data_found THEN
dbms_output.put_line('no employee with no:'|| empno1);
end;
Use below one :
create or replace function empdnm (empno1 number) return varchar2 is
deptname varchar (30);
BEGIN
SELECT d.dname
INTO deptname
FROM dept d
JOIN emp e on e.deptno = d.deptno
WHERE e.empno = empno1;
return(deptname);
exception
WHEN no_data_found THEN
dbms_output.put_line('no employee with no:'|| empno1);
end;
BEGIN
FOR r IN (SELECT * FROM emp where dept_id = 20) loop
dbms_output.put_line( 'employee '|| r.emp_name || ' is in department '|| empdnm(r.empno1) );
END loop;
end;