PL/SQL - If statement to check salary - sql

I'm attempting to develop a program that takes a number from the user, sorts employees by their highest salary, and displays a number of rows equivalent to the input number showing the top earners. The program should also verify if the last employee received the same salary as the next employee. If this is the case, the next employee should also be displayed.
I have attempted to modify the code, but none of my changes seem to be effective. Do you have any suggestions for how to make the code operate in accordance with the description provided? The functional code is included below.
Thank you.
Code:
CREATE OR REPLACE PROCEDURE p5_q5 (num_employee NUMBER) AS
BEGIN
FOR emp IN (SELECT ENAME, SAL
FROM EMP
ORDER BY SAL DESC
FETCH FIRST num_employee ROWS ONLY) -- Will be returned X rows according to input of the user (num_employee)
LOOP
DBMS_OUTPUT.PUT_LINE('Employee name: ' || emp.ENAME || ' - Salary: ' || emp.SAL);
END LOOP;
END;
/
EXEC p5_q5(3);
*Output:
Employee name: KING - Salary: 5512.5
Employee name: JONES - Salary: 3570
Employee name: SCOTT - Salary: 3450
*Ideal Output:
Employee name: KING - Salary: 5512.5
Employee name: JONES - Salary: 3570
Employee name: SCOTT - Salary: 3450
Employee name: FORD - Salary: 3450
I have tried several strategies, but have been unsuccessful in developing a method to determine if the subsequent employee earns the same salary as the previous employee, and to display that employee if they do earn the same amount.

Use FETCH FIRST n ROWS WITH TIES:
CREATE OR REPLACE PROCEDURE p5_q5 (num_employee NUMBER) AS
BEGIN
FOR emp IN (SELECT ENAME, SAL
FROM EMP
ORDER BY SAL DESC
FETCH FIRST num_employee ROWS WITH TIES)
LOOP
DBMS_OUTPUT.PUT_LINE('Employee name: ' || emp.ENAME || ' - Salary: ' || emp.SAL);
END LOOP;
END;
/
Which, for the sample data:
CREATE TABLE emp (ename, sal) AS
SELECT 'Alice', 100 FROM DUAL UNION ALL
SELECT 'Betty', 100 FROM DUAL UNION ALL
SELECT 'Carol', 90 FROM DUAL UNION ALL
SELECT 'Debra', 90 FROM DUAL UNION ALL
SELECT 'Emily', 90 FROM DUAL;
Then:
BEGIN
DBMS_OUTPUT.ENABLE;
p5_q5(1);
END;
/
and
BEGIN
DBMS_OUTPUT.ENABLE;
p5_q5(2);
END;
/
Both output:
Employee name: Alice - Salary: 100
Employee name: Betty - Salary: 100
and:
BEGIN
DBMS_OUTPUT.ENABLE;
p5_q5(3);
END;
/
Outputs:
Employee name: Alice - Salary: 100
Employee name: Betty - Salary: 100
Employee name: Carol - Salary: 90
Employee name: Debra - Salary: 90
Employee name: Emily - Salary: 90
fiddle

CREATE OR REPLACE PROCEDURE p5_q5 (num_employee NUMBER) AS
curr_salary NUMBER;
BEGIN
curr_salary := 0;
FOR emp IN (SELECT ENAME, SAL
FROM EMP
ORDER BY SAL DESC)
LOOP
IF emp.SAL = curr_salary THEN
DBMS_OUTPUT.PUT_LINE('Employee name: ' || emp.ENAME || ' - Salary: ' || emp.SAL);
num_employee := num_employee - 1;
ELSE
curr_salary := emp.SAL;
DBMS_OUTPUT.PUT_LINE('Employee name: ' || emp.ENAME || ' - Salary: ' || emp.SAL);
num_employee := num_employee - 1;
END IF;
IF num_employee = 0 THEN
EXIT;
END IF;
END LOOP;
END;
/
EXEC p5_q5(3);

Related

pl/sql ,oracle add heading in cursor

