Chapter 4 Practice (Volume 2) 1) Write the Procedure called Get_Job_Income that will as Input parameter accept Last Name (in Upper case) of the employee and as Output display firstly the message about his/her First Name, Job Id and Annual Income (incl. Commission). Then you will display just Job id and Annual Income as Output variables. You will use previously defined Function Get_Income. You need to take care also for situations where the Input is an invalid Last Name and if there is more than one person with that name. You should use For Cursor Loop and test your procedure by using BIND variables. Here are the outputs: VARIABLE VARIABLE EXECUTE PRINT job VARCHAR2(20) total NUMBER Get_Job_Income('ZLOTKEY',:job,:total) job total Employee ELENI ZLOTKEY who works as SA_MAN earns annually total of $151200 PL/SQL procedure successfully completed. JOB SA_MAN TOTAL 151200 VARIABLE VARIABLE EXECUTE PRINT job VARCHAR2(20) total NUMBER Get_Job_Income('GRANT',:job,:total) job total Employee KIMBERELY GRANT who works as SA_REP earns annually total of $96600 Employee DOUGLAS GRANT who works as SH_CLERK earns annually total of $31200 PL/SQL procedure successfully completed. JOB SH_CLERK TOTAL 31200 VARIABLE VARIABLE EXECUTE PRINT job VARCHAR2(20) total NUMBER Get_Job_Income('ADAMS',:job,:total) job total There is NO such employee ADAMS - Try your input again! PL/SQL procedure successfully completed. JOB TOTAL 2) Write the PACKAGE specification for the Package called Emp_Pack. It will contain Two Procedures and One Function defined earlier in the Chapter 2 and 3 examples plus the Procedure from Question 1. Your Package will store following Program Units (in the given order): Add_Dept, Get_Dept, Get_Job_Income and Get_Mgr. Compile without errors. 3) Write the PACKAGE BODY code for the Package called Emp_Pack. Compile without errors. 4) Remove Procedure Get_Job_Income created in Question 1) from your schema. Then test your Package in the similar way you tested this Procedure in Question 1 by providing ‘SMITH’ as your input to the package construct. Here is the Output. Employee William SMITH who works as SA_REP earns annually total of $102120 Employee Lindsey SMITH who works as SA_REP earns annually total of $124800 PL/SQL procedure successfully completed. JOB SA_MAN TOTAL 124800 5) Remove Function Get_Income created in Chapter 3 examples earlier. Then test your Package again by providing ‘SMITH’ as your input to the package construct. What happened? 6) Now, place your Function Get_Income (as the Private or Local one) to the back of the Package Body. What happened when you compile the Body? 7) Use FORWARD DECLARATION method to fix the Error received in Question 6). Then test your Package again by providing ‘SMITH’ as your input to the package construct. Was it successful? ANSWERS 1) CREATE OR REPLACE PROCEDURE Get_Job_Income ( p_lname IN EMPLOYEES.LAST_NAME%TYPE, p_job OUT EMPLOYEES.JOB_ID%TYPE, p_total OUT EMPLOYEES.SALARY%TYPE) IS CURSOR c1 IS SELECT employee_id, first_name, job_id FROM employees WHERE UPPER(last_name) = p_lname; emp_rec c1%ROWTYPE; BEGIN OPEN c1; FETCH c1 INTO emp_rec; IF c1%ROWCOUNT = 0 THEN DBMS_OUTPUT.PUT_LINE('There is NO such employee ' || p_lname || ' - Try your input again!'); p_job := NULL; p_total := NULL; END IF; CLOSE c1; FOR ind IN c1 LOOP p_total := get_income(ind.employee_id); p_job := ind.job_id; DBMS_OUTPUT.PUT_LINE('Employee ' || ind.first_name || ' ' || p_lname || ' who works as ' || p_job || ' earns annually total of $' || p_total); END LOOP; END get_job_income; / 2) CREATE OR REPLACE PACKAGE Emp_Pack IS PROCEDURE Add_Dept ( deptname departments.department_name %TYPE, deptloc departments.location_id%TYPE) ; PROCEDURE Get_Dept ( deptname OUT departments.department_name %TYPE, deptloc OUT departments.location_id%TYPE) ; PROCEDURE Get_Job_Income ( p_lname IN EMPLOYEES.LAST_NAME%TYPE, p_job OUT EMPLOYEES.JOB_ID%TYPE, p_total OUT EMPLOYEES.SALARY%TYPE) ; FUNCTION Get_Mgr ( dept# IN DEPARTMENTS.DEPARTMENT_ID%TYPE) RETURN NUMBER ; END emp_pack; / 3) CREATE OR REPLACE PACKAGE BODY Emp_Pack IS PROCEDURE Add_Dept ( deptname departments.department_name %TYPE, deptloc departments.location_id%TYPE) IS error_code NUMBER; error_msg VARCHAR2(255); BEGIN INSERT INTO mydept VALUES (DEPARTMENTS_SEQ.NEXTVAL, DEPTNAME, NULL, DEPTLOC); EXCEPTION WHEN OTHERS THEN Rollback; error_code := SQLCODE; error_msg := SQLERRM; DBMS_OUTPUT.PUT_LINE('Error # is : ' || error_code); DBMS_OUTPUT.PUT_LINE('Error Message is : ' || error_msg); END add_dept; PROCEDURE Get_Dept ( deptname OUT departments.department_name %TYPE, deptloc OUT departments.location_id%TYPE) IS BEGIN SELECT department_name, location_id INTO deptname, deptloc FROM mydept WHERE department_id = (SELECT MAX(department_id) END FROM mydept); get_dept; PROCEDURE Get_Job_Income ( p_lname IN EMPLOYEES.LAST_NAME%TYPE, p_job OUT EMPLOYEES.JOB_ID%TYPE, p_total OUT EMPLOYEES.SALARY%TYPE) IS CURSOR c1 IS SELECT employee_id, first_name, job_id FROM employees WHERE UPPER(last_name) = p_lname; emp_rec c1%ROWTYPE; BEGIN OPEN c1; FETCH c1 INTO emp_rec; IF c1%ROWCOUNT = 0 THEN DBMS_OUTPUT.PUT_LINE('There is NO such employee ' || p_lname || ' - Try your input again!'); p_job := NULL; p_total := NULL; END IF; CLOSE c1; FOR ind IN c1 LOOP p_total := get_income(ind.employee_id); p_job := ind.job_id; DBMS_OUTPUT.PUT_LINE('Employee ' || ind.first_name || ' ' || p_lname || ' who works as ' || p_job || ' earns annually total of $' || p_total); END LOOP; END get_job_income; FUNCTION Get_Mgr ( dept# IN DEPARTMENTS.DEPARTMENT_ID%TYPE) RETURN NUMBER IS mgr# DEPARTMENTS.MANAGER_ID%TYPE; e_mgr EXCEPTION; BEGIN SELECT manager_id INTO mgr# FROM departments WHERE department_id = dept#; IF mgr# IS NULL THEN RAISE e_mgr; END IF; RETURN mgr#; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN -1; WHEN e_mgr THEN RETURN 0; END get_mgr; END emp_pack; / 4) DROP PROCEDURE Get_Job_Income VARIABLE job VARCHAR2(20) VARIABLE total NUMBER EXECUTE Emp_pack.Get_Job_Income('SMITH',:job,:total) PRINT job total Even without stand alone Procedure in your schema, you could still run it as a part of the Package. 5) DROP FUNCTION Get_Income VARIABLE job VARCHAR2(20) VARIABLE total NUMBER EXECUTE Emp_pack.Get_Job_Income('SMITH',:job,:total) PRINT job total BEGIN Emp_pack.Get_Job_Income('SMITH',:job,:total); END; * ERROR at line 1: ORA-04063: package body "DBS501_093A40.EMP_PACK" has errors ORA-06508: PL/SQL: could not find program unit being called: "DBS501_093A40.EMP_PACK" Here, without stand alone Function in your schema, you can NOT run your Procedure as a part of the Package, because it calls for the Function that is NOT part of the same Package. 6) … the code above in the Body END get_mgr; FUNCTION Get_Income ( emp# IN EMPLOYEES.EMPLOYEE_ID%TYPE) RETURN NUMBER IS tot_income EMPLOYEES.SALARY%TYPE; e_tot EXCEPTION; BEGIN SELECT 12*(NVL(salary,0)*(1+NVL(commission_pct,0))) INTO tot_income FROM employees WHERE employee_id = emp#; IF tot_income IS NULL THEN RAISE e_tot; END IF; RETURN tot_income; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN -1; WHEN e_tot THEN RETURN 0; END get_income; END emp_pack; Compiled with errors Show Errors Errors for PACKAGE BODY EMP_PACK: LINE/COL ERROR 50/8 PL/SQL: Statement ignored 50/19 PLS-00313: 'GET_INCOME' not declared in this scope Here, the Procedure Get_Job_Income is defined BEFORE the Function that calls for -Get_Income and can NOT be complied without errors.. You may place your Function up front (as the first Program Unit) OR use the Forward Declaration Method 7) Forward Declaration of the Function Get_Income: Place just the DECLARATION of the Function (without the code) as the FIRST construct in the Package Body. CREATE OR REPLACE PACKAGE BODY Emp_Pack IS FUNCTION get_income ( emp# IN EMPLOYEES.EMPLOYEE_ID%TYPE) RETURN NUMBER; PROCEDURE Add_Dept ( … the code below in the Body VARIABLE job VARCHAR2(20) VARIABLE total NUMBER EXECUTE Emp_pack.Get_Job_Income('SMITH',:job,:total) PRINT job total It is SUCCESS as shown below Employee William SMITH who works as SA_REP earns annually total of $102120 Employee Lindsey SMITH who works as SA_REP earns annually total of $124800 PL/SQL procedure su