Using define in simple sql statement - sql

Is there a way of defining a section of SQL as a constant for repeated use in a simple oracle SQL statement. In a similar way that can be done in a script and in a similar way to the WITH statement?
Something like
Define meat as "foodtype<>'fruit' and foodtype<>'veg'"
select * from meals where meat

In SQL*Plus you could use DEFINE.
For example,
SQL> define l_str = 'ename = ''SCOTT'''
SQL>
SQL> select empno, deptno from emp where &l_str;
old 1: select empno, deptno from emp where &l_str
new 1: select empno, deptno from emp where ename = 'SCOTT'
EMPNO DEPTNO
---------- ----------
7788 20
SQL>
NOTE DEFINE is a SQL*Plus command. Define sets a user variable or displays its value.
However, not possible in plain SQL as the query won't parse and would throw an error. Since, during parse time, Oracle would expect it to be static, you cannot have it dynamic.
You can use Dynamic SQL in PL/SQL, such that you could have it as a string variable and use it repeatedly in the scope of the session.
For example,
SQL> DECLARE
2 var_str VARCHAR2(1000);
3 var_str1 VARCHAR2(1000);
4 v_empno emp.empno%type;
5 BEGIN
6 var_str := 'ename = ''SCOTT''';
7 var_str1 := 'select empno FROM emp WHERE '|| var_str;
8 EXECUTE IMMEDIATE var_str1 INTO v_empno;
9
10 dbms_output.put_line(v_empno);
11
12 END;
13 /
7788
PL/SQL procedure successfully completed.
SQL>

As per your question it seems that the condition should be dynamic. It can be acheived in PLSQL Block like below
DECLARE
v1 varchar2(100) := ' foodtype <> ''fruit'' and foodtype <> ''Veg''';
mealstyp meals%ROWTYPE;
BEGIN
EXECUTE IMMEDIATE 'select * from meals where'||v1 into mealstyp;
LOOP
--your type record
END LOOP;
END

Related

Assign to variables values from rowtype parameter

I've searched on stackoverflow and other sources, but haven't found anything useful.
I have to implement a procedure that receives a row (hence "%rowtype") from Reservation table as a parameter.
The table's name is Reservation (as attributes: id_Reservation, id_Room, price, etc).
I created an anonymous block to create the var_reservation variable, which will serve as the parameter to the procedure:
CREATE OR REPLACE PROCEDURE priceCheckout(var_reservation reservation%ROWTYPE) IS
l_idReservation NUMBER;
l_idRoom NUMBER;
prie INT;
date1 DATE;
date2 DATE;
ex exception;
BEGIN
l_idReservation := var_reservation.id_reservation;
(...)
END;
But this is wrong. How can I correct this?
If you do it correctly, it works. Have a look at the following example based on Scott's DEPT table:
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------------- --------------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL> set serveroutput on
Procedure:
SQL> create or replace procedure p_test (var_row dept%rowtype)
2 is
3 l_dname dept.dname%type;
4 begin
5 l_dname := var_row.dname;
6
7 dbms_output.put_line('Department name = ' || l_dname);
8 end;
9 /
Procedure created.
Testing:
SQL> declare
2 l_row dept%rowtype;
3 begin
4 select *
5 into l_row
6 from dept
7 where deptno = 10;
8
9 p_test(l_row);
10 end;
11 /
Department name = ACCOUNTING
PL/SQL procedure successfully completed.
SQL>
Saying that "this is wrong" is difficult to debug. It is not an Oracle error message so ... unless you specify what is "this" and, possibly, edit the question and post copy/paste of your own SQL*Plus session (like I did) so that we'd see what you did and how Oracle responded, YOYO.

Performance Tuning In Plsql Function Query