how I can add heading for table shown at top , in my code like this pic:
enter image description here
declare
E_Name employ.name%type;
E_Salary employ.salary%type;
CURSOR c_employees is
SELECT name , salary from employ order by salary desc;
BEGIN
OPEN c_employees;
LOOP
FETCH c_employees into E_Name,E_Salary ;
EXIT WHEN c_employees%notfound;
dbms_output.put_line( rpad(E_Name, 20, '.') || ' ' || rpad('$', (E_Salary/100), '$')||' '||E_Salary);
END LOOP;
CLOSE c_employees;
END;
/
Include two more DBMS_OUTPUT.PUT_LINEs which will display that header (lines #2 and 3).
For example (using my tables as I don't have yours):
SQL> begin
2 dbms_output.put_line('Emloyee name Salary');
3 dbms_output.put_line('------------ ------');
4
5 for cur_r in (select ename, sal from emp where deptno = 10) loop
6 dbms_output.put_line(rpad(cur_r.ename, 12, ' ') ||' '||
7 to_char(cur_r.sal, '99990'));
8 end loop;
9 end;
10 /
Emloyee name Salary
------------ ------
CLARK 2450
KING 5000
MILLER 1300
PL/SQL procedure successfully completed.
SQL>

PL/SQL CREATE PROCEDURE - Salary increase based on tenure

I have worked on this for a while but the code did not work and I could not figure out the correct solution. Did I miss something from the code? Thank you.
-- Question – The company wants to calculate the employees’ annual salary: --The first year of employment, the amount of salary is the base salary which is $10,000. --Every year after that, the salary increases by 5%. --Write a stored procedure named calculate_salary which gets an employee ID and --for that employee calculates the salary based on the number of years the employee has --been working in the company. (Use a loop construct to calculate the salary). --The procedure calculates and prints the salary. --Sample output: --First Name: first_name --Last Name: last_name --Salary: $9999,99 --If the employee does not exists, the procedure displays a proper message.
CREATE OR REPLACE PROCEDURE calculate_salary(EMPLOYEE_ID EMPLOYEES.EMPLOYEE_ID%TYPE) AS
increase FLOAT := 1.05;
base_salary NUMBER := 10000;
TENURE NUMBER;
SALARY NUMBER;
EMP_ID EMPLOYEES.EMPLOYEE_ID%TYPE;
FIRST_NAME EMPLOYEES.FIRST_NAME%TYPE;
LAST_NAME EMPLOYEES.FIRST_NAME%TYPE;
BEGIN
SELECT EMPLOYEE_ID, ROUND((SYSDATE - HIRE_DATE)/365,0), FIRST_NAME, LAST_NAME INTO EMP_ID,TENURE, FIRST_NAME, LAST_NAME
FROM EMPLOYEES
WHERE EMPLOYEE_ID = EMP_ID;
FOR i IN 0..TENURE LOOP
SALARY := base_salary * i;
END LOOP;
DBMS_OUTPUT.PUT_LINE ('First Name: '||FIRST_NAME);
DBMS_OUTPUT.PUT_LINE ('Last Name: '||LAST_NAME);
DBMS_OUTPUT.PUT_LINE ('Salary: '||TO_CHAR(SALARY,'$99,999.99'));
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE ('No Data Found!');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE ('Error!');
END;
/
BEGIN
calculate_salary(1);
END;
/
The calculation in the FOR loop is wrong. In the first loop iteration you are setting SALARY to zero. In the second iteration, you are setting SALARY equal to base_salary. In the third iteration you are setting SALARY to double base_salary, etc. Also, in PL/SQL, FOR loop limits are inclusive. Hence your loop should start at 1 (one) and not 0 (zero).
The below code calculates the salary assuming that the increase is based on the current salary and not the base salary. Changes to your code are indicated by comments at the end of the changed line.
CREATE OR REPLACE PROCEDURE calculate_salary(EMPLOYEE_ID EMPLOYEES.EMPLOYEE_ID%TYPE) AS
increase FLOAT := 1.05;
base_salary NUMBER := 10000;
TENURE NUMBER;
SALARY NUMBER;
EMP_ID EMPLOYEES.EMPLOYEE_ID%TYPE;
FIRST_NAME EMPLOYEES.FIRST_NAME%TYPE;
LAST_NAME EMPLOYEES.FIRST_NAME%TYPE;
BEGIN
SELECT EMPLOYEE_ID, ROUND((SYSDATE - HIRE_DATE)/365,0), FIRST_NAME, LAST_NAME INTO EMP_ID,TENURE, FIRST_NAME, LAST_NAME
FROM EMPLOYEES
WHERE EMPLOYEE_ID = EMP_ID;
SALARY := base_salary; -- Added this line.
FOR i IN 1..TENURE LOOP -- Changed this line.
SALARY := SALARY * increase; -- Changed this line.
END LOOP;
DBMS_OUTPUT.PUT_LINE ('First Name: '||FIRST_NAME);
DBMS_OUTPUT.PUT_LINE ('Last Name: '||LAST_NAME);
DBMS_OUTPUT.PUT_LINE ('Salary: '||TO_CHAR(SALARY,'$99,999.99'));
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE ('No Data Found!');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE ('Error!');
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

Every column on different line with one record SQL reports

I am trying to make a report for a particular table emp something like this.
************************************************************
EMPLOYEE NUMBER : 1010
EMPLOYEE NAME : SARAH
JOB : DESIGNER
SALARY : 10000$
*************************************************************
EMPLOYEE NUMBER : 1011
EMPLOYEE NAME : HANNAH
JOB : DECORATOR
SALARY : 20000$
*************************************************************
But I don't know how to get every column name on different line.
sql > break column on empname skip page
The above code does something like this.
EMPNO ENAME JOB SAL
---------- ---------- --------- ----------
7839 KING PRESIDENT 5000
EMPNO ENAME JOB SAL
---------- ---------- --------- ----------
7840 QUEEN PRESIDENT 4000
Please suggest what would be the code for it ? TIA.
If you just want the data in this format in some presentation layer (e.g. a text editor), you can simply select the columns concatenated together, separated by a newline:
DECLARE
CURSOR cur IS
SELECT empno, ename, job, sal FROM yourTable;
v_empno yourTable.empno%INT;
v_ename yourTable.ename%VARCHAR2(100);
v_job yourTable.job%VARCHAR2(100);
v_sal yourTable.sal%NUMBER(10,4);
BEGIN
OPEN cur;
LOOP
FETCH cur INTO v_empno, v_ename, v_job, v_sal
EXIT WHEN cur%NOTFOUND;
dbms_output.put_line('EMPLOYEE NUMBER : ' || v_empno || CHR(13) || 'EMPLOYEE NAME : '
|| v_ename || CHR(13) || 'JOB : ' || v_job || CHR(13) || 'SALARY : ' ||
v_sal || '$' || CHR(13));
END LOOP;
CLOSE cur;
END;

PL/SQL - Exact fetch returns more than requested number of rows

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;