PLSQL block to increase salary of departments - sql

I have to Increase the Salary of employees working in deptno 10 by 15%, Deptno 20 by 15% and others by 5% and display the corresponding the employees working in that Dept. I am able to increase the salary of departmnet 10 and 20 but I am not able to increase the salary of other departments by 5%. I have tried with for loop too.This is a practice question.
My question is this:(Display the all records from the Dept table. Increase the Salary of employees working in deptno 10 by 15%, Deptno 20 by 15% and others by 5% Also display the corresponding the employees working in that Dept. Use a parameter Cursor and Cursor with Update clause.)
MY code:
declare
cursor sal_increase(v_dno number) is select empno,sal,ename,deptno from emp where deptno=v_dno ;
emp_record sal_increase%rowtype;
begin
OPEN sal_increase(10);
LOOP
FETCH sal_increase INTO emp_record;
EXIT WHEN sal_increase%NOTFOUND;
update emp set sal=sal+sal*0.15 where empno=emp_record.empno;
DBMS_OUTPUT.PUT_LINE(emp_record.ename||' '||emp_record.deptno);
END LOOP;
CLOSE sal_increase;
OPEN sal_increase(20);
LOOP
FETCH sal_increase INTO emp_record;
EXIT WHEN sal_increase%NOTFOUND;
update emp set sal=sal+sal*0.15 where empno=emp_record.empno;
DBMS_OUTPUT.PUT_LINE(emp_record.ename||' '||emp_record.deptno);
END LOOP;
CLOSE sal_increase;
end;

You don't need any procedural code at all to do this. That would just unnecessarily and severely slow it down, especially the use of cursors.
Instead just use a simple UPDATE with a CASE expression returning the new salary depending on the department.
UPDATE emp
SET sal = CASE
WHEN deptno IN (10, 20) THEN
sal * 0.15
ELSE
sal * 0.05
END;
For the output just use a SELECT.
SELECT ename || ' ' || deptno
FROM emp;

Don't forget the solution from #sticky bit for future.
Going back to your requirement , I believe you stick to do it with one anonymous block and with parameterized cursor.(correct me if I am wrong)
if yes, I would change the following and it should do the work for you.
Change your cursor to below, For others you pass null as parameter and in the where clause we can handle the same. (when value pass as 10 or 20 it will do a comparison and when null it will update for all other than 10 and 20)
CURSOR sal_increase(v_dno NUMBER) IS
SELECT empno
,sal
,ename
,deptno
FROM emp
WHERE ( v_dno IS NOT NULL AND deptno = v_dno
OR v_dno IS NULL AND deptno NOT IN (10,20)
);
Then call again the cusrosr as you did for 10 and 20 once more like below,
OPEN sal_increase(v_dno => NULL);
LOOP
FETCH sal_increase
INTO emp_record;
EXIT WHEN sal_increase%NOTFOUND;
UPDATE emp SET sal = sal * 0.05 WHERE empno = emp_record.empno;
dbms_output.put_line(emp_record.ename || ' ' || emp_record.deptno);
END LOOP;
CLOSE sal_increase;

Related

How can I insert rows from one table to another in PL SQL using the cursor for loop?

