Not enough values while compiling procedure - oracle

Im trying to get the compile the below program and im getting not enough values error. This program will give the first 5 maximum and mininum salary holding by employees.
select * from all_tables where table_name like 'EMP5%';
create or replace package ptest1 is
Type rec is record (emin number,salmin number,min_salary number,emax number,salmax number,max_salary number);
TYPE T71 is table of rec;
procedure min_max(inp number, oup out T71);
end;
/
create or replace package body ptest1 as
procedure min_max(inp number, oup out T71)
as
begin
select emin,salmin,min_salary,emax,salmax,max_salary
into oup
from
(select * from
(select empno emin,sal salmin,row_number() over(order by sal asc) min_salary from emp5 where sal is not null) where min_salary <= inp ) t1,
(select * from
(select empno emax,sal salmax,row_number() over(order by sal desc) max_salary from emp5 where sal is not null) where max_salary <= inp) t2
where t1.min_salary = t2.max_salary;
for i in oup.first..oup.last
loop
dbms_output.put_line('Employee number '||oup(i).emin);
end loop;
end;
end ptest1;
/

Related

Oracle View or Table Function that returns union of queries stored as text in another table

Let's say that I have a "Rules" table that has a column that contains oracle queries in a varchar2 column:
Row
Query
1
select 'Hardcoded_Tablename_1' tablename, request_id from table_1 where status >= 4 and resolve_date < *some date math goes here*
2
select 'Table_2' tablename, t2.request_id from table_2 t2 join table_1 t1 on t1.request_id = t2.parent_id where t1.status >= 4 and t1.resolve_date < *some date math goes here*
If this were never going to change, I'd just make a view with a union of these queries.
Our requirement is that we be able to add to or to modify these rules on-the-fly at the whims of leadership.
So, what I need is either:
a very smart view (I think impossible) that executes and unions all of these stored query strings
or
a table function that returns the results of the union of these stored query strings. (I think this is the more likely solution)
It will only ever be those two columns: The hardcoded name of the table and the ID of the record.
Can someone help get me started on this?
Thanks
You can use a PIPELINED function.
First create the types:
CREATE TYPE request_data IS OBJECT (tablename VARCHAR2(30), request_id NUMBER);
CREATE TYPE request_list IS TABLE OF request_data;
Then the function:
CREATE FUNCTION get_requests RETURN request_list PIPELINED
IS
BEGIN
FOR r IN (SELECT "QUERY" FROM table_name ORDER BY "ROW")
LOOP
DECLARE
c_cursor SYS_REFCURSOR;
v_tablename VARCHAR2(30);
v_request_id NUMBER;
BEGIN
OPEN c_cursor FOR r."QUERY";
LOOP
FETCH c_cursor INTO v_tablename, v_request_id;
EXIT WHEN c_cursor%NOTFOUND;
PIPE ROW (request_data(v_tablename, v_request_id));
END LOOP;
CLOSE c_cursor;
EXCEPTION
WHEN NO_DATA_NEEDED THEN
CLOSE c_cursor;
RETURN;
END;
END LOOP;
END;
/
Then, if you have the sample data:
CREATE TABLE table_name ("ROW", "QUERY") AS
SELECT 1, q'[select 'Hardcoded_Tablename_1' tablename, request_id from table_1 where status >= 4 and resolve_date < SYSDATE]' FROM DUAL UNION ALL
SELECT 2, q'[select 'Table_2' tablename, t2.request_id from table_2 t2 join table_1 t1 on t1.request_id = t2.parent_id where t1.status >= 4 and t1.resolve_date < SYSDATE]' FROM DUAL
CREATE TABLE table_1 (request_id, status, resolve_date) AS
SELECT 42, 4, SYSDATE - 1 FROM DUAL;
CREATE TABLE table_2 (request_id, parent_id) AS
SELECT 57, 42 FROM DUAL;
Then you can use the function in a table collection expression:
SELECT *
FROM TABLE(get_requests());
Which outputs:
TABLENAME
REQUEST_ID
Hardcoded_Tablename_1
42
Table_2
57
db<>fiddle here
One option might be a function that returns refcursor.
SQL> select * from rules;
CROW QUERY
---------- ----------------------------------------------------------------------------------------------------
1 select 'EMP' tablename, empno from emp where hiredate = (select max(hiredate) from emp)
2 select 'DEPT' tablename, d.deptno from emp e join dept d on d.deptno = e.deptno where e.hiredate = (
select min(hiredate) from emp)
Function creates union of all queries from the rules table and uses it as a source for the refcursor:
SQL> create or replace function f_test return sys_refcursor
2 is
3 l_rc sys_refcursor;
4 l_str clob;
5 begin
6 for cur_r in (select query from rules order by crow) loop
7 l_str := l_str || cur_r.query ||' union all ';
8 end loop;
9 l_str := rtrim(l_str, ' union all ');
10
11 open l_rc for l_str;
12 return l_rc;
13 end;
14 /
Function created.
Testing:
SQL> select f_test from dual;
F_TEST
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
TABL EMPNO
---- ----------
EMP 7876
DEPT 20
SQL>