I have plsql function. And this function has query like as below:
select colum_name
from table_name
where filter_coloumn_name in ( select filter_coloumn_name from table_name_2);
My function is used by other query to return value from query. This query which use this function take more time. Even the site (front screen) down. Because table_name_2 has three million record. So that i must use some performance tuning method in my function. I change my function query like as below:
cursor my_cursor IS
select filter_coloumn_name from table_name_2;
TYPE cursor_array_type IS TABLE OF my_cursor%ROWTYPE INDEX BY BINARY_INTEGER;
m cursor_array_type;
TYPE cursor_table_type IS TABLE OF VARCHAR2(20) INDEX BY BINARY_INTEGER;
cursor_table_object cursor_table_type;
fetch_size NUMBER := 5000;
index_var number;
begin
index_var := 1;
open my_cursor;
loop
FETCH my_cursor BULK COLLECT
into m LIMIT fetch_size;
exit when my_cursor %notfound;
for i in 1 .. m.count loop
cursor_table_object (index_var) := m(i).filter_coloumn_name;
index_var := index_var + 1;
end loop;
end loop;
Close my_cursor;
select colum_name
from table_name
where filter_coloumn_name in (cursor_table_object);
select colum_name
from table_name
where filter_coloumn_name in (cursor_table_object);
Namely, i want fetch all values at one time, and then i want use this table object like as above. But i can't use table object with in condition expression in sql query.
I take PLS-00382: expression is of wrong type error.
I want to use this table object like as below:
select colum_name
from table_name
where filter_coloumn_name in ('bla', 'bla bla', 'bla bla bla');
Should i convert table object to the array ?
A suggestion, based on what you've said so far: how about using a table function? It makes it possible to use something like statement you wanted to use, i.e.
select colum_name from table_name where filter_coloumn_name in (cursor_table_object);
but which returns
PLS-00382: expression is of wrong type
Here's an example based on Scott's schema. Have a look:
SQL> create or replace function f_test
2 return sys.odcinumberlist
3 is
4 -- Cursor's SELECT should contain your 3-million-rows table
5 cursor cur_r is select deptno from dept where deptno < 30;
6 l_ret sys.odcinumberlist;
7 begin
8 open cur_r;
9 fetch cur_r bulk collect into l_ret;
10 close cur_r;
11 return l_ret;
12 end;
13 /
Function created.
SQL> -- How to use it?
SQL> select e.ename
2 from emp e join table(f_test) x on x.column_value = e.deptno;
ENAME
----------
MILLER
KING
CLARK
ADAMS
SCOTT
FORD
JONES
SMITH
8 rows selected.
SQL>
You must use exists instead of in, also table_name_2 must have index over filter_coloumn_name field.
Using cursor only what you get is full access to a big table.

Oracle - Concatenate Strings as column name

I am trying to concatenate strings that are used as column names
I want to do something like:
Select someData as "ONE" || :someVariable) from sometable;
where someVariable is a bind variable, which does not work inside double quotes.
(The column should have the name "ONE2018" if someVariable = 2018.)
I tried it with single quotes and with the concat function. It doesn't work.
Is there some way to accomplish this?
EDIT:
With inspiration from littlefoots answer I tried
declare
customVariable number(4);
rc sys_refcursor;
begin
open rc for 'select 1 as bla' || :customVariable || ' from dual';
dbms_sql.return_result(rc);
end;
/
which does have the output
BLA2018
----------
1
I don't know how to put that into a PreparedStatement, but if used on it's own it works and might help someone else
An example based on Scott's EMP table which contains columns whose names begins with an E: ENAME and EMPNO.
You'd pass NAME or MPNO and get the result.
SQL> create or replace function f_one (par_column_name in varchar2)
2 return sys_refcursor
3 is
4 l_rc sys_refcursor;
5 l_str varchar2(200);
6 begin
7 l_str := 'select e' || par_column_name || ' from emp where rownum < 3';
8 open l_rc for l_str;
9 return l_rc;
10 end;
11 /
Function created.
SQL> select f_one('mpno') from dual;
F_ONE('MPNO')
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
EMPNO
----------
7369
7499
SQL> select f_one('name') from dual;
F_ONE('NAME')
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
ENAME
----------
SMITH
ALLEN
SQL>

can a procedure in oracle have ordinary select * statement inside begin and end block?