I don't know how much of this is right
DECLARE
CURSOR cur_depts IS
SELECT *
FROM dept;
BEGIN
FOR i IN cur_depts
LOOP
INSERT INTO dept_backup
VALUES(i);
END LOOP;
CLOSE cur_depts;
END;
And this is the error I got
Error report -
ORA-06550: line 8, column 20:
PL/SQL: ORA-00947: not enough values
ORA-06550: line 8, column 8:
PL/SQL: SQL Statement ignored
06550. 00000 - "line %s, column %s:\n%s"
*Cause: Usually a PL/SQL compilation error.
*Action:
I'm using the tables from Scott schema here.
You do not need a cursor (and should not use one as individual inserts in a loop will be slower and generate a lot more logging). You can simply use a single INSERT ... SELECT ... statement:
INSERT INTO dept_backup
SELECT * FROM dept;
As you were told, you don't really need PL/SQL nor cursors nor loops, but - if you're learning about them, then here's how.
SQL> create table dept_backup as select * From dept where 1 = 2;
Table created.
SQL> declare
2 cursor cur_depts is
3 select deptno, dname, loc
4 from dept;
5 begin
6 for i in cur_depts loop
7 insert into dept_backup (deptno, dname, loc)
8 values (i.deptno, i.dname, i.loc);
9 end loop;
10 end;
11 /
PL/SQL procedure successfully completed.
SQL> select * from dept_backup;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
Even shorter option is not to explicitly declare a cursor - use cursor FOR loop:
SQL> truncate table dept_backup;
Table truncated.
SQL> begin
2 for i in (select deptno, dname, loc from dept) loop
3 insert into dept_backup (deptno, dname, loc)
4 values (i.deptno, i.dname, i.loc);
5 end loop;
6 end;
7 /
PL/SQL procedure successfully completed.
SQL> select * from dept_backup;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
Note that you don't have to close a cursor if you use such a syntax; you would have to declare a cursor variable, open the cursor, fetch from it, exit the loop and close the cursor if you used this syntax:
SQL> truncate table dept_backup;
Table truncated.
SQL> declare
2 cursor cur_depts is select deptno, dname, loc from dept;
3 cd_r cur_depts%rowtype;
4 begin
5 open cur_depts;
6 loop
7 fetch cur_depts into cd_r;
8 exit when cur_depts%notfound;
9 insert into dept_backup (deptno, dname, loc)
10 values (cd_r.deptno, cd_r. dname, cd_r.loc);
11 end loop;
12 close cur_depts;
13 end;
14 /
PL/SQL procedure successfully completed.
SQL> select * from dept_backup;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
Obviously, cursor FOR loop is way simpler.
i is the cursor record, you cannot insert it directly to another table, you need to refer to the specific columns.
For example,
INSERT INTO dept_backup (b_col1, b_col2, b_col3) values(i.c_col1,i.c_col2,i.c_col3);
Two issues (assuming dept and dept_backup have matching column lists):
there should be no brackets around the record variable when using this syntax.
you cannot explicitly close a for-loop cursor after the loop has completed and already implicitly closed it.
Try this:
declare
cursor cur_depts is
select * from dept;
begin
for r in cur_depts loop
insert into dept_backup values r;
end loop;
end;
(I prefer to use r for record loop variables to avoid confusion with i numeric loop variables.)

how to print the each column separated by dot in pl/sql

I am a beginner to PL/SQL. Consider I have three tables: emp, organization, emp_detail. Refer the image to know the table Schema, and result format.
I can get the result by joining these three tables based on the emp_id but I don't know how to print the dots (....) in the result.
Here's how; I don't have your tables (and don't feel like creating ones, as you didn't feel like providing test case yourself) so I used Scott's EMP.
If you don't care about nice alignment, omit RPAD function call and just concatenate desired number of dots.
SQL> set serveroutput on
SQL> begin
2 for cur_r in (select empno, ename, job from emp where deptno = 10) loop
3 dbms_output.put_line(cur_r.empno ||'.....'||
4 rpad(cur_r.ename, 15, '.') ||
5 cur_r.job
6 );
7 end loop;
8 end;
9 /
7782.....CLARK..........MANAGER
7839.....KING...........PRESIDENT
7934.....MILLER.........CLERK
PL/SQL procedure successfully completed.
SQL>
select cast(emp_id as varchar2(5)) || '....' || emp_name || '....' || organisation || '....'
|| cast(salary as varchar2(10))
from emp
join organisation on emp.emp_id=organisation.emp_id
join emp_details on emp.emp_id=emp_details.emp_id

Validating whether an emp is a manager in oracle

I am trying to build a trigger that would not allow users to give a salary higher than 10,000 to employees but if the employee is a manager, I don't want the user to be able to give less than 15000 of a salary.
What I reached so far is the following
create or replace trigger sal_check
before update or insert on emp
for each row
begin
if :new.sal > 10000
then
update emp set sal = 10000;
end if;
end;
Now can I please take a hint of how to do a check whether the employee is a manager or not?
Note : I am using Oracle, and this is running on Scott schema, the one that actually comes with Oracle by default.
If emp table has mgr column and you set up foreign key constraint, it's enough just to check whether mgr field is not null. Also, update emp set sal = 10000; in your code will cause ORA-04091 (table is mutating), you need to change it to :new.sal := 1000. So you trigger will look like
...
if :new.mgr IS NULL THEN
if :new.sal > 10000
then
:new.sal := 10000;
end if;
ELSE
if :new.sal < 15000
then
:new.sal := 15000;
end if;
END IF;
I think you should create a table (not temporary) with all the managers.
Afterwords, it would be quite easy to detect if a employee is a manager or not and by making a simple select on that table you could see what should be the salary.
To determine what employees should be on the table, it would be the ones in the mgr column.

