How to start procedure in oracle? - sql

So I have this SELECT statement:
SELECT emp.emp_name, pos_emp.POSITION_NAME
from EMPLOYEE emp
join POSITION_EMPLOYEE pos_emp
on emp.POSITION_EMPLOYEE_POSITION_ID=pos_emp.POSITION_ID
where emp.EMP_NAME='&employee_name';
When I enter employee name from keyboard it returns me his name(from EMPLOYEE table) and position(from POSITION_EMPLOYEE table). But I want to do this with stored procedure:
create or replace PROCEDURE emp_pos( EMPLOYEE_NAME IN EMPLOYEE.EMP_NAME%TYPE,
POSITION_NAME OUT POSITION_EMPLOYEE.POSITION_NAME%TYPE )
AS
BEGIN
SELECT pos_emp.POSITION_NAME
INTO
POSITION_NAME
FROM EMPLOYEE emp
JOIN
POSITION_EMPLOYEE pos_emp
ON
emp.POSITION_EMPLOYEE_POSITION_ID
= pos_emp.POSITION_ID
WHERE emp.EMP_NAME
= EMPLOYEE_NAME
;
END;
I am trying to start the procedure with begin:
begin emp_pos('&employee_name');
end;
The compiler gives me error:wrong number or types of arguments in call to 'EMP_POS'. Where am I wrong?

