passing parameter but unable to fetch? - oracle

how to resolve this one, unable to fetch ?
I am passing the IN parameter but still unable to fetch.
create or replace procedure p1(p_ename in varchar2,p_sal out number)is
begin
select salary into p_sal from employees where last_name=p_ename;
dbms_output.put_line(p_sal);
end;
variable b number;
execute p1('King',:b);
[info](https://infoallsite.wordpress.com/2016/01/29/unable-to-fetch- data)
[error][1]
' got error ,but only one row has last_name as King, '
how to resolve I want to get the salary of king.
[1]: http://i.stack.imgur.com/AsOHG.png'

You table probably has more than 1 rows for name King.
Run this
select count(*) from employees where last_name='King';
If it returns more than 1 row, then you have to choose which row do you need to select. If you want any row at random, then use this select in your procedure.
select salary into p_sal from employees
where last_name=p_ename
and rownum<2;

Related

SYS_REFCURSOR is returning all the rows from table without considering the IN parameter

I am facing a weird problem here.
PROCEDURE USL_EMPLOYEEBYID (
EMPLOYEE_ID IN NUMBER,
EMPIDCURSOR OUT SYS_REFCURSOR
)
AS
BEGIN
OPEN EMPIDCURSOR FOR
SELECT emp.employee_id,emp.employee_name,emp.present_address,emp.permanent_address,emp.status
FROM Employee_Info emp
WHERE emp.employee_id = EMPLOYEE_ID;
END;
This procedure should give me a single employee upon entering the employee Id. But it is returning all the employees.
What am I doing wrong here?
In your query, Oracle interprets EMPLOYEE_ID as the column EMPLOYEE_ID, not the input parameter, here you find something more; in this way, your where condition is something like a=a.
Change the parameter name to distinguish it from the table column:
PROCEDURE USL_EMPLOYEEBYID (
p_EMPLOYEE_ID IN NUMBER,
po_EMPIDCURSOR OUT SYS_REFCURSOR
)
AS
BEGIN
OPEN po_EMPIDCURSOR FOR
SELECT emp.employee_id,emp.employee_name,emp.present_address,emp.permanent_address,emp.status
FROM Employee_Info emp
WHERE emp.employee_id = p_EMPLOYEE_ID;
END;
this is a good practice, to always know in your code whether you are handling an input parameter, a local variable, a column and so on

Oracle Trigger How to get Inserted value's parameter

I have a question while studying Oracle Database's triggers.
First of all, I created two tables like below:
**[Employee]**
(empno NUMBER, VARCHAR2 (20), deptno NUMBER, salary NUMBER)
**[Department]**
(deptno NUMBER, dname VARCHAR2 (20))
Employee deptno refers to Department's deptno.
The trigger I want to create is to calculate the salary average of the data with deptno, such as inserted deptno.
For example, suppose I run the following code.
INSERT INTO employee values (1000, 'PAUL', 10, 4000);
Then Trigger would like to have the following output:
Department [10]'s average of Salary = <avg (salary)>
But I do not know how to pass inserted row's parameter in the Trigger.
Does anyone have a good way for me?
I realized that if I use the AFTER INSERT keyword in the trigger, I get an 'ORA-04091' error because the contents of the table are changing. So I found out that I had to use before insert to get the result.
This is not the correct way, but I was able to express the output I wanted.
It was very simple. Before the table is changed (BEFORE INSERT Keyword), I store the sum of the columns in the variable to get the average.
Then add the new value to the variable using the ': NEW' keyword.
When I used this method, I did not get the 'ORA-04091' error because the table is still before the change.
Thanks to a lot of people, I've found that this is not a good way to write in a trigger.
However, I am thinking about this problem and I have learned a lot of internal actions such as the cost of triggers and when to call them.
Thank you again for all the answerer.
This makes no sense to be implemented as a trigger. This is just a simple SQL query :
select deptno ,avg(salary)
from employee
group by deptno
Trigger is not the best place for such things, but it can be done with compound trigger:
create or replace trigger avg_dept_salary for insert on employee compound trigger
type tb_num is table of number;
v_depts tb_num := tb_num();
v_sum number;
before each row is
begin
if :new.deptno not member of v_depts then
v_depts.extend();
v_depts(v_depts.count()) := :new.deptno;
end if;
end before each row;
after statement is
begin
for i in 1..v_depts.count() loop
select avg(salary) into v_sum from employee where deptno = v_depts(i);
dbms_output.put_line('Department '||v_depts(i)||' average salary: '||v_sum);
end loop;
end after statement;
end;
Test:
insert into employee
select 1001, 'PAUL', 10, 1000 from dual union all
select 1002, 'ANNE', 10, 1700 from dual union all
select 1003, 'MARY', 10, 900 from dual union all
select 1004, 'JOHN', 20, 1200 from dual;
Output for each affected department:
Department 10 average salary: 1900
Department 20 average salary: 1200

Accept uni or multi-dimensional parameter in PL/SQL function

I'm trying to write a function nbrJobs() in PL/SQL that counts the number of jobs that a user has had in the past.
In order to do this I first need to determine the "employee id", which can be determined by a pair (first name, last name).
I manage to do this when the arguments of the function are:
nbrJobs(firstName IN VARCHAR2(20), lastName IN VARCHAR2(25))
Then, this simple test runs without any problem:
DECLARE
nbrJobsTotal NUMBER;
BEGIN
SELECT nbrJobs(first_name, last_name) INTO nbrJobsTotal
FROM employees
WHERE employee_id = 101
END
Now, the problem is that my function should also work with that kind of call:
SELECT nbrJobs(first_name, last_name) INTO nbrJobsTotal FROM employees
with table employees containing multiple tuples.
So, now I'm confused about the input parameters type.. Should I use a VARRAY, a nested TABLE, a CURSOR, something else ?
What does a SELECT actually returns if multiple rows are selected?
A PL/SQL function is executed for each row of the SELECT statement. Therefore, if you call your function in a regular SELECT SQL statement you will get a value for each record.
Here is an example by concatenating the first and last name together:
CREATE OR REPLACE FUNCTION NAME (p_FIRST_NAME IN VARCHAR2, p_LAST_NAME IN VARCHAR2)
RETURN VARCHAR2
AS
BEGIN
RETURN p_FIRST_NAME || ' ' || p_LAST_NAME;
END;
/
SELECT first_name, last_name, name(first_name, last_name) FROM HR.employees;
FIRST_NAME LAST_NAME NAME(FIRST_NAME,LAST_NAME)
---------- --------- --------------------------
Ellen Abel Ellen Abel
Sundar Ande Sundar Ande
Mozhe Atkinson Mozhe Atkinson
David Austin David Austin
As you can see, for each row the PL/SQL function is executed and concatenates the first and last name and then is returning the result as a new column to the SELECT statement. There is no need for you to change the function to make it work with multiple row.
Now how would you be executing this inside PL/SQL with the SELECT statement that you have used as example above. You will either have to loop over the results with a cursor or you could use a collection type if you just want to fetch the result of all of the rows into a variable.
Using a cursor (
I'm demonstrating this on my example above by using the Cursor FOR LOOP):
BEGIN
FOR result IN (SELECT first_name, last_name, name(first_name, last_name) name FROM HR.employees) LOOP
DBMS_OUTPUT.PUT_LINE(result.first_name || ', ' || result.last_name || ', ' || result.name);
END LOOP;
END;
/
Ellen, Abel, Ellen Abel
Sundar, Ande, Sundar Ande
Mozhe, Atkinson, Mozhe Atkinson
David, Austin, David Austin
What happens here is that I'm executing the very same SELECT statement but now inside the Cursor FOR LOOP which allows me to loop over each individual row that has been return. In this case I just print the result out into the console.
If you want to just save all the rows into a variable you will have to use a PL/SQL Collection:
DECLARE
-- Specify cursor with expected results
CURSOR c1 IS
SELECT first_name, last_name, name(first_name, last_name) name
FROM HR.employees;
-- Create PL/SQL type for a nested table of the rowtype of the cursor (first_name, last_name, name)
TYPE NameSet IS TABLE OF c1%ROWTYPE;
employees NameSet; -- Instantiate a variable of the nested table of records
BEGIN
-- Assign values to nested table of records:
SELECT first_name, last_name, name(first_name, last_name) name
BULK COLLECT INTO employees
FROM HR.employees;
-- Print nested table of records:
FOR i IN employees.FIRST .. employees.LAST LOOP
DBMS_OUTPUT.PUT_LINE (
employees(i).first_name || ' ' ||
employees(i).last_name || ', ' ||
employees(i).name
);
END LOOP;
END;
/
Ellen Abel, Ellen Abel
Sundar Ande, Sundar Ande
Mozhe Atkinson, Mozhe Atkinson
David Austin, David Austin
As you can see here the same SELECT is executed but here we use BULK COLLECT INTO rather than just INTO. This is because the SELECT is returning more than one row, hence we need to tell the compiler that we do indeed expect that so that the compiler doesn't throw an error that more rows have been returned.
Last but not least, given that you use the variable name nbrJobsTotal in your example SELECT nbrJobs(first_name, last_name) INTO nbrJobsTotal FROM employees above, I think what you really want to try to do here is to sum up all the number of different jobs that employees had in your company. You can accomplish just that but using the built-in SUM() function which is an aggregation function, i.e it will only return one row without a GOUP BY clause:
SELECT SUM(nbrJobs(first_name, last_name)) INTO nbrJobsTotal FROM employees
You can use a Table of Type - to bulk collect all rows returned by the select statement.
Based on your example it would look like this:
DECLARE
nbrJobsTotal NUMBER;
TYPE jobsTotalTable_type IS TABLE OF nbrJobsTotal%TYPE;
jobsTotalTable jobsTotalTable_type ;
BEGIN
--bulk collect results
SELECT nbrJobs(first_name, last_name) BULK COLLECT INTO jobsTotalTable
FROM employees;
--print results
FOR indx IN 1 .. jobsTotalTable.COUNT LOOP
DBMS_OUTPUT.PUT_LINE(jobsTotalTable (indx));
END LOOP;
END

PL/SQL: Executing Procedure

I have created a procedure. It is giving an error ( ORA-01422: exact fetch returns more than requested number of rows). As for a specific department_id there are more than one employees. But how to solve this problem ?
Create Procedure PP1
(ID in number, Percent in number, Sal out number, increase_sal out number) IS
Begin
Select salary, salary *(1+percent/100) into sal, increase_sal
From employees
where department_id= id;
DBMS_OUTPUT.PUT_LINE (sal || ' ' || increase_sal);
END;
/
Variable a number
Variable b number
Exec PP1 (100, 10, :a, :b)
Print a b
Thanks,
Kuntal Roy
My guess is that you want something like (untested)
CREATE TYPE num_tbl IS TABLE OF NUMBER;
CREATE PROCEDURE raise_dept_salaries( p_dept_id IN employees.department_id%type,
p_raise_pct IN NUMBER,
p_old_sals OUT num_tbl,
p_new_sals OUT num_tbl )
AS
BEGIN
SELECT salary
BULK COLLECT INTO p_old_sals
FROM employees
WHERE department_id = p_dept_id;
UPDATE employees
SET salary = salary * (1 + p_raise_pct)
WHERE department_id = p_dept_id
RETURNING salary
BULK COLLECT INTO p_new_sals;
END;
Now, splitting things up this way does introduce the possibility that some other session will modify the data between your first SELECT and your UPDATE so this isn't really safe to use in a multi-user environment. Of course, you really wouldn't want to return both the old and the new salaries in the first place since you already know that they are going to be directly related to each other. If you only returned the collection of new salaries, then you would only need a single UPDATE statement and you wouldn't have the race condition.

Iterate over a column in PL/SQL

I have a table Emp with EmpID, Empname, Salary and I am trying to do a calculation for each employee. But I am having problems trying to iterate over each emp to do the calculation. I cant use explicit cursors though.
So right now I am just trying to create the list of empIDs:
Declare
aRows Number;
eid emp_ID%TYPE;
Begin
Select Count(*)
Into aRows
from emp;
Select emp_ID
Into eid
From emp;
FOR days IN 1..Tot_Rows
Loop
Dbms_Output.Put_Line(eid);
eid := eid + 1;
End Loop;
END;
But I get the error:
PLS-00320: the declaration of the type of this expression is incomplete or malformed
The simplest way to iterate over the rows in a table in PL/SQL is to do something like
BEGIN
FOR employees IN (SELECT emp_id FROM emp)
LOOP
dbms_output.put_line( employees.emp_id );
END LOOP;
END;
Alternately, you could fetch all the EID values into a PL/SQL collection and iterate over the collection, as in this example
DECLARE
TYPE emp_id_tbl IS TABLE OF emp.emp_id%type;
l_emp_ids emp_id_tbl ;
BEGIN
SELECT emp_id
BULK COLLECT INTO l_emp_ids
FROM emp;
FOR i IN l_emp_ids .FIRST .. l_empnos.LAST
LOOP
dbms_output.put_line( l_emp_ids (i) );
END LOOP;
END;
If your query can return thousands of rows, however, fetching all the data into the collection may use more of the PGA memory than you'd like and you may need to fetch rows in chunks using the LIMIT clause. But that would seem to be getting ahead of ourselves at this point.
Justin Cave has explained how to do it, but to specifically look at the error you got, that was because of this:
eid emp_ID%TYPE;
When using the %TYPE you have to specify the table name as well as the column name:
eid emp.emp_ID%TYPE;
If you were selecting all the columns in the row you could also look at %ROWTYPE.
Your approach was also making two assumptions: that the initial select into eid found the lowest ID, which is by no means guaranteed; and that all the subsequent ID values are sequential. And you're declaring and populating aRows but referring to Tot_Rows.

Resources