insert statement in oracle without using user defined function

I have table employees with columns eno, ename, job, sal, comm
and the query like
INSERT a new employee
eno as 7787,
ename as 'abc',
job as 'salesman'
sal as 2000,
comm as tax amount
this tax is the function like
CREATE OR REPLACE FUNCTION tax
( p_sal employees.sal%type
)
RETURN NUMBER
IS
v_tax employees.sal%type;
BEGIN
v_tax:= CASE
WHEN SAL> 4000 THEN SAL * 0.33
WHEN SAL >2500 THEN SAL *0.25
WHEN SAL >1500 THEN SAL * 0.20
ELSE 0
END;
RETURN v_tax
END tax;
At the INSERT statement I can't use function tax for the column comm.
Is there any other method to do this, or how can this be best achieved?
When you say
I can't use function tax for the column comm
do you mean you're not allowed to use this function, or you can't figure out how to use it?
Assuming the latter, I don't see why you shouldn't be able to use a function in an INSERT statement. You have, however, got the syntax of the INSERT statement completely wrong.
Try
INSERT INTO employee (eno, ename, job, sal, comm)
VALUES (7787, 'abc', 'salesman', 2000, tax(2000));
I don't know where amount in your INSERT statement comes from, but given that your function takes a parameter called p_sal, I'm guessing it's applied to the value in the column sal.

Updating database records in a loop?

declare
begin
for i in (select * from emp)
loop
if i.sal=1300 then
update emp
set sal=13000;
end if;
end loop;
end;
This code is updating all the records with salary 13000. Instead i want to update records having salary 1300 to the value 13000. Can you tell where I made a mistake?
I am accesing records using implicit cursor..
for every record i am checking the sal value of that record..
if salary value in a particular record is 1500 i want to update it to 15000..
delete that code and just use:
update emp set sal = 13000 where sal = 1300
Whenever you can do the update with one single statement, you should do that instead of using a loop. You'll get a very huge performance gain that way; or, the other way round, updates in a loop cost you a lot of performance.
If you really really have to use a loop, of course you need a where condition to make sure you are only updating the record you really want to update. A possible way that always works (even if there is no unique key available) is to use the rowid pseudocolumn:
begin
for i in (select rowid, emp.* from emp)
loop
if i.sal=1300 then
update emp
set sal=13000
where rowid=i.rowid;
end if;
end loop;
end;
Another possibility would be using an explicit cursor and the "update ... where current of cursorname" syntax.
You need to put a constraint on your update statement.
What you have at the moment will loop through the results rows, and if it finds a row with salary equal to 1300, if then executest he following SQL:
update emp
set sal=13000;
Without the contraint this updates every row.
This code is updating all the records with salary 13000.
Instead i want to update records having salary 1300 to the value 13000.
for every record i am checking the sal value of that record..
if salary value in a particular record is 1500 i want to update it to 15000..
So what exactly do you want?
You want to update only 1,500 salary, you issue:
UPDATE emp
SET sal = 15000
WHERE sal = 1500;
You want to increase all salary ten times, you issue:
UPDATE emp
SET sal = sal * 10;
While some of these solutions are workable its not a one size fits all solution. I ran into a scenario where we had to set an xml/nvarchar(max) field to null on a table that has +50 M records. This is an excerpt of the code
begin
declare #rows int,
#y int = 2020,
#m int = 1;
set #rows = 1;
while (#rows > 0)
begin
update top (500) cr
set [xml] = null
from [dbo].[customer] cr with(index(ix_customer_reportdt))
where year([reportdt]) = #y
and month([reportdt]) = #m
and [xml] is not null;
set #rows = ##rowcount;
end
end
here's a quick solution that helps in removing the spaces (trimming) of a column data based on created date:
UPDATE table_Name SET column_name = LTRIM(RTRIM(column_name))
WHERE EXTEND(dateTime_column, YEAR TO DAY)='2020-01-31' ;