Can I use "order by" with user input in oracle? - oracle

I use the same procedure for several tasks. Each function also needs to use ORDER BY every time.
I want to use ORDER BY with user inputs.
I tried this but it didn't work.
PROCEDURE GET_DEPARTMENT_LIST(ORDER_BY_PARAM IN VARCHAR2, DEPT_DATA OUT T_CURSOR) IS
V_CURSOR T_CURSOR;
BEGIN
OPEN V_CURSOR FOR
SELECT GET_LIST(ORDER_BY_PARAM) FROM DUAL;
DEPT_DATA := V_CURSOR;
END GET_DEPARTMENT_LIST;
FUNCTION GET_LIST (PAR_ORDER_BY IN VARCHAR2)
RETURN SYS_REFCURSOR
IS
L_CR SYS_REFCURSOR;
BEGIN
OPEN L_CR FOR
SELECT DEPARTMENT_ID, DEPARTMENT_CODE, DEPARTMENT_NAME
FROM DEPARTMENT ORDER BY PAR_ORDER_BY ASC;
RETURN L_CR;
END;
Procedure Executed Query :
VARIABLE RC REFCURSOR;
EXECUTE DEPARTMENT_PKG.GET_DEPARTMENT_LIST('DEPARTMENT_NAME', :RC);
PRINT RC;
Result :

Sure you can. For example:
SQL> create or replace function get_list (par_order_by in varchar2)
2 return sys_refcursor
3 is
4 l_rc sys_refcursor;
5 begin
6 open l_rc for
7 'select empno, ename, job, sal from emp where deptno = 10 order by ' || par_order_by;
8 return l_rc;
9 end;
10 /
Function created.
Testing:
SQL> select get_list('ename') result from dual;
RESULT
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
EMPNO ENAME JOB SAL
---------- ---------- --------- ----------
7782 CLARK MANAGER 2450
7839 KING PRESIDENT 5000
7934 MILLER CLERK 1300
SQL> select get_list('job, sal') result from dual;
RESULT
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
EMPNO ENAME JOB SAL
---------- ---------- --------- ----------
7934 MILLER CLERK 1300
7782 CLARK MANAGER 2450
7839 KING PRESIDENT 5000
SQL>
As you noticed, I created a function - for simplicity - instead of a procedure with an OUT parameter. If you have to use a procedure, no problem; it is the ORDER BY you had problem with, not the way you'll return the result to the caller.
[EDIT: procedure that calls a function]
SQL> CREATE OR REPLACE PROCEDURE get_dept_list (
2 order_by_param IN VARCHAR2,
3 dept_data OUT SYS_REFCURSOR)
4 IS
5 BEGIN
6 OPEN dept_data FOR SELECT get_list (order_by_param) FROM DUAL;
7 END get_dept_list;
8 /
Procedure created.
SQL> CREATE OR REPLACE FUNCTION get_list (par_order_by IN VARCHAR2)
2 RETURN SYS_REFCURSOR
3 IS
4 l_rc SYS_REFCURSOR;
5 BEGIN
6 OPEN l_rc FOR
7 'select empno, ename, job, sal from emp where deptno = 10 order by '
8 || par_order_by;
9
10
11 RETURN l_rc;
12 END;
13 /
Function created.
Sorted by ENAME:
SQL> var rc refcursor
SQL> exec get_dept_list('ename', :rc)
PL/SQL procedure successfully completed.
SQL> print rc
GET_LIST(:B1)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
EMPNO ENAME JOB SAL
---------- ---------- --------- ----------
7782 CLARK MANAGER 2450
7839 KING PRESIDENT 5000
7934 MILLER CLERK 1300
Sorted by JOB:
SQL> exec get_dept_list('job', :rc)
PL/SQL procedure successfully completed.
SQL> print rc
GET_LIST(:B1)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
EMPNO ENAME JOB SAL
---------- ---------- --------- ----------
7934 MILLER CLERK 1300
7782 CLARK MANAGER 2450
7839 KING PRESIDENT 5000
Sorted by SAL in descending order:
SQL> exec get_dept_list('sal desc', :rc)
PL/SQL procedure successfully completed.
SQL> print rc
GET_LIST(:B1)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
EMPNO ENAME JOB SAL
---------- ---------- --------- ----------
7839 KING PRESIDENT 5000
7782 CLARK MANAGER 2450
7934 MILLER CLERK 1300
SQL>

Related

How to create an oracle plsql function that will return multiple rows for a specific parameter (ex: employee id)

I need to create a function that will be called on oracle screen.
I just don't know how to do it
A simple option is function that returns refcursor.
This is example based on Scott's sample emp table; function accepts department number as a parameter and returns data related to employees working in that department.
SQL> create or replace function f_test (par_deptno in emp.deptno%type)
2 return sys_refcursor
3 is
4 l_rc sys_refcursor;
5 begin
6 open l_rc for
7 select deptno, empno, ename, job, sal
8 from emp
9 where deptno = par_deptno;
10 return l_rc;
11 end;
12 /
Function created.
SQL> select f_test(10) from dual;
F_TEST(10)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
DEPTNO EMPNO ENAME JOB SAL
---------- ---------- ---------- --------- ----------
10 7782 CLARK MANAGER 2572.5
10 7839 KING PRESIDENT 5250
10 7934 MILLER CLERK 1365
SQL>

