This question already has answers here:
SELECT from table with Varying IN list in WHERE clause
(2 answers)
Closed 5 years ago.
I am passing an input value as 'MI,NOKIA,APPLE' to a procedure. This input value has to be passed to the query in where condition.
For example:
create or replace procedure abc123(p_name varchar2)
is
v_val number;
begin
dbms_output.put_line ('p_name: '||p_name );
Select 1
Into v_val
from MYTABLE
where Model in p_name;
dbms_output.put_line ('v_val: '||v_val );
end;
This is not working. How do I pass the multiple input values as single parameter?
Use paranthesis after IN operator :
Select 1 Into v_val from MYTABLE where Model in (p_name);
try below code,
create or replace procedure proc123 (p_param VARCHAR2)
IS
TYPE cur_typ IS REF CURSOR;
c cur_typ;
v_query VARCHAR2(200) := 'SELECT * '||
'FROM (SELECT ''Model1'' model FROM dual '||
'union all '||
'SELECT ''Model2'' FROM dual '||
'union all '||
'SELECT ''Model3'' FROM dual)';
v_model VARCHAR2(20);
BEGIN
v_query := v_query||' WHERE INSTR('''||p_param||''',model) > 0';
dbms_output.put_line(v_query);
OPEN c FOR v_query;
LOOP
FETCH c INTO v_model;
EXIT WHEN c%NOTFOUND;
dbms_output.put_line(v_model);
END LOOP;
CLOSE c;
END;
/
--set your dbms_output on
begin
proc123('Model1, Model2');
end;
/
if you want to pass the parameter in a query, then you can use the INSTR function
Select 1
from MYTABLE
where INSTR(p_name, model) > 0;
Related
I created a function and it uses a dynamic sql:
create function check_ref_value
(
table_name varchar2,
code_value number,
code_name varchar2
) return number is
l_query varchar2(32000 char);
l_res number;
begin
l_query := '
select sign(count(1))
into :l_res
from '|| table_name ||'
where '|| code_name ||' = :code_value
';
execute immediate l_query
using in code_value, out l_res;
return l_res;
end;
But when I try to use it I get an exception "ORA-00933: SQL command not properly ended"
What is wrong with this code?
You can use EXECUTE IMMEDIATE ... INTO ... USING ... to get the return value and DBMS_ASSERT to raise errors in the case of SQL injection attempts:
create function check_ref_value
(
table_name varchar2,
code_value number,
code_name varchar2
) return number is
l_query varchar2(32000 char);
l_res number;
begin
l_query := 'select sign(count(1))'
|| ' from ' || DBMS_ASSERT.SIMPLE_SQL_NAME(table_name)
|| ' where ' || DBMS_ASSERT.SIMPLE_SQL_NAME(code_name)
|| ' = :code_value';
execute immediate l_query INTO l_res USING code_value;
return l_res;
end;
/
Which, for the sample data:
CREATE TABLE abc (a, b, c) AS
SELECT 1, 42, 3.14159 FROM DUAL;
Then:
SELECT CHECK_REF_VALUE('abc', 42, 'b') AS chk FROM DUAL;
Outputs:
CHK
1
And:
SELECT CHECK_REF_VALUE('abc', 42, '1 = 1 OR b') AS chk FROM DUAL;
Raises the exception:
ORA-44003: invalid SQL name
ORA-06512: at "SYS.DBMS_ASSERT", line 160
ORA-06512: at "FIDDLE_UVOFONEFDEHGDQJELQJL.CHECK_REF_VALUE", line 10
As for your question:
What is wrong with this code?
Using SELECT ... INTO is only valid in an SQL statement in a PL/SQL block and when you run the statement via EXECUTE IMMEDIATE it is executed in the SQL scope and not a PL/SQL scope.
You can fix it by wrapping your dynamic code in a BEGIN .. END PL/SQL anonymous block (and reversing the order of the bind parameters in the USING clause):
create function check_ref_value
(
table_name varchar2,
code_value number,
code_name varchar2
) return number is
l_query varchar2(32000 char);
l_res number;
begin
l_query := '
BEGIN
select sign(count(1))
into :l_res
from '|| DBMS_ASSERT.SIMPLE_SQL_NAME(table_name) ||'
where '|| DBMS_ASSERT.SIMPLE_SQL_NAME(code_name) ||' = :code_value;
END;
';
execute immediate l_query
using out l_res, in code_value;
return l_res;
end;
/
(However, that is a bit more of a complicated solution that just using EXECUTE IMMEDIATE ... INTO ... USING ....)
db<>fiddle here
How can I do something like this in oracle SQL developer?
DECLARE
p_name products.product_name%TYPE;
BEGIN
p_name := 'Strawberry';
SELECT * FROM products WHERE product_name = p_name;
END;
Use a cursor:
DECLARE
p_name VARCHAR2(100);
p_cur SYS_REFCURSOR;
BEGIN
p_name := 'Strawberry';
OPEN p_cur FOR
SELECT * FROM products where product_name=p_name;
-- do something with the cursor.
END;
/
Or use a SQL/Plus-style bind variable declaration:
VARIABLE p_name VARCHAR2;
BEGIN
:p_name := 'Strawberry';
END;
/
SELECT * FROM products where product_name=:p_name;
If only one row will ever be returned from your query (i.e. product_name is UNIQUE) then you can use SELECT ... INTO ...:
DECLARE
p_name VARCHAR2(100);
p_value1 products.value1%TYPE;
p_value2 products.value2%TYPE;
p_value3 products.value3%TYPE;
BEGIN
p_name := 'Strawberry';
SELECT value1, value2, value3
INTO p_value1, p_value2, p_value3
FROM products
WHERE product_name=p_name;
-- do something with the values.
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
-- Handle the exception
WHEN TOO_MANY_ROWS THEN
NULL;
-- Handle the exception
END;
/
You need to give the SELECT statement somewhere to put the results of the query:
DECLARE
p_name VARCHAR2(100);
aProducts_row PRODUCTS%ROWTYPE;
BEGIN
p_name := 'Strawberry';
SELECT *
INTO aProducts_row
FROM products
where product_name=p_name;
-- Add code to manipulate data in aProducts_row here, as in...
DBMS_OUTPUT.PUT_LINE('PRODUCT_NAME = ''' ||
aProducts_row.PRODUCT_NAME || '''');
END;
Or if you're expecting more than one row to be returned you can use a cursor:
DECLARE
p_name VARCHAR2(100);
BEGIN
p_name := 'Strawberry';
FOR aProducts_row IN (SELECT *
FROM products
where product_name=p_name)
LOOP
-- Add code to manipulate data in aProducts_row here, as in...
DBMS_OUTPUT.PUT_LINE('PRODUCT_NAME = ''' ||
aProducts_row.PRODUCT_NAME || '''');
END LOOP;
END;
If the question is not about setting variables but is actually about output from a PL/SQL anonymous block then it is already answered here.
declare
rc sys_refcursor;
begin
open rc for select 'Hello' as test from dual;
dbms_sql.return_result(rc);
end;
You can set any variable you want using the normal PL/SQL assignment syntax. If there is some part of the documentation that is not clear then please provide an example of what you are trying to do.
I created an Object and procedure as below and while execution i get the below error.
ORA-06504: PL/SQL: Return types of Result Set variables or query do
not match ORA-06512: at line 8
CREATE OR REPLACE TYPE OBJ_TST AS OBJECT
(
COl_ID NUMBER (30, 0),
Col_DATE TIMESTAMP (6)
);
/
create or replace TYPE OBJ_TBL AS TABLE OF OBJ_TST;
/
CREATE OR REPLACE PROCEDURE TST_OBJ (input_date IN DATE,
out_cur OUT SYS_REFCURSOR )
AS
l_tab OBJ_TBL := OBJ_TBL ();
BEGIN
SELECT OBJ_TST (ti.col_id, ti.col_date)
BULK COLLECT INTO l_tab
FROM MY_TBL ti
WHERE ti.create_date BETWEEN input_date AND input_date + 1;
Open o_cur for select col_id,col_date from table(l_tab);
END TST_OBJ;
/
Execution brings me the above mentioned error. MY_TBL has column data type of (col_id and col_date) same as of my object.
DECLARE
a SYS_REFCURSOR;
var1 OBJ_TBL;
BEGIN
TST_OBJ (input_date => '21-Aug-2017', out_cur => a);
FETCH a bulk collect INTO var1;
For rec in 1..var1.count
LOOP
DBMS_OUTPUT.put_line (var1(rec).col_id ||' '|| var1(rec).Col_DATE);
END LOOP;
END;
/
ORA-06504: PL/SQL: Return types of Result Set variables or query do
not match ORA-06512: at line 8
However when i excute like this it works fine:
DECLARE
a SYS_REFCURSOR;
var1 NUMBER;
var2 TIMESTAMP (6);
BEGIN
TST_OBJ (i_date => '21-Aug-2017', out_cur => a);
LOOP
FETCH a INTO var1, var2;
EXIT WHEN a%NOTFOUND;
DBMS_OUTPUT.put_line (var1 ||' '|| var2);
END LOOP;
END;
Can anyone please suggest whats wrong here ?
You're using a table collection expression to unnest your table collection:
Open out_cur for select col_id,col_date from table(l_tab);
The query is returning two relational columns, not a single object, so your cursor has two columns too. Trying to bulk collect two relational columns into a single object in your anonymous block is throwing the exception.
You could, I suppose, recombine them as objects:
Open out_cur for select OBJ_TST(col_id,col_date) from table(l_tab);
or if you don't want to explicitly list the column/field names:
Open out_cur for select cast(multiset(select * from table(l_tab)) as obj_tbl) from dual;
But then in your example having the table type is a bit pointless, and you can just do:
CREATE OR REPLACE PROCEDURE TST_OBJ (input_date IN DATE,
out_cur OUT SYS_REFCURSOR )
AS
BEGIN
Open out_cur for
SELECT OBJ_TST (ti.col_id, ti.col_date)
FROM MY_TBL ti
WHERE ti.create_date BETWEEN input_date AND input_date + 1;
END TST_OBJ;
/
But I image you have some other use for the collection inside the function - modifying it before querying and returning it. Or you could make the second argument of OBJ_TBL type instead of a ref cursor, so the caller doesn't have to bulk collect that into its own local collection itself.
DECLARE
a SYS_REFCURSOR;
var1 OBJ_TBL;
BEGIN
TST_OBJ (input_date => '21-Aug-2017', out_cur => a);
FETCH a bulk collect INTO var1;
For rec in 1..var1.count
LOOP
DBMS_OUTPUT.put_line (var1(rec).col_id ||' '|| var1(rec).Col_DATE);
END LOOP;
END;
/
The cursor a has two columns and you are trying to bulk collect them into a single variable. Oracle will not wrap them in a OBJ_TST object and it can't match them.
Why use cursors at all:
CREATE OR REPLACE PROCEDURE TST_OBJ (
input_date IN DATE,
out_objs OUT OBJ_TBL
)
AS
BEGIN
SELECT OBJ_TST( col_id, col_date)
BULK COLLECT INTO out_objs
FROM MY_TBL
WHERE create_date BETWEEN input_date AND input_date + 1;
END TST_OBJ;
/
Then you can just do:
DECLARE
var1 OBJ_TBL;
BEGIN
TST_OBJ (
input_date => DATE '2017-08-21',
out_objs => var1
);
For rec in 1..var1.count LOOP
DBMS_OUTPUT.put_line (var1(rec).col_id ||' '|| var1(rec).Col_DATE);
END LOOP;
END;
/
I am trying to execute below query, but not getting any result.
Could some one tell what wrong I am doing?.
DECLARE
object_types VARCHAR2(200);
v_object_types VARCHAR2(200);
l_count number;
BEGIN
object_types :='TABLE,VIEW';
select ''''||regexp_replace(object_types, '( )*(,)( )*',''',''')||''''
into v_object_types from dual;
dbms_output.put_line(to_char(v_object_types));
SELECT count(*) into l_count
FROM all_objects o where o.object_type IN ('||v_object_types||');
dbms_output.put_line(l_count);
END;
WHERE variable IN (1,2,3)
is different to what you are sending now
WHERE variable IN ('1,2,3') or
WHERE variable IN ('||v_object_types||')
You are trying to build the SQL dynamically but instead you are using a single string literal '||v_object_types||' in the IN clause.
You can do it using a collection:
Oracle Setup:
CREATE TYPE stringlist IS TABLE OF VARCHAR2(200);
/
PL/SQL:
DECLARE
object_types VARCHAR2(200) := 'TABLE,VIEW';
v_object_types stringlist;
BEGIN
SELECT TRIM( BOTH FROM REGEXP_SUBSTR( object_types, '[^,]+', 1, LEVEL ) )
BULK COLLECT INTO v_object_types
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT( object_types, '[^,]+' );
FOR i IN 1 .. v_object_types.COUNT LOOP
dbms_output.put_line( v_object_types(i) );
END LOOP;
SELECT count(*)
INTO l_count
FROM all_objects
WHERE object_type MEMBER OF v_object_types;
dbms_output.put_line(l_count);
END;
/
or using dynamic sql:
DECLARE
object_types VARCHAR2(200) := 'TABLE,VIEW';
v_sql VARCHAR2(200);
l_count INTEGER;
BEGIN
v_sql := 'SELECT count(*) FROM all_objects WHERE object_type IN ('
|| REGEXP_REPLACE( object_types, ' *(.+?) *(,|$)', '''\1''\2' )
|| ')';
EXECUTE IMMEDIATE v_sql INTO l_count;
dbms_output.put_line(l_count);
END;
/
I have a string which contains SQL SELECT statement.
I wonder how can I output result of the execution of that statement on the screen, execution will be done using native dynamic SQL (EXECUTE IMMEDIATE).
example:
DECLARE
v_stmt VARCHAR2 := 'SELECT * FROM employees';
BEGIN
EXECUTE IMMEDIATE v_stmt; -- ??? how to output result of that select on the screen.
END;
Important remark: structure of table can be any. I have to write a procedure which accepts name of the table as parameter, so I can't hardcode a table structure and don't want to do it.
Thanks for responses. Any ideas very appreciated/
If you are on Oracle 12c with a 12c client, this should work:
declare
rc sys_refcursor;
begin
open rc for 'select * from dual';
dbms_sql.return_result(rc);
end;
Yes we can execute select statement dynamically.
Let say we have a table test. It has four column Row_id,Name,Rank etc
When we do select * from test;
Result will be
Row_id Name Rank
1 R1 5
2 R2 1
3 R3 2
4 R4 4
Now we can use DBMS_SQL package to execute dynamically SELECT Sql Statament.
Code is below:
DECLARE
v_CursorID NUMBER;
v_table VARCHAR2(50):='test';
v_SelectRecords VARCHAR2(500);
v_NUMRows INTEGER;
v_MyNum INTEGER;
v_Myname VARCHAR2(50);
v_Rank INTEGER;
BEGIN
v_CursorID := DBMS_SQL.OPEN_CURSOR;
v_SelectRecords := 'SELECT * from ' || v_table ;
DBMS_SQL.PARSE(v_CursorID,v_SelectRecords,DBMS_SQL.V7);
DBMS_SQL.DEFINE_COLUMN(v_CursorID,1,v_MyNum);
DBMS_SQL.DEFINE_COLUMN(v_CursorID,2,v_Myname,50);
DBMS_SQL.DEFINE_COLUMN(v_CursorID,3,v_Rank);
v_NumRows := DBMS_SQL.EXECUTE(v_CursorID);
LOOP
IF DBMS_SQL.FETCH_ROWS(v_CursorID) = 0 THEN
EXIT;
END IF;
DBMS_SQL.COLUMN_VALUE(v_CursorId,1,v_MyNum);
DBMS_SQL.COLUMN_VALUE(v_CursorId,2,v_Myname);
DBMS_SQL.COLUMN_VALUE(v_CursorId,3,v_Rank);
DBMS_OUTPUT.PUT_LINE(v_MyNum || ' ' || v_Myname || ' ' || v_Rank );
END LOOP;
EXCEPTION
WHEN OTHERS THEN
RAISE;
DBMS_SQL.CLOSE_CURSOR(v_CursorID);
end;