Oracle - How to execute a query with parameters? - oracle

I am new to Oracle and i am trying to execute a simple select with some parameters but i cant get it to work.
For
SELECT idl.column_value clientguid
FROM TableName idl
LEFT JOIN :ParamName_Type olt ON olt.clientguid = idl.column_value
WHERE (olt.flag = 0)
But declare does not work. I could not find any help on internet.
Thanks.

Oracle SQL Developer should handle variables the same way SQLPlus does, that is with the &.
For example ( in SQLPlus for simplicity):
SQL> select 1 from &tableName;
Enter value for tablename: dual
old 1: select 1 from &tableName
new 1: select 1 from dual
1
----------
1
What you can not do is use the parameter as a part of a table name, assuming that Developer "knows" which part is the parameter name and which one is the fixed part.
For example:
SQL> select * from &ParamName_Type;
Enter value for paramname_type:
that is, all the string ParamName_Type wil be interpreted as a variable name and substituited with the value you enter.
Also, consider that this is a client-specific behaviour, not an Oracle DB one; so, the same thing will not work in a different client (Toad for Oracle for example).
Consider that you are trying to use a "parameter" that represents a table name, and you only can do this by the means of some client, because plain SQL does not allow it.
If you need to do such a thing in some piece of code that has to work no matter the client, you need dynamic SQL
If you need something more complex, you may need some dynamic SQL; for example:
SQL> declare
2 vTableName varchar2(30) := '&table_name';
3 vSQL varchar2(100):= 'select 1 from ' || vTableName ||
' union all select 2 from ' || vTableName;
4 type tResult is table of number;
5 vResult tResult;
6 begin
7 execute immediate vSQL bulk collect into vResult;
8 --
9 -- do what you need with the result
10 --
11 for i in vResult.first .. vResult.last loop
12 dbms_output.put_line(vResult(i));
13 end loop;
14 end;
15 /
Enter value for table_name: dual
old 2: vTableName varchar2(30) := '&table_name';
new 2: vTableName varchar2(30) := 'dual';
1
2
PL/SQL procedure successfully completed.
SQL>

Related

Using cursor with where in condition