PL/SQL: ORA-00947: not enough values

Im creating a procedure to display the n number of maximum and minimum salary for an employee. If i ll give 5 as input, the query will get me 5 maximum and minimum salary for an employee.
For the above scenario, I have created an object with two columns like below
create type vrec as object(
empno number,
sal number
);
/
Then i created nested table with the help of object type, so that i can use the nested table as out parameter to return all the rows at one short.
create type vrec_type is table of vrec;
/
After the data type creation, im creating a procedure like below
create or replace procedure pro_first_last(input in number,salary out vrec_type)
is
begin
select empno,sal BULK COLLECT INTO salary from (
select empno,sal from
(select empno,sal,rank() over(order by sal asc) min_sal from emp5 where sal is not null) where min_sal <= 5
union all
select empno,sal from
(select empno,sal,rank() over(order by sal desc) max_sal from emp5 where sal is not null) where max_sal <= 5);
for i in salary.first..salary.last
loop
dbms_output.put_line(salary(i).empno);
end loop;
end;
/
When i compiling the above procedure, im getting not enough values. I have also created the object with two columns and in select statement also im returning only two column. Could someone review and help me on this or provide some alternate solution.
You are directly adding empno, sal values into salary (vrec_type object, which can take the values of only object type vrec)
You need to create the object of vrec and then add it into salary as following:
create or replace procedure pro_first_last(input in number,salary out vrec_type)
is
begin
select vrec(empno,sal) -- change in this line
BULK COLLECT INTO salary from (
select empno,sal from
(select empno,sal,rank() over(order by sal asc) min_sal from emp5 where sal is not null) where min_sal <= 5
union all
select empno,sal from
(select empno,sal,rank() over(order by sal desc) max_sal from emp5 where sal is not null) where max_sal <= 5);
for i in salary.first..salary.last
loop
dbms_output.put_line(salary(i).empno);
end loop;
end;
Cheers!!

PL\SQL Hot do I convert select value to bit flag?

I would like to have pl-sql like the following:
SELECT ID FROM some-table WHERE
(SELECT MAX(some-expression) FROM another-table = 1) OR
(some-table.ID IN SELECT (SELECT ID FROM one-more-table))
This pseudo-query would select all IDs from some-table if maximum value of some-expression equals 1 (or filter IDs by one-more-table values otherwise)
How do I properly implement this in PL-SQL ?
Thank you in advance!
Please find the below pl-sql:
DECLARE
v_Column1 NUMBER(4);
v_deptno NUMBER(2);
BEGIN
SELECT MAX(A.DEPTNO)
INTO v_deptno
FROM (SELECT DEPTNO FROM DEPT) A;
SELECT A.EMPNO
INTO v_Column1
FROM EMP A
WHERE A.DEPTNO=v_deptno
OR
A.EMPNO IN ( SELECT EMPNO FROM EMP_2);
END;
/

I want to return top 4 highest salaries from each dept along with deptno and dname in a stored procedure

I want to return top 4 highest salaries from each dept along with deptno and dname in a stored procedure.
when deptno is not included there is no error but
When including deptno im getting an error invalid number.
ORA-01722: invalid number
Here is the code
create or replace procedure tpro2(dno in emp.deptno%type,dnum out dept.deptno%type, name out emp.ename%type, sal out emp.sal%type,dname out dept.dname%type, cur out sys_refcursor)
is begin
open cur for select ename , sal, dname, dept.deptno from (select * from emp order by sal desc)emp, dept where emp.deptno=dept.deptno and emp.deptno=dno and rownum<=4;
end tpro2;
/
declare
dnum dept.deptno%type;
name emp.ename%type;
sal emp.sal%type;
dname dept.dname%type;
cur sys_refcursor;
begin
tpro2(&dno,dnum,name,sal,dname,cur);
loop
fetch cur into dnum,name,sal,dname;
exit when cur%notfound;
dbms_output.put_line(dnum||' '||name||' '||sal||' '||dname);
end loop;
close cur;
end;
/
Please help!!!
open cur for select ename, sal, dname, dept.deptno
fetch cur into dnum, name, sal, dname;
Does you see nothing suspicious here? :)
I would use window functions to get the information you need without writing a stored procedure at all:
SELECT ename, sal, dname, deptno
RANK() OVER (PARTITION BY deptno ORDER BY sal DESC) "Rank"
FROM emp e inner join dept d using (deptno)