SQL>set serveroutput on;
SQL> create or replace PROCEDURE emp_pos( EMPLOYEE_NAME IN EMPLOYEE.EMP_NAME%TYPE,
POSITION_NAME OUT POSITION_EMPLOYEE.POSITION_NAME%TYPE )
AS
BEGIN
SELECT pos_emp.POSITION_NAME
INTO
POSITION_NAME
FROM EMPLOYEE emp
JOIN
POSITION_EMPLOYEE pos_emp
ON
emp.POSITION_EMPLOYEE_POSITION_ID
= pos_emp.POSITION_ID
WHERE emp.EMP_NAME
= EMPLOYEE_NAME
;
dbms_output.put_line(EMPLOYEE_NAME||'''s position is : '||POSITION_NAME);
END;
after defining this procedure, you may use in this way :
SQL>var v_emp_pos varchar2;
SQL>exec emp_pos('&employee_name',:v_emp_pos);
SQL>print v_emp_pos; -- just to print out this "out" parameter.

Related

Write PL/SQL Procedure

Write PL/SQL Procedure for the given Specification.
Assume the Table design is as follows :
Table Name :
employee_details
Column Name Data Type Constraint
Emp_ID NUMBER(5) NOT NULL Primary Key
Emp_Name VARCHAR2(100) NOT NULL
Date_Joined DATE NOT NULL
Gender CHAR(1) NOT NULL
Salary NUMBER(20,2) NOT NULL
Increment CHAR(1) NULL Y / N
Procedure is for incrementing the salary with the given value if the Increment Flag for the Employee is set to Y and commit the changes done
Procedure Name : PRC_INCREMENT
Input Parameters : a_Emp_ID, a_Salary
Output Parameters : a_Exec_Status
return messages by procedure
if Given employee is not found 'EMPLOYEE IS NOT FOUND'
Employee is found and Increment flag is not Y 'INCREMENT NOT APPLICABLE FOR EMPLOYEE' – [GIVEN EMPLOYEE ID]
Successfully update 'SUCCESSFULLY UPDATED FOR EMPLOYEE' – [GIVEN EMPLOYEE ID]
Any other unhandled exception ERROR with the information about ORACLE ERROR
below code i have tried, plx help me with procedure
CREATE OR replace PROCEDURE Prc_increment(a_emp_id IN VARCHAR2,
a_salary IN VARCHAR2)
AS
CURSOR c1 IS
SELECT emp_id,
salary,
increment_stat
FROM emp
WHERE emp_id = a_emp_id;
num_salary emp.salary%TYPE;
num_emp_id emp.emp_id%TYPE;
num_increment_stat emp.increment_stat%TYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO num_emp_id, num_salary, num_increment_stat;
IF num_emp_id <> a_emp_id THEN
dbms_output.Put_line('Employee not found');
ELSIF num_increment_stat <> 'Y' THEN
dbms_output.Put_line('Employee not eligible for increment');
ELSE
UPDATE emp
SET salary = num_salary + a_salary
WHERE emp_id = a_emp_id
AND increment_stat = 'Y';
END IF;
EXIT WHEN c1% NOTFOUND;
END LOOP;
EXCEPTION
WHEN OTHERS THEN
dbms_output.Put_line(SQLERRM);
CLOSE c1;
END;

How to call a function within a procedure to update all records of a table on PLSQL?

I'm practicing PLSQL and I'm coding a package with 2 functions to update commission and the other one to update salary but now I want to create a procedure within the same package to update commission and salary for all employees using the functions on the package. Is it possible?
CREATE OR REPLACE PACKAGE BODY emp_upd_pkg IS
-- Function to update commission_pct --
FUNCTION comm_upd(
p_empid employees.commission_pct%TYPE)
RETURN NUMBER
IS
v_new_comm employees.commission_pct%TYPE;
BEGIN
UPDATE employees
SET commission_pct = commission_pct * 1.1
WHERE employee_id = p_empid;
SELECT commission_pct
INTO v_new_comm
FROM employees
WHERE employee_id = p_empid;
RETURN v_new_comm;
EXCEPTION
WHEN
NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20992, 'NO EXISTE EMPLEADO');
END comm_upd;
-- Function to update salary --
FUNCTION sal_upd(
p_empid employees.salary%TYPE)
RETURN employees.salary%TYPE
IS
v_newsal employees.salary%TYPE;
BEGIN
UPDATE employees
SET salary = salary + 350
WHERE employee_id = p_empid;
-- Consulta select para la salida del a funcion --
SELECT salary
INTO v_newsal
FROM employees
WHERE employee_id = p_empid;
RETURN v_newsal;
END sal_upd;
-- Procedure to update all records of employees table --
PROCEDURE comm_sal_upd(
p_new_comm employees.commission_pct%TYPE,
p_new_sal employees.salary%TYPE);
END emp_upd_pkg;
I've tried creating a cursor and fetching into functions but I didn't succeed.
PROCEDURE comm_sal_upd(
p_new_comm employees.commission_pct%TYPE,
p_new_sal employees.salary%TYPE)
IS
CURSOR emp_cur IS
SELECT commission_pct, salary
FROM employees;
BEGIN
OPEN emp_cur;
FETCH emp_cur
INTO emp_upd_pkg.comm_upd(p_comm), emp_upd_pkg.sal_upd(p_sal);
CLOSE emp_cur;
END comm_sal_upd;
You are using function so its returing a value. you must capture the value in your procedure as below:
PROCEDURE comm_sal_upd(
p_new_comm employees.commission_pct%TYPE,
p_new_sal employees.salary%TYPE)
IS
CURSOR emp_cur IS
SELECT commission_pct, salary
FROM employees;
var number;
var2 employees.salary%TYPE;
BEGIN
for rec in emp_cur
loop
var:= emp_upd_pkg.comm_upd(p_comm);
var2:=emp_upd_pkg.sal_upd(p_sal);
dbms_output.put_line('updated commission--'|| var || ' Updated Sal -- '|| var2);
end loop;
commit;
END comm_sal_upd;
First of all, few tricks to improve your functions.
You don't need to make two queries when you can make one. Instead of
v_new_comm employees.commission_pct%TYPE;
BEGIN
UPDATE employees
SET commission_pct = commission_pct * 1.1
WHERE employee_id = p_empid;
SELECT commission_pct
INTO v_new_comm
FROM employees
WHERE employee_id = p_empid;
RETURN v_new_comm;
use returning clause:
v_new_comm employees.commission_pct%TYPE;
BEGIN
UPDATE employees
SET commission_pct = commission_pct * 1.1
WHERE employee_id = p_empid
returning commission_pct
into v_new_comm;
RETURN v_new_comm;
Also, this exception block doesn't look like very helpful:
EXCEPTION
WHEN
NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20992, 'NO EXISTE EMPLEADO');
You just change the language of the error message.
As for the procedure to update all records, there are several issues:
You should avoid update many records one by one, it significantly reduces the performance. If possible, update them all together:
UPDATE employees
SET commission_pct = commission_pct * 1.1,
salary = salary + 350;
Cursors don't work this way. You shold use them to select data.
To process all rows in cursors, use loops:
PROCEDURE comm_sal_upd(
p_new_comm employees.commission_pct%TYPE,
p_new_sal employees.salary%TYPE)
IS
CURSOR emp_cur IS
SELECT commission_pct, salary
FROM employees;
v_comission number;
v_salary number;
BEGIN
OPEN emp_cur;
loop
FETCH emp_cur INTO v_comission, v_salary;
EXIT WHEN emp_cur%NOTFOUND;
<do something>
end loop;
CLOSE emp_cur;
END comm_sal_upd;
With one FETCH you process only one record.
I think the following is the kind of thing you are trying to do. (Untested, probably has bugs.) Note this is the least efficient way to do anything in PL/SQL, so it's just to demonstrate how you might structure procedures that call each other.
(btw I don't know if there is some textbook out there that tells people to code in uppercase, but there is really no need to.)
create or replace package body emp_upd_pkg
as
-- Update commission_pct for one employee:
procedure comm_upd
( p_empid employees.employee_id%type
, p_new_comm employees.commission_pct%type )
is
begin
update employees set commission_pct = commission_pct * p_new_comm
where employee_id = p_empid;
if sql%rowcount = 0 then
raise_application_error(-20992, 'Commission update failed: employee id ' || p_empid || ' not found.', false);
end if;
end comm_upd;
-- Update salary for one employee:
procedure sal_upd
( p_empid employees.employee_id%type
, p_new_sal employees.salary%type )
is
begin
update employees set salary = salary + p_new_sal
where employee_id = p_empid;
if sql%rowcount = 0 then
raise_application_error(-20993, 'Salary update failed: employee id ' || p_empid || ' not found.', false);
end if;
end sal_upd;
-- Update all employees:
procedure comm_sal_upd
( p_new_comm employees.commission_pct%type
, p_new_sal employees.salary%type )
is
begin
for r in (
select employee_id from employees
)
loop
comm_upd(r.employee_id, p_new_comm);
sal_upd(r.employee_id, p_new_sal);
end loop;
end comm_sal_upd;
end emp_upd_pkg;

pl/sql function- pass multiple varchar2 values dynamically for SELECT ... NOT IN()

I have a table EMP with following definition:
EMP_ID NOT NULL NUMBER(6)
EMP_NAME NOT NULL VARCHAR2(25)
EMAIL NOT NULL VARCHAR2(25)
PHONE_NUMBER VARCHAR2(20)
HIRE_DATE NOT NULL DATE
JOB_ID NOT NULL VARCHAR2(10)
SALARY NUMBER(8,2)
If we want to count employees whose name not in 'King', 'Steve', 'John' we simply use this query :
SELECT count(*) FROM emp WHERE emp_name NOT IN('King','Steve','John');
Now Here is what I want above this:
What I want to do is, Create a PL/SQL Function which returns number of count according to the dynamic input, Like if we pass:
SELECT count_emp('King,Steve,John') FROM dual;
SELECT count_emp('William,Donald') FROM dual;
SELECT count_emp('Daniel') FROM dual;
needs to return appropriate count, how can I achieve this using PL/SQL FUNCTION
This is what I have tried and Needs guideline:
CREATE OR REPLACE FUNCTION count_emp(emp_nm IN varchar)
RETURN number
IS
cnt NUMBER;
BEGIN
SELECT count(*) INTO cnt FROM emp WHERE emp_name NOT IN(emp_nm);
RETURN cnt;
END;
it is giving result for single name, but how can I split/format multiple input(i.e. emp_nm) to pass in NOT IN()?
Try like this,
CREATE OR REPLACE
FUNCTION count_emp(emp_nm IN VARCHAR)
RETURN NUMBER
IS
cnt NUMBER;
BEGIN
SELECT count(*)
INTO cnt
FROM emp
WHERE ename NOT IN(
SELECT regexp_substr (emp_nm, '[^,]+', 1, ROWNUM)
FROM dual
CONNECT BY LEVEL <= LENGTH (regexp_replace (emp_nm, '[^,]+')) + 1);
RETURN cnt;
END;
You can try dynamic sql:
CREATE OR REPLACE FUNCTION count_emp(emp_nm IN varchar)
RETURN number
IS
cnt NUMBER;
BEGIN
Execute immediate 'SELECT count(*) FROM emp WHERE emp_name NOT IN(' || emp_nm || ')' returning into cnt;
RETURN cnt;
END;
You can also use MEMBER OF. Here is a snippet. Hope this helps!
-- Create a type in SQL
CREATE OR REPLACE TYPE t_emp_name AS TABLE OF VARCHAR2 (10);
-- use MEMBER OF to use your list as IN parameter
CREATE OR REPLACE FUNCTION count_emp (emp_nm IN t_emp_name)
RETURN NUMBER
IS
cnt NUMBER;
BEGIN
SELECT COUNT (*)
INTO cnt
FROM emp
WHERE emp_name NOT MEMBER OF (emp_nm);
RETURN cnt;
END;
-- Assign values to the list, you can do it dynamically as well. Call the function
DECLARE
l_emp_name_list t_emp_name;
lv_count NUMBER;
BEGIN
l_emp_name_list := t_emp_name ('King', 'Steve'); --add more names as needed
lv_count := count_emp (l_emp_name_list);
END;

Oracle SQL Code

Trying to use this code, which was provided on here, however, it returns the following error when trying to run:
PLS-00103: Encountered the symbol "IS" when expecting one of the following:
constant exception table long double ref char time timestamp interval
date binary national character nchar
1. DECLARE
2. emp employee%ROWTYPE;
3. tbl_emp IS TABLE OF emp;
4. v_user_type employee.user_type%TYPE;
5. BEGIN
Code is:
DECLARE
emp employee%ROWTYPE;
tbl_emp IS TABLE OF emp;
v_user_type employee.user_type%TYPE;
BEGIN
SELECT user_type
INTO v_user_type
FROM Employee
WHERE upper(username) = v('APP_USER');
IF v_user_type = 1
THEN
SELECT * BULK COLLECT INTO tbl_emp
FROM employee;
ELSE
SELECT * BULK COLLECT INTO tbl_emp
FROM employee;
WHERE upper(username) = v('APP_USER');
END IF;
END;
/
You need to first declare type, then variable of that type.
E.g.
declare
type tbl_emp_type IS TABLE OF employee%ROWTYPE;
tbl_emp tbl_emp_type;
begin
select *
bulk collect into tbl_emp
from employee;
end;
SQL Fiddle

Update employees table

Guys I have the following problem:
Increase salary 15% for employees whose salary is less than 50% of their manager's salary.
Write PL/SQL procedure using cursor, loop and update.
Procedure header
Create or replace procedure inc_salary is:
. Exception if their salary after increase is more than 50% of their manager's salary.
Actually, we can do it directly like this:
update emp e
set e.salary+=e.salary*0.15
where e.salary<(select e.mgr from emp e, group by e.mgr)
Here is a picture of this table:
But I don't understand how to use the procedure. If I declare it like this, create or replace procedure inc_salary, then what should be its parameters? We can use of course loop, like
declare
for r in (select * from emp e) loop
update emp e
set r.salary+=r.salary*0.15;
where r.salary<r.mgr
exception
if r.salary >r.mgr*1.15 then
dbms.output_putline(' it can't increase');
end loop;
end;
But how to combine it together?
Why would you need a PL/SQL procedure? A simple query would do this job!
UPDATE emp
SET salary = salary * 1.15
WHERE empno IN (
SELECT e.empno
FROM emp e
JOIN emp m ON e.mgr = m.empno
WHERE e.salary < m.salary * 0.5
)
That's it!
But, if at all you need to use a procedure, you have to decide for yourself what exactly you want to do with it.
Every procedure has a set of formal parameters, which can even be an empty set. It is you who decides what to pass to a procedure. Consult your manager or architect for these situations.
declare
prec number;
procedure inc_salary(prcin number)
is
cursor cl is * from employees;
msal number(8,2);
mid number(6);
begin
for r in cl loop
mid := nvl(r.manager_id, r.employee_id);
select salary into msal from employees where employee_id = mid;
if r.salary < (msal * 0.5) then
update employees set
salary = salary * prc
where employee_id = r.employee_id;
end if;
end loop;
end inc_salary;
begin
prec := 1.5;
inc_salary(prec);
end ;