create procedure
return(val_id int)
is
begin
select *
from emp
where emp_id = val_id;
end
No, In Oracle you can't run ordinary SELECT statement, it's not supported.
You can do this in many other RDBMS, for example in MySql this works in this way:
A.4.14. Can MySQL 5.5 stored routines return result sets?
Stored procedures can, but stored functions cannot. If you perform an
ordinary SELECT inside a stored procedure, the result set is returned
directly to the client. You need to use the MySQL 4.1 (or higher)
client/server protocol for this to work. This means that, for example,
in PHP, you need to use the mysqli extension rather than the old mysql
extension.
In other words, if you run ordinary SELECT statement within a procedure, then MySql automatically creates a resultset which is then passed to the caller client.
In Oracle it is not done in automatic manner - you must explicitely declare a cursor and pass it as OUT parameter to the caller, for example:
CREATE OR REPLACE PROCEDURE test22( val_id int, cur OUT sys_refcursor )
IS
BEGIN
open cur for select EMPNO, ENAME
from emp
where empno = val_id;
END;
/
And the caller must retrieve the cursor from the parameter and handle it in explicit way, there is no any "implicit automation", for example (in SQL-Plus or SQL-Developer):
var ppp refcursor
exec test22( 7654, :ppp );
print ppp
EMPNO ENAME
---------- ----------
7654 MARTIN
But beginning from Oracle 12.1c there is Implicit Statement Results feature available which make it possible to simulate this behaviour:
Implicit Statement Results
Before Oracle Database 12c, a PL/SQL stored
subprogram returned result sets from SQL queries explicitly, through
OUT REF CURSOR parameters, and the client program that invoked the
subprogram had to bind to those parameters explicitly to receive the
result sets.
As of Oracle Database 12c, a PL/SQL stored subprogram can return query
results to its client implicitly, using the PL/SQL package DBMS_SQL
instead of OUT REF CURSOR parameters. This technique makes it easy to
migrate applications that rely on the implicit return of query results
from stored subprograms from third-party databases to Oracle Database.
For more information, see "DBMS_SQL.RETURN_RESULT Procedure" and
"DBMS_SQL.GET_NEXT_RESULT Procedure".
On Oracle 12c you can just do:
CREATE OR REPLACE PROCEDURE test22( val_id int )
IS
cur sys_refcursor;
BEGIN
open cur for select EMPNO, ENAME
from emp
where empno = val_id;
DBMS_SQL.RETURN_RESULT( cur, true );
END;
/
and in the client (SQL/Plus, SQL-Developer, in Java, in C# etc) you can just call this procedure, and the resultset will be automatically retrieved:
exec test22( 7654 );
PL/SQL procedure successfully completed.
ResultSet #1
EMPNO ENAME
---------- ----------
7654 MARTIN
You can even return multiple resultsets to the client, an example:
CREATE OR REPLACE PROCEDURE test22( val_id int )
IS
cur sys_refcursor;
cur2 sys_refcursor;
cur3 sys_refcursor;
BEGIN
open cur for select EMPNO, ENAME
from emp
where empno = val_id;
DBMS_SQL.RETURN_RESULT( cur, true );
open cur2 for select ENAME, sal, deptno
from emp
where empno = val_id;
DBMS_SQL.RETURN_RESULT( cur2, true );
open cur3 for select ENAME, mgr, sal, deptno, hiredate
from emp
where empno = val_id;
DBMS_SQL.RETURN_RESULT( cur3, true );
END;
/
exec test22( 7654 );
PL/SQL procedure successfully completed.
ResultSet #1
EMPNO ENAME
---------- ----------
7654 MARTIN
ResultSet #2
ENAME SAL DEPTNO
---------- ---------- ----------
MARTIN 1250 30
ResultSet #3
ENAME MGR SAL DEPTNO HIREDATE
---------- ---------- ---------- ---------- ----------------
MARTIN 7698 1250 30 1981/09/28 00:00

Can an SQL procedure return a table?

Can an Oracle SQL procedure return a table? I'm currently using a dbms_output to print out the outputs of two cursors which are in a loop, although this would look nicer if it was returning two columns instead. Would that be possible within a procedure?
A PL/SQL function can return a nested table. Provided we declare the nested table as a SQL type we can use it as the source of a query, using the the TABLE() function.
Here is a type, and a nested table built from it:
SQL> create or replace type emp_dets as object (
2 empno number,
3 ename varchar2(30),
4 job varchar2(20));
5 /
Type created.
SQL> create or replace type emp_dets_nt as table of emp_dets;
2 /
Type created.
SQL>
Here is a function which returns that nested table ...
create or replace function get_emp_dets (p_dno in emp.deptno%type)
return emp_dets_nt
is
return_value emp_dets_nt;
begin
select emp_dets(empno, ename, job)
bulk collect into return_value
from emp
where deptno = p_dno;
return return_value;
end;
/
... and this is how it works:
SQL> select *
2 from table(get_emp_dets(10))
3 /
EMPNO ENAME JOB
---------- ------------------------------ --------------------
7782 CLARK MANAGER
7839 KING PRESIDENT
7934 MILLER CLERK
SQL>
SQL Types offer us a great deal of functionality, and allow us to build quite sophisticated APIs in PL/SQL. Find out more.
I think that you can use Oracle Cursor for this (if your Oracle version supports it):
PROCEDURE myprocedure(
mycursor OUT SYS_REFCURSOR )
AS
BEGIN
OPEN mycursor FOR SELECT * FROM mytable;
END;
END;
This may also help:
DECLARE
TYPE t_emptbl IS TABLE OF scott.emp%rowtype;
v_emptbl t_emptbl;
ret_val t_emptbl;
--
Function getEmployeeList Return t_emptbl
IS
BEGIN
SELECT * bulk collect INTO v_emptbl FROM scott.emp;
-- Print nested table of records:
FOR i IN 1 .. v_emptbl.COUNT LOOP
DBMS_OUTPUT.PUT_LINE (v_emptbl(i).empno);
END LOOP;
RETURN v_emptbl;
END;
--
BEGIN
ret_val:= getEmployeeList;
END;
/