How to Loop department names - sql

Oracle SQL Developer
I want to Loop department id and department names starting from dep_id = 10 and till 50th department. Departments are increasing by 10, so, there are 5 departments from 10 to 50.
Here's my code
DECLARE
dep_name VARCHAR(15);
dep_id NUMBER;
BEGIN
SELECT department_name, department_id INTO dep_name, dep_id FROM Departments
WHERE DEPARTMENT_ID = 10;
LOOP
IF dep_id < 51 THEN
DBMS_OUTPUT.PUT_LINE('Deparment id is ' || dep_id);
dep_id := dep_id + 10;
DBMS_OUTPUT.PUT_LINE('Deparment name is ' || dep_name);
ELSE
EXIT WHEN dep_id > 51;
END IF;
END LOOP;
END;
And here is the output,
Deparment id is 10
Deparment name is Administration
Deparment id is 20
Deparment name is Administration
Deparment id is 30
Deparment name is Administration
Deparment id is 40
Deparment name is Administration
Deparment id is 50
Deparment name is Administration
But as you can see the Administration row is repeating itself.
The output should be like this
Deparment id is 10
Deparment name is Administration
Deparment id is 20
Deparment name is Marketing
Deparment id is 30
Deparment name is Purchasing
Deparment id is 40
Deparment name is Human Resources
Deparment id is 50
Deparment name is Shipping
What am I supposed to do about this?
Thanks!

How about a simple cursor FOR loop?
SQL> set serveroutput on;
SQL> begin
2 for cur_d in (select department_id, department_name
3 from departments
4 where department_id between 10 and 50
5 )
6 loop
7 dbms_output.put_line('Department ID is ' || cur_d.department_id);
8 dbms_output.put_line('Department name is ' || cur_d.department_name);
9 end loop;
10 end;
11 /
Department ID is 10
Department name is Administration
Department ID is 20
Department name is Marketing
Department ID is 30
Department name is Purchasing
Department ID is 40
Department name is Human Resources
Department ID is 50
Department name is Shipping
PL/SQL procedure successfully completed.
SQL>

If the loop is mandatory
DECLARE
dep_name VARCHAR(15);
dep_id NUMBER;
BEGIN
dep_id:=10;
LOOP
IF dep_id < 51 THEN
DBMS_OUTPUT.PUT_LINE('Deparment id is ' || dep_id);
SELECT department_name INTO dep_name FROM Departments
WHERE DEPARTMENT_ID = dep_id;
DBMS_OUTPUT.PUT_LINE('Deparment name is ' || dep_name);
dep_id := dep_id + 10;
ELSE
EXIT WHEN dep_id > 51;
END IF;
END LOOP;
END;

You could avoid the loop .. using a simply (correct) query
SELECT department_name, department_id
FROM Departments
WHERE DEPARTMENT_ID between 10 AND 50;
(your code is wrong, you are only selecting the id = 10 not the values from 10 to 50 )

It seems the major requirement is to print department id and name. That does indeed require a plsql loop; and a cursor to hold the data after the select. Further, you should NOT relay on a specific interval between department id. Instead let the query and the cursor holding result set control the entire process.
declare
cursor c_department_list is
select department_name, department_id
from departments
where department_id <= 50 ;
l_dept_name departments.department_name%type;
l_dept_id departments.department_id%type;
begin
open c_department_list;
loop
fetch c_department_list
into l_dept_name, l_dept_id;
exit when c_department_list%notfound.
dbms_output.put_line(' Deparment id is ' || l_dep_id ||
'. Deparment name is ' || l_dep_name);
end loop;
close c_department_list;
end;
References: Cursors, Loop
The above uses a technique referred to as explicit cursor. There is another preferred (perhaps easier) technique referred to as implicit cursor. I will leave that to your research.

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>

WITH clause with FUNCTION AND PROCEDURE, where is the mistake?