I am passing arguments `EBN,BGE' into a procedure , then I am passing this argument to a cursor.
create or replace procedure TEXT_MD (AS_IDS VARCHAR2)
is
CURSOR C_A (AS_ID VARCHAR2) IS
SELECT
name
FROM S_US
WHERE US_ID IN (AS_ID);
BEGIN
FOR A IN C_A (AS_IDS) LOOP
DBMS_OUTPUT.PUT_LINE('I got here: '||AS_IDS);
end loop;
END;
But while debuging the count of the cursor is still null
So my question , why the cursor not returning values with in condition
You are passing a string parameter, so it will be used as a string, not as a list of strings; so, your cursor will be something like
SELECT name
FROM S_US
WHERE US_ID IN ('EBN,BGE')
This will, of course, not do what you need.
You may need to change your procedure and the way to pass parameters; if you want to keep a string parameter , one way could be the following:
setup:
SQL> CREATE TABLE S_US
2 (
3 US_ID,
4 NAME
5 ) AS
6 SELECT 'EBN', 'EBN name' FROM DUAL
7 UNION ALL
8 SELECT 'BGE', 'BGE name' FROM DUAL;
Table created.
procedure:
SQL> CREATE OR REPLACE PROCEDURE TEXT_MD_2(AS_IDS VARCHAR2) IS
2 vSQL varchar2(1000);
3 c sys_refcursor;
4 vName varchar2(16);
5 BEGIN
6 vSQL := 'SELECT name
7 FROM S_US
8 WHERE US_ID IN (' || AS_IDS || ')';
9 open c for vSQL;
10 loop
11 fetch c into vName;
12 if c%NOTFOUND then
13 exit;
14 end if;
15 DBMS_OUTPUT.PUT_LINE(vName);
16 END LOOP;
17 END;
18 /
Procedure created.
You need to call it with a string already formatted to be a parameter list for IN:
SQL> EXEC TEXT_MD_2('''EBN'',''BGE''');
EBN name
BGE name
PL/SQL procedure successfully completed.
This is only an example of a possible way, and not the way I would do this.
Among the reasons to avoud this kind of approach, consider what Justin Cave says:
"that would be a security risk due to SQL injection and would have a potentially significant performance penalty due to constant hard parsing".
I believe you should better check how to pass a list of values to your procedure, rather then using a string to represent a list of strings.
Here is a possible way to do the same thing with a collection:
SQL> CREATE OR REPLACE TYPE tabVarchar2 AS TABLE OF VARCHAR2(16)
2 /
Type created.
SQL>
SQL> CREATE OR REPLACE PROCEDURE TEXT_MD_3(AS_IDS tabVarchar2) IS
2 vSQL VARCHAR2(1000);
3 c SYS_REFCURSOR;
4 vName VARCHAR2(16);
5 BEGIN
6 FOR i IN (SELECT name
7 FROM S_US INNER JOIN TABLE(AS_IDS) tab ON (tab.COLUMN_VALUE = US_ID))
8 LOOP
9 DBMS_OUTPUT.PUT_LINE(i.name);
10 END LOOP;
11 END;
12 /
Procedure created.
SQL>
SQL> DECLARE
2 vList tabVarchar2 := NEW tabVarchar2();
3 BEGIN
4 vList.EXTEND(2);
5 vList(1) := 'BGE';
6 vList(2) := 'EBN';
7 TEXT_MD_3(vList);
8 END;
9 /
BGE name
EBN name
PL/SQL procedure successfully completed.
SQL>
Again, you can define collections in different ways, within a stored procedure or not, indexed or not, and so on; this is only one of the possible ways, not necessarily the best, depending on your environment, needs.

Oracle PL/SQL - Show results of declared table

I am using Toad. I have a declaration of a table in a package as follows:
TYPE MyRecordType IS RECORD
(ID MyTable.ID%TYPE
,FIELD1 MyTable.FIELD1%TYPE
,FIELD2 MyTable.FIELD2%TYPE
,FIELD3 MyTable.FIELD3%TYPE
,ANOTHERFIELD VARCHAR2(80)
);
TYPE MyTableType IS TABLE OF MyRecordType INDEX BY BINARY_INTEGER;
There is a procedure (lets say MyProcedure), that is using an object of this table type as input/output. I want to run the procedure and see the results (how the table is filled). So I am thinking I will select the results from the table:
declare
IO_table MyPackage.MyTableType;
begin
MyPackage.MyProcedure (IO_table
,parameter1
,parameter2
,parameter3);
select * from IO_table;
end;
I get the message:
Table or view does not exist (for IO_table). If I remove the select line, the procedure runs successfully, but I cannot see its results. How can I see the contents of IO_table after I call the procedure?
You cannot see the results for a PL/SQL table by using Select * from IO_table
You will need to loop through the collection in the annonymous block.
do something like, given in pseudo code below...
declare
IO_table MyPackage.MyTableType;
l_index BINARY_INTEGER;
begin
MyPackage.MyProcedure (IO_table
,parameter1
,parameter2
,parameter3);
l_index := IO_table.first;
While l_index is not null
loop
dbms_output.put_line (IO_table(l_index).id);
.
.
.
.
l_index :=IO_table.next(l_index_id);
end loop;
end;
You have to do it like this:
select * from TABLE(IO_table);
and, of course you missed the INTO or BULK COLLECT INTO clause
1) You can not use associated arrays in SELECT statement, Just nested tables or varrays declared globally.
2) You should use TABLE() expression in SELECT statement
3) You can't simply use SELECT in PL/SQL code - cursor FOR LOOP or REF CURSOR or BULK COLLECT INTO or INTO must be used.
4) The last but not least - please study the manual:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28371/adobjcol.htm#ADOBJ00204
Just an example:
SQL> create type t_obj as object( id int, name varchar2(10));
2 /
SQL> create type t_obj_tab as table of t_obj;
2 /
SQL> var rc refcursor
SQL> declare
2 t_var t_obj_tab := t_obj_tab();
3 begin
4 t_var.extend(2);
5 t_var(1) := t_obj(1,'A');
6 t_var(2) := t_obj(2,'B');
7 open :rc for select * from table(t_var);
8 end;
9 /
SQL> print rc
ID NAME
---------- ----------
1 A
2 B

Find out if a collection was populated by bulk collect

I created an oracle Object Type like this:
CREATE OR REPLACE TYPE DFBOWNER."RPT_WIRE_IMPORT_ROWTYPE" AS OBJECT
(
REC_VALUE_DATE DATE
)
/
And then a collection based on this type:
CREATE OR REPLACE TYPE DFBOWNER."RPT_WIRE_IMPORT_TABLETYPE" IS TABLE OF RPT_WIRE_IMPORT_RowType;
/
Now I populate the collection using oracle bulk collect into syntax inside a procedure.
So now i want to test if the collection actually got populated, and i am not sure how to do it.
I tried looking it up:
http://docs.oracle.com/cd/B28359_01/appdev.111/b28371/adobjcol.htm#autoId17 but I am not able to find what I need.
I also have another question. When the procedure bulk collects data into collections, does the data in the collection become permanent as in a table? Or is it semi-permanent...i.e. only lives for the session...as in a temp table.
I suspect you are looking for the COUNT method, i.e.
DECLARE
l_local_collection dbfowner.rpt_wire_import_tabletype;
BEGIN
SELECT sysdate + level
BULK COLLECT INTO l_local_collection
FROM dual
CONNECT BY level <= 10;
dbms_output.put_line( 'l_local_collection contains ' ||
l_local_collection.count ||
' elements.' );
END;
Like any local variable, l_local_collection will have the scope of the block in which it is declared. The data is stored in the PGA for the session. The data in a collection is not permanent.
You can select from the local collection
SQL> create type some_object as object (
2 rec_value_date date
3 );
4 /
Type created.
SQL> create type some_coll
2 as table of some_object;
3 /
Type created.
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_local_collection some_coll;
3 begin
4 select some_object( sysdate + numtodsinterval( level, 'day' ) )
5 bulk collect into l_local_collection
6 from dual
7 connect by level <= 10;
8 for x in (select * from table( l_local_collection ))
9 loop
10 dbms_output.put_line( x.rec_value_date );
11 end loop;
12* end;
SQL> /
20-AUG-12
21-AUG-12
22-AUG-12
23-AUG-12
24-AUG-12
25-AUG-12
26-AUG-12
27-AUG-12
28-AUG-12
29-AUG-12
PL/SQL procedure successfully completed.
but it generally doesn't make sense to go through the effort of pulling all the data from the SQL VM into the PL/SQL VM only to then pass all of the data back to the SQL VM in order to issue the SELECT statement. It would generally make more sense to just keep the data in SQL or to define a pipelined table function to return the data.
If you merely want to iterate over the elements in the collection
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_local_collection some_coll;
3 begin
4 select some_object( sysdate + numtodsinterval( level, 'day' ) )
5 bulk collect into l_local_collection
6 from dual
7 connect by level <= 10;
8 for i in 1 .. l_local_collection.count
9 loop
10 dbms_output.put_line( l_local_collection(i).rec_value_date );
11 end loop;
12* end;
SQL> /
20-AUG-12
21-AUG-12
22-AUG-12
23-AUG-12
24-AUG-12
25-AUG-12
26-AUG-12
27-AUG-12
28-AUG-12
29-AUG-12
PL/SQL procedure successfully completed.
It would make much more sense to iterate over the elements in the collection, which keeps everything in PL/SQL, than to SELECT from the collection, which forces all the data back into the SQL VM.

How to use parameters in a 'where value in...' clause?

This works when I have only one state code as a parameter.
How can I get code to work when I have more than one state_code in parm_list?
Requirements:
(1)I don't want to hard code the state codes in my cursor definition
(2) I do want to allow for more than one state code in my where clause
For example: I want to run this code for parm_list = ('NY','NJ','NC').
I'm encountering difficulties in reconciling single quotes in parm_list with the single quotes in the 'where state_code in ' query.
set serveroutput on;
DECLARE
parm_list varchar2(40);
cursor get_state_codes(in_state_codes varchar2)
is
select state_name, state_code from states
where state_code in (in_state_codes);
BEGIN
parm_list := 'NY';
for get_record in get_state_codes(parm_list) loop
dbms_output.put_line(get_record.state_name || get_record.state_code);
end loop;
END;
Using dynamic SQL is the simplest approach from a coding standpoint. The problem with dynamic SQL, though, is that you have to hard parse every distinct version of the query which not only has the potential of taxing your CPU but has the potential to flood your shared pool with lots of non-sharable SQL statements, pushing out statements you'd like to cache, causing more hard parses and shared pool fragmentation errors. If you're running this once a day, that's probably not a major concern. If hundreds of people are executing it thousands of times a day, that is likely a major concern.
An example of the dynamic SQL approach
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_deptnos varchar2(100) := '10,20';
3 l_rc sys_refcursor;
4 l_dept_rec dept%rowtype;
5 begin
6 open l_rc for 'select * from dept where deptno in (' || l_deptnos || ')';
7 loop
8 fetch l_rc into l_dept_rec;
9 exit when l_rc%notfound;
10 dbms_output.put_line( l_dept_rec.dname );
11 end loop;
12 close l_rc;
13* end;
SQL> /
ACCOUNTING
RESEARCH
PL/SQL procedure successfully completed.
Alternately, you can use a collection. This has the advantage of generating a single, sharable cursor so you don't have to worry about hard parsing or flooding the shared pool. But it probably requires a bit more code. The simplest way to deal with collections
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_deptnos tbl_deptnos := tbl_deptnos(10,20);
3 begin
4 for i in (select *
5 from dept
6 where deptno in (select column_value
7 from table(l_deptnos)))
8 loop
9 dbms_output.put_line( i.dname );
10 end loop;
11* end;
SQL> /
ACCOUNTING
RESEARCH
PL/SQL procedure successfully completed.
If, on the other hand, you really have to start with a comma-separated list of values, then you will have to parse that string into a collection before you can use it. There are various ways to parse a delimited string-- my personal favorite is to use regular expressions in a hierarchical query but you could certainly write a procedural approach as well
SQL> ed
Wrote file afiedt.buf
1 declare
2 l_deptnos tbl_deptnos;
3 l_deptno_str varchar2(100) := '10,20';
4 begin
5 select regexp_substr(l_deptno_str, '[^,]+', 1, LEVEL)
6 bulk collect into l_deptnos
7 from dual
8 connect by level <= length(replace (l_deptno_str, ',', NULL));
9 for i in (select *
10 from dept
11 where deptno in (select column_value
12 from table(l_deptnos)))
13 loop
14 dbms_output.put_line( i.dname );
15 end loop;
16* end;
17 /
ACCOUNTING
RESEARCH
PL/SQL procedure successfully completed.
One option is to use INSTR instead of IN:
SELECT uo.object_name
,uo.object_type
FROM user_objects uo
WHERE instr(',TABLE,VIEW,', ',' || uo.object_type || ',') > 0;
Although this looks ugly, it works well and as long as no index on the column being tested was going to be used (because this prevents the use of any index) the performance won't suffer much. If the column being tested is a primary key for instance, definitely this should not be used.
Another option is:
SELECT uo.object_name
,uo.object_type
FROM user_objects uo
WHERE uo.object_type IN
(SELECT regexp_substr('TABLE,VIEW', '[^,]+', 1, LEVEL)
FROM dual
CONNECT BY regexp_substr('TABLE,VIEW', '[^,]+', 1, LEVEL) IS NOT NULL);
In this case, the list of values should be concatenated into a single varchar variable, delimited by commas (or anything you like.)

How to calculate value of string in oracle?

If I have a string say "3*2+24" how can calculate its value in Oracle? In sql server you can do exec ('select 3*2+24') and it returns 30
Thanks
Here's a little function to calculate arbitrary strings of arithmetic:
SQL> CREATE OR REPLACE FUNCTION calc(pi_val VARCHAR2) RETURN NUMBER IS
2 v_return NUMBER;
3 BEGIN
4 EXECUTE IMMEDIATE 'select '||pi_val||' from dual' INTO v_return;
5 RETURN v_return;
6 END;
7 /
Function created
SQL> SELECT calc('2*6*10') FROM dual;
CALC('2*6*10')
--------------
120
SQL>
Obviously, for production purposes you'd need some error handling...
You can use DCookie's solution.
You can decrease the possibility of an sql injection attack by creating a new user without privileges.
Log in as system and create a new user without privileges:
create user new_user identified by password_new_user;
Create the function (as system) in schema new_user.
create or replace function new_user.calc(pi_val varchar2) return number
is
v_return number;
begin
execute immediate 'select '||pi_val||' from dual' INTO v_return;
return v_return;
end;
/
Next grant execute privileges (as system) on function new_user.calc to the relevant Oracle users (for instance tuinstoel (that's me)).
grant execute on new_user.calc to tuinstoel;
Log in as user tuinstoel.
connect tuinstoel/cheese_and_cheese#ora11
SQL> select new_user.calc('2+3') from dual;
NEW_USER.CALC('2+3')
--------------------
5
Everyone who calls the function new_user.calc has the privileges of new_user (none) inside function new_user.calc, not the rights of the caller (definer's privileges not invoker's privileges).
When you don't want to include new_user. in every call to calc do as tuinstoel:
create synonym calc for new_user.calc;
Next you can test this with:
SQL> select calc('2+3') from dual;
CALC('2+3')
-----------
5
edit1: Others have contemplated about this solution and they have pointed out some (potential) problems: See http://forums.oracle.com/forums/thread.jspa?forumID=75&threadID=943576 .
In ORACLE you can do any computation you want using the dual feature. For example
SELECT 3*3-(2+2) FROM DUAL
However, you cannot do
SELECT '3*3-(2+2)' FROM DUAL
Because it will just return the string of '3*3-(2+2)'. If all you have is a string, I would probably strip it out into a numeric function then do the calculation.
You can use the xmlquery() function to implement it. Here is a sample code:
select
xmlquery(
replace( 'your variable', '/', ' div ')
returning content
).getNumberVal()
from
dual;
For example the code below
SELECT
XMLQUERY(
REPLACE( '3/4', '/', ' div ')
RETURNING CONTENT
).getNumberVal() FROM DUAL;
returns 0.75
It's a bit clunkier in Oracle but it can be done.
SQL> set serveroutput on
SQL> declare
2 str varchar2(250) := '3*2+24';
3 n number;
4 begin
5 execute immediate 'select '||str||' from dual' into n;
6 dbms_output.put_line(str||' = '||to_char(n)||' !!');
7 end;
8 /
3*2+24 = 30 !!
PL/SQL procedure successfully completed.
SQL>

Resources