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;
Related
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;
/
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
So here is the code as given to me in my oracle class:
CREATE OR REPLACE PROCEDURE add_job_hist
(p_emp_id IN employees.employee_id%TYPE,
p_new_jobid IN jobs.job_id%TYPE) IS
BEGIN
INSERT INTO job_history
SELECT employee_id, hire_date, SYSDATE, job_id, department_id
FROM employees
WHERE employee_id = p_emp_id;
UPDATE employees
SET hire_date = SYSDATE,
job_id = p_new_jobid,
salary = (SELECT min_salary + 500
FROM jobs
WHERE job_id = p_new_jobid)
WHERE employee_id = p_emp_id;
DBMS_OUTPUT.PUT_LINE('Added employee ' || p_emp_id || '
details to the JOB_HISTORY table');
DBMS_OUTPUT.PUT_LINE('Updated current job of employee ' ||
p_emp_id|| 'to ' || p_new_jobid);
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR (-20001, 'Employee does not exist!');
END add_job_hist;
/
The problem is that this is not a "SELECT INTO" statement, so the WHEN NO_DATA_FOUND exception isn't processed. I can raise my own user-defined exception, but it doesn't seem to let me do this in a procedure. How can I fix this so that it handles the exception? Because right now, if i give a p_emp_id that does not exist, it handles it anyway and the instructions do not want any additional procedure variables.
Thanks!
Check using SQL%ROWCOUNT after the insert statement, if it returns 0, then raise no_data_found explicitly.
CREATE OR REPLACE PROCEDURE add_job_hist(p_emp_id IN employees.employee_id%TYPE,
p_new_jobid IN jobs.job_id%TYPE) IS
BEGIN
INSERT INTO job_history
SELECT employee_id,
hire_date,
SYSDATE,
job_id,
department_id
FROM employees
WHERE employee_id = p_emp_id;
IF SQL%ROWCOUNT = 0 THEN
RAISE no_data_found;
END IF;
UPDATE employees
SET hire_date = SYSDATE,
job_id = p_new_jobid,
salary =
(SELECT min_salary + 500 FROM jobs WHERE job_id = p_new_jobid)
WHERE employee_id = p_emp_id;
dbms_output.put_line('Added employee ' || p_emp_id || '
details to the JOB_HISTORY table');
dbms_output.put_line('Updated current job of employee ' || p_emp_id || 'to ' || p_new_jobid);
EXCEPTION
WHEN no_data_found THEN
raise_application_error(-20001, 'Employee does not exist!');
END add_job_hist;
/
I am trying to create function that will return message with maximum salary for each job inside department and order by Maximum salary.
Message need to be:
Department: Department name,
Job/Position: Name of the job, Maximum salary: salary amount,
create or replace PACKAGE BODY Salary AS
FUNCTION max_sal(DEPTNO_F NUMBER)
RETURN VARCHAR2 IS
dept_name VARCHAR2(25);
job_possition VARCHAR(25);
maximum_salary NUMBER;
message VARCHAR2(255);
BEGIN
SELECT DNAME, JOB, MAX(SAL) AS "SAL"
INTO job_possition, maximum_salary
FROM EMP
WHERE DEPTNO = DEPTNO_F
GROUP BY JOB, DNAME
ORDER BY SAL DESC;
message := 'Department name: '||dept_name|| 'Job positin: ' ||job_possitin||, 'Maximum Salary: ' ||maximum_salary;
return message;
END max_sal;
END Salary;
The problem is your query is returning one than one row and it can't select into your 2 variables. Have you tried running the SELECT SQL statement outside of the package and checked the rows you are getting back?
For any given DEPTNO_F value you should not have more than one permutation of DNAME and JOB
To process several rows in query result you have to use loops:
create or replace PACKAGE BODY Salary AS
FUNCTION max_sal(DEPTNO_F NUMBER)
RETURN VARCHAR2 IS
dept_name VARCHAR2(25);
job_possition VARCHAR(25);
maximum_salary NUMBER;
message VARCHAR2(255);
BEGIN
for i in (SELECT DNAME, JOB, MAX(SAL) SAL
FROM EMP
WHERE DEPTNO = DEPTNO_F
GROUP BY JOB, DNAME
ORDER BY SAL DESC) loop
message := message || 'Department name: ' || i.DNAME || ', Job position: ' ||
i.JOB || ', Maximum Salary: ' || i.SAL || chr(10);
end loop;
return message;
END max_sal;
END Salary;
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.