I have table 'Studies' (with columns: student_id, name, surname, course_name, mark) and I have a task to write a PL/SQL program, where will be WITH word + FUNCTION word + PROCEDURE word.I decided to make such a program: the function will calculate the average mark for some course (input parameter) and the procedure will display information about students whose mark in this course is higher than the average. I managed to create a function that returns the average score,
create or replace FUNCTION average_mark(co_name IN VARCHAR2) RETURN REAL IS
iter NUMBER := 0;
aver NUMBER := 0;
CURSOR c1
IS
SELECT mark
FROM studies
WHERE course_name = co_name;
BEGIN
FOR student IN c1
LOOP
iter := iter + 1;
aver := aver + student.mark;
END LOOP;
RETURN ROUND((aver/iter),2);
END above_average_mark;
and procedure which displays information about a student whose mark in the course is more than a certain one, how now to connect the procedure and the function and the WITH word?
CREATE OR REPLACE PROCEDURE above(co_name IN VARCHAR2) IS
CURSOR c2
IS
SELECT *
FROM studies
WHERE course_name = co_name;
BEGIN
FOR student IN c2
LOOP
IF (student.mark > 4) THEN
DBMS_OUTPUT.PUT_LINE('name: ' || student.student_name || ', mark: ' || student.mark);
END IF;
END LOOP;
END;
i need something like this:
WITH
PROCEDURE
FUNCTION
I don't have your tables to illustrate it, so I'll use Scott's sample schema to calculate average salaries for departments.
As you said that you need something like WITH PROCEDURE FUNCTION, the only thing you have to do is to follow syntax.
Therefore, here you are: with factoring clause in this example contains a procedure which displays department name and average salary; function calls the procedure (and passes department number and average salary it calculated). Also, as any other function it actually returns a value.
SQL> set serveroutput on;
SQL> with
2 procedure p_deptno (par_deptno in dept.deptno%type,
3 par_avgsal in number)
4 is
5 l_dname dept.dname%type;
6 begin
7 select dname into l_dname
8 from dept
9 where deptno = par_deptno;
10 dbms_output.put_line('Average salary for department ' || l_dname ||
11 ' = ' || par_avgsal);
12 end p_deptno;
13
14 function f_avgsal (par_deptno in dept.deptno%type)
15 return number
16 is
17 l_avgsal number;
18 begin
19 select round(avg(e.sal)) into l_avgsal
20 from emp e
21 where e.deptno = par_deptno;
22
23 p_deptno (par_deptno, l_avgsal);
24
25 return l_avgsal;
26 end f_avgsal;
27 select f_avgsal (a.deptno) avg_sal
28 from dept a;
29 /
Result:
AVG_SAL
----------
2917
2175
1567
Average salary for department ACCOUNTING = 2917
Average salary for department RESEARCH = 2175
Average salary for department SALES = 1567
Average salary for department OPERATIONS =
SQL>
Now, adjust it to your tables & data.

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;

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;

Procedure cannot be executed because fetch returns more than requested number of rows

CREATE OR REPLACE
PROCEDURE P_raise
AS
v_salary NUMBER;
v_first_name VARCHAR2(20);
v_min_salary NUMBER;
BEGIN
SELECT first_name,
salary
INTO v_first_name,
v_salary
FROM employees
WHERE salary =
(SELECT MIN(salary) FROM employees
);
dbms_output.put_line(v_first_name||' has a minimum salary of '||v_salary);
SELECT MIN(salary) INTO v_min_salary FROM employees;
IF v_salary = v_min_salary THEN
UPDATE employees SET salary = salary*1.15 WHERE salary=v_min_salary;
dbms_output.Put_line(v_first_name||' has an increase of 15% in his salary ');
END IF;
END;
/
This messy code is used to find the min(salary) and then give it a raise.
There are two minimum salary records with the same value. How do I rework my code to by pass the error?
Thanks
You can't use SELECT INTO if the statement doesn't return exactly one row. Here you could use a loop, for instance with an implicit cursor:
CREATE OR REPLACE PROCEDURE P_raise AS
BEGIN
FOR cc IN (SELECT first_name, salary, ROWID
INTO v_first_name, v_salary
FROM employees
WHERE salary = (SELECT MIN(salary) FROM employees)
FOR UPDATE NOWAIT) LOOP
dbms_output.put_line(cc.first_name || ' has a minimum salary of '
|| cc.salary);
UPDATE employees SET salary = salary * 1.15 WHERE ROWID = cc.rowid;
dbms_output.Put_line(cc.first_name
|| ' has an increase of 15% in his salary');
END LOOP;
END;
Or use a straight update (which would be more efficient in general, although you won't have the details of which row got updated):
CREATE OR REPLACE PROCEDURE P_raise AS
BEGIN
UPDATE employees
SET salary = salary * 1.15
WHERE salary = (SELECT MIN(salary) FROM employees);
dbms_output.put_line(SQL%ROWCOUNT
|| ' employees had their salary increased by 15%');
END;