Creating ORACLE PL/SQL store procedures with different kind of AND conditions

I need to create a Oracle query for example
select * from emp where emp_id=i_emp_id and emp_nm=i_emp_nm and emp_dpt=i_emp_dpt
if all the three inputs are not null it should function like
select * from emp where emp_id=i_emp_id and emp_nm=i_emp_nm and emp_dpt=i_emp_dpt
if i pass i_emp_id as null then the query should function like
select * from emp where emp_nm=i_emp_nm and emp_dpt=i_emp_dpt
if i pass i_emp_id as null and i_emp_dpt as null then the query should function like
select * from emp where emp_nm=i_emp_nm
The best way to handle different permutations of input variables is to assemble the query dynamically. The following example will produce a query which performs well and handles NULL values neatly so as to return the correct result.
create or replace function get_dyn_emps
(i_empno in emp.empno%type
, i_ename in emp.ename%type
, i_deptno in emp.deptno%type)
return sys_refcursor
is
rc sys_refcursor;
stmt varchar2(32767);
begin
stmt := 'select * from emp where 1=1';
if i_empno is not null
then
stmt := stmt||' and empno = :p_empno';
else
stmt := stmt||' and (1=1 or :p_empno is null)';
end if;
if i_ename is not null
then
stmt := stmt||' and ename = :p_ename';
else
stmt := stmt||' and (1=1 or :p_ename is null)';
end if;
if i_deptno is not null
then
stmt := stmt||' and deptno = :p_deptno';
else
stmt := stmt||' and (1=1 or :p_deptno is null)';
end if;
open rc for stmt
using i_empno, i_ename , i_deptno;
return rc;
end get_dyn_emps;
/
This may seem like a long-winded solution compared to the currently-accepted answer, but here's why it is the better approach: it returns the correct answer.
In deparment 40 there is an employee with no name:
SQL> var rc refcursor
SQL> exec :rc := get_dyn_emps(null, null, 40)
PL/SQL procedure successfully completed.
SQL> print rc
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
------- ---------- --------- ---------- --------- ---------- ---------- ---------
8101 03-DEC-10 40
SQL>
If I implement the apparently neater DECODE() solution ...
create or replace function get_fix_emps
(i_empno in emp.empno%type
, i_ename in emp.ename%type
, i_deptno in emp.deptno%type)
return sys_refcursor
is
rc sys_refcursor;
begin
open rc for
SELECT * FROM emp
WHERE empno = DECODE(NVL(i_empno,0), 0, empno, i_empno)
AND ename = DECODE(NVL(i_ename,'X'), 'X', ename, i_ename)
AND deptno = DECODE(NVL(i_deptno,0), 0, deptno, i_deptno);
return rc;
end get_fix_emps;
/
... this is what happens:
SQL> exec :rc := get_fix_emps(null, null, 40)
PL/SQL procedure successfully completed.
SQL> print rc
no rows selected
SQL>
Because NULL does not ever equal NULL, which is what ename = DECODE(NVL(i_ename,'X'), 'X', ename, i_ename) evaluates to in this case.
As I did in my applications, you can achieve this functionality by simply using NVL and DECODE functions.
SELECT * FROM emp
WHERE emp_id = DECODE(NVL(i_emp_id,0), 0, emp_id, i_emp_id)
AND emp_nm = DECODE(NVL(i_emp_nm,0), 0, emp_nm, i_emp_nm)
AND emp_dpt = DECODE(NVL(i_emp_dpt,'X'), 'X', emp_dpt, i_emp_dpt)
If i_emp_id is null than it will match with current value so all records will match otherwise only record which matches i_emp_id will return. Same applies to emp_nm and emp_dpt.

Resources