Simple Stored Procedure for learning

My journey is progressing and I'm learning rapidly. I can't get enough of this stuff... alas I am at a dead end here and need some help.
I am running Visual Studio, am connected to a database (that's filled with dummy data). I am able to run queries on it as expected. However, I'm learning about Procedures right now and I'm coming up with a problem.
I am trying to simply run a query that will select all from table.
CREATE PROCEDURE nearthetop()
BEGIN
SELECT * FROM RESULTS WHERE VOLUME = (SELECT MAX(VOLUME) FROM RESULTS WHERE VOLUME NOT In (SELECT Max(VOLUME) from RESULTS))
END;
When I run this inside Visual Studio I get an error:
EXECUTE FAIL:
CREATE PROCEDURE twofromtop() BEGIN SELECT * FROM RESULTS WHERE VOLUME = (SELECT MAX(VOLUME) FROM RESULTS WHERE VOLUME NOT In (SELECT Max(VOLUME) from RESULTS)) END
Message :
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'END' at line 3
When I remove BEGIN and END; from the procedure it creates it fine.
Why is that?
And then, once created, how do I "call" that procedure so as I can see the returned results?
In Oracle, when PL/SQL code uses a SELECT statement, you have to select into something - a local variable, a collection, whatever - or use a cursor loop and deal with cursor variable.
You're selecting row(s) whose volume value is the 2nd largest. Code you wrote works, but it fetches from the same table 3 times which isn't optimal. For example (based on Scott's sample schema), fetching the 2nd largest salary:
SQL> select ename, sal from emp order by sal desc;
ENAME SAL
---------- ----------
KING 5000 --> largest
FORD 3000 --> Ford and Scott both share
SCOTT 3000 --> the 2nd largest salary
JONES 2975
BLAKE 2850
CLARK 2450
<snip>
Your query returns correct result:
SQL> select *
2 from emp
3 where sal = (select max(sal) from emp
4 where sal not in (select max(sal) from emp)
5 );
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7788 SCOTT ANALYST 7566 09.12.82 3000 20
7902 FORD ANALYST 7566 03.12.81 3000 20
Consider doing it differently:
SQL> select *
2 from (select e.*,
3 rank() over (order by sal desc) rnk
4 from emp e
5 )
6 where rnk = 2;
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO RNK
---------- ---------- --------- ---------- -------- ---------- ---------- ---------- ----------
7788 SCOTT ANALYST 7566 09.12.82 3000 20 2
7902 FORD ANALYST 7566 03.12.81 3000 20 2
SQL>
Now, back to your procedure. A simple option which doesn't require much effort is to use a cursor FOR loop. Procedure's IN parameter says which largest salary you want:
SQL> create or replace procedure nearthetop (par_n in number) as
2 begin
3 for cur_r in (select *
4 from (select e.*,
5 rank() over (order by sal desc) rnk
6 from emp e
7 )
8 where rnk = par_n
9 )
10 loop
11 dbms_output.put_line(cur_r.ename ||': '|| cur_r.sal);
12 end loop;
13 end;
14 /
Procedure created.
Testing:
SQL> set serveroutput on
SQL> begin
2 nearthetop(2); --> give me the 2nd largest salary
3 end;
4 /
SCOTT: 3000
FORD: 3000
PL/SQL procedure successfully completed.
SQL> begin
2 nearthetop(5); --> give me the 5th largest salary
3 end;
4 /
BLAKE: 2850
PL/SQL procedure successfully completed.
SQL>
I'm just displaying those values on the screen; you never said what you'd want to do with them.
If you'd like to return the result to the caller, a better option is to use a function instead of a procedure. In this case, it would be a ref cursor it returns.
SQL> create or replace function f_nearthetop (par_n in number)
2 return sys_refcursor
3 as
4 rc sys_refcursor;
5 begin
6 open rc for select *
7 from (select e.*,
8 rank() over (order by sal desc) rnk
9 from emp e
10 )
11 where rnk = par_n;
12 return rc;
13 end;
14 /
Function created.
Testing:
SQL> var l_rc refcursor
SQL>
SQL> exec :l_rc := f_nearthetop(2);
PL/SQL procedure successfully completed.
SQL> print :l_rc
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO RNK
---------- ---------- --------- ---------- -------- ---------- ---------- ---------- ----------
7788 SCOTT ANALYST 7566 09.12.82 3000 20 2
7902 FORD ANALYST 7566 03.12.81 3000 20 2
SQL>
So, yes - there are various options. Which one you'll actually use depends on what you want to do.
(As of Visual Studio: I can't help about it, I don't use it.)

PL/SQL Procedure/function with params to return a table from(of) a select query

how can a (Oracle)function/Procedure with parameters can be created to return a table, which was returned by a select statement, inside a block
tried with refcursor
getting data as below ,all in a single row
{<G_ID=748,P_G_ID=746,P_NO=null,M_DATE=06-OCT-20 >,
<G_ID=749,P_G_ID=746,P_NO=null,M_DATE=06-OCT-20>} instead of a table format
image link for the sample output
P.S. I am pretty new to PL/SQL
One option is to return refcursor. Here's an example:
SQL> create or replace function f_test (par_deptno in number)
2 return sys_refcursor
3 is
4 rc sys_refcursor;
5 begin
6 open rc for select * from emp where deptno = par_deptno;
7 return rc;
8 end;
9 /
Function created.
SQL> select f_test(10) from dual;
F_TEST(10)
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- -------- ---------- ---------- ----------
7782 CLARK MANAGER 7839 09.06.81 2450 10
7839 KING PRESIDENT 17.11.81 10000 10
7934 MILLER CLERK 7782 23.01.82 1300 10
SQL>

Not able to convert this SQL query to PL/SQL

The question is to retrieve all the students name who have scored more than average marks. So, As i am familiar in SQL i have came up with this query:
SELECT Student_Name
FROM Student
WHERE Mark >
( SELECT AVG(Mark)
FROM Student
);
But i need this query to be in PL/SQL format. Please help me.
It depends on what you want to do, once you convert it to PL/SQL. In PL/SQL, you have to fetch the result into something. For example (based on Scott's schema):
SQL> select ename
2 from emp
3 where sal > (select avg(sal) from emp);
ENAME
----------
JONES
BLAKE
CLARK
SCOTT
KING
FORD
6 rows selected.
You could select into an array:
SQL> set serveroutput on
SQL> declare
2 l_tab sys.odcivarchar2list;
3 begin
4 select ename
5 bulk collect into l_tab
6 from emp
7 where sal > (select avg(sal) from emp);
8
9 for i in l_tab.first .. l_tab.last loop
10 dbms_output.put_line(l_tab(i));
11 end loop;
12 end;
13 /
JONES
BLAKE
CLARK
SCOTT
KING
FORD
PL/SQL procedure successfully completed.
SQL>
Or, you can process those rows directly in a loop:
SQL> begin
2 for cur_e in (select ename
3 from emp
4 where sal > (select avg(sal) from emp))
5 loop
6 dbms_output.put_line(cur_e.ename);
7 end loop;
8 end;
9 /
JONES
BLAKE
CLARK
SCOTT
KING
FORD
PL/SQL procedure successfully completed.
SQL>
Or, you can make a function out of it and return refcursor:
SQL> create or replace function f_test
2 return sys_refcursor
3 is
4 rc sys_refcursor;
5 begin
6 open rc for select ename
7 from emp
8 where sal > (select avg(sal) from emp);
9 return rc;
10 end;
11 /
Function created.
SQL> select f_test from dual;
F_TEST
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
ENAME
----------
JONES
BLAKE
CLARK
SCOTT
KING
FORD
6 rows selected.
SQL>
There might be other options as well. Basically, it just depends on the requirement.

Oracle: wrong number or types of arguments in call

create procedure employee_get
(
c_Emp out SYS_REFCURSOR
)
is
begin
open c_Emp for
select a.last_name, b.department_id, b.department_name from employee a inner join department b on a.department_id = b.department_id
order by a.last_name asc ;
end employee_get;
-- run
var c_out refcursor;
exec employee_get(:c_out);
print :c_out;
Everything was fine until I made the Run command. I'm new member, please help me. Thanks and respect!
You need following code to execute the procedure:
create procedure employee_get
(
c_Emp out SYS_REFCURSOR
)
is
begin
open c_Emp for
select a.last_name, b.department_id, b.department_name from employee a inner join department b on a.department_id = b.department_id
order by a.last_name asc ;
end employee_get;
/
-- EXECUTE THE PROCEDURE
DECLARE
C_OUT SYS_REFCURSOR;
BEGIN
-- run
employee_get(C_OUT);
dbms_sql.return_result(C_OUT); --UPDATED
END;
/
Cheers!!
The procedure which returns refcursor:
SQL> create or replace procedure employee_get (c_emp out sys_refcursor) is
2 begin
3 open c_emp for select empno, ename from emp;
4 end;
5 /
Procedure created.
Execution:
SQL> declare
2 c_out sys_refcursor;
3 begin
4 employee_get(c_out);
5 dmbs_sql.return_result(c_out);
6 end;
7 /
dmbs_sql.return_result(c_out);
*
ERROR at line 5:
ORA-06550: line 5, column 3:
PLS-00201: identifier 'DMBS_SQL.RETURN_RESULT' must be declared
ORA-06550: line 5, column 3:
PL/SQL: Statement ignored
See with your DBA whether you can get execute privileges on DBMS_SQL package.
If not:
SQL> var c_out refcursor;
SQL> exec employee_get(:c_out);
PL/SQL procedure successfully completed.
SQL> print :c_out
EMPNO ENAME
---------- ----------
7369 SMITH
7499 ALLEN
7521 WARD
7566 JONES
7654 MARTIN
7698 BLAKE
7782 CLARK
7788 SCOTT
7839 KING
7844 TURNER
7876 ADAMS
7900 JAMES
7902 FORD
7934 MILLER
14 rows selected.
SQL>

Resources