oracle dbms_sql evaluate expression in where clause - oracle

I learned from here that a complete where-clause expression cannot be evaluated using placeholders with native dynamic SQL; placeholders are for passing values.
However, it was also mentioned in the comments that it may be possible to do this with DBMS_SQL.
What is the analogous version of the following code in DBMS_SQL:
DECLARE
where_expression VARCHAR2(40) := q'[filter_column = 'some_value')]';
plsql_block VARCHAR2(500);
BEGIN
plsql_block := 'SELECT column FROM mytable WHERE :a';
EXECUTE IMMEDIATE plsql_block USING where_expression;
END;
/

Related

Oracle display resultset of dynamic sql

i am pretty new to oracle and i am searching for two days already for a solution for my problem.
i have a view which should have a dynamic column and table name.
something like that:
DECLARE
plsql_block VARCHAR2(500);
BEGIN
plsql_block := 'SELECT CONCAT('some','column') FROM CONCAT('some','table')';
EXECUTE IMMEDIATE plsql_block
END;
This would work but how to i display the result? I already tried it with DBMS.Output and a Loop but thats not exactly what i want. i need that it is displayed as a normal result set in the GUI when i run this command. Does anyone has a hint for me how i am doing this in oracle?
I am Thankful for every answer
Thanks pat
Actually I don't understand your dynamic query. But as per my understanding this query is multirow result result set. So you need to use BULK collect and iterate throuh the output for just the purpose of display.
There are two approaches
1) Just to display the output.
SET serveroutput ON;
DECLARE
plsql_block VARCHAR2(500);
lv_col1 VARCHAR2(10):='1';
lv_col2 VARCHAR2(10):='2';
type tab_var
IS
TABLE OF VARCHAR2(10);
tab tab_var;
BEGIN
plsql_block := 'SELECT CONCAT('||lv_col1||','||lv_col2||') FROM dual';
EXECUTE immediate plsql_block bulk collect INTO tab;
FOR i IN tab.first..tab.last
LOOP
dbms_output.put_line(tab(i));
END LOOP;
END;
2) Approach will be refactor this into a function and then use it as below.
Creating a Table Type
create or replace
type string_table
IS TABLE OF VARCHAR2(100);
CREATE OR REPLACE
FUNCTION func_mu
RETURN string_table
AS
plsql_block VARCHAR2(500);
lv_col1 VARCHAR2(10):='1';
lv_col2 VARCHAR2(10):='2';
tab string_table;
BEGIN
plsql_block := 'SELECT CONCAT('||lv_col1||','||lv_col2||') FROM dual';
EXECUTE immediate plsql_block bulk collect INTO tab;
RETURN tab;
END;
SELECT * FROM TABLE(func_mu);
If you are on Oracle 12c (with the corresponding Oracle client), you can do this:
declare
l_resultset sys_refcursor;
l_sql_text varchar2(500) :=q'{select 'Hello, 12c!' as greeting from dual}';
begin
open l_resultset for l_sql_text;
dbms_sql.return_result(l_resultset);
end;
(Untested, because I'm not near a 12c command line right now.)

Oracle dynamic parameters

I'm struggling to create a dynamic sql parametrized query. It involves using 'IS NULL' or 'IS NOT NULL'
Here's a simple pl/sql query:
CREATE OR REPLACE PROCEDURE GET_ALL_INFORMATION
(
"PARAM_START_DATE" IN DATE,
"PARAM_END_DATE" IN DATE,
"PARAM_IS_SUBMITTED" IN NUMBER,
"EXTRACT_SUBMITTED_CONTACTS" OUT sys_refcursor
) IS
sql_stmt VARCHAR2(3000);
PARAM_CONDITION VARCHAR2(20);
BEGIN
IF PARAM_IS_SUBMITTED = 1 THEN
PARAM_CONDITION := 'NOT NULL';
ELSE
PARAM_CONDITION := 'NULL';
END IF;
sql_stmt := ' SELECT
REGISTRATION_NUMBER,
NAME PROVIDER_TYPE,
ORGANIZATION
FROM TABLE_A
WHERE
P.DATE_FINALIZED IS :A;
OPEN EXTRACT_SUBMITTED_CONTACTS FOR sql_stmt USING PARAM_CONDITION;
Whereas the parameter (:A) in (USING PARAM_CONDITION) should have 'NULL' or 'NOT NULL'. It does not seem to work the way I envisioned.
Am I missing something?
As explained by GriffeyDog in a comment above, bind parameters could only be used as place holder for values. Not to replace keywords or identifiers.
However, this is not really an issue here, as you are using dynamic SQL. The key idea ifs that you build your query as a string -- and it will be parsed at run-time by the PL/SQL engine when you invoke EXECUTE or OPEN .. FOR.
Simply said, you need a concatenation -- not a bound parameter:
...
sql_stmt := ' SELECT
REGISTRATION_NUMBER,
NAME PROVIDER_TYPE,
ORGANIZATION
FROM TABLE_A
WHERE
P.DATE_FINALIZED IS ' || PARAM_CONDITION;
-- ^^
OPEN EXTRACT_SUBMITTED_CONTACTS FOR sql_stmt;

using EXECUTE IMMEDIATE with multiple same bind arguments

When I create the following procedure
create or replace procedure check_exec_imm(
tab IN VARCHAR2,
col IN VARCHAR2,
col_name IN VARCHAR2
)
IS
cv SYS_REFCURSOR;
col_value VARCHAR2(32767);
lv_query VARCHAR2(32767);
BEGIN
lv_query := 'SELECT ' ||col||
' FROM ' ||tab||
' WHERE (:1 = ''EUR'' OR :1 = ''USD'') and rownum <=1';
EXECUTE IMMEDIATE lv_query INTO col_value USING col_name ;
DBMS_OUTPUT.PUT_LINE('COLUMN VALUE : ' || col_value);
END;
When the procedure is executed, I'm getting the following error
ORA-01008: not all variables bound
ORA-06512: at "GRM_IV.CHECK_EXEC_IMM", line 18
ORA-06512: at line 2
When I give the bind argument col_name again as below, the procedure is running fine.
EXECUTE IMMEDIATE lv_query INTO col_value USING col_name, col_name ;
Why oracle is behaving differently in this procedure. Since, it is the same bind variable, one bind argument should be sufficient right..!!? Please explain where I'm getting my logic wrong.
There is "special" behaviour in Oracle: Repeated Placeholder Names in Dynamic SQL Statements
In an Anonymous Block or CALL Statement it is not required to repeat the bind values if the names are equal.
For example this Anonymous Block is working:
DECLARE
a NUMBER := 4;
b NUMBER := 7;
plsql_block VARCHAR2(100);
BEGIN
plsql_block := 'BEGIN calc_stats(:x, :x, :y, :x); END;';
EXECUTE IMMEDIATE plsql_block USING a, b; -- calc_stats(a, a, b, a)
END;
/
But this EXECUTE IMMEDIATE plsql_block USING a, b; does not work inside a Procedure.
The way you have referenced the column name through bind variable is not a preferred method as Nichoas pointed out. What you tried is called as native dynamic SQL using 'cleverer' bind variables.
In this method, you need to bind every parameter X times since you use it X times because they are all treated as separate variables.
Read more on binding to dynamic SQL.
#ethan and #ManiSankar I too had a same problem in my scenario as well. I solved this using some kind of brute force techinque. What i have done is
Before this
EXECUTE IMMEDIATE lv_query INTO col_value USING col_name ;
I have added replace condition in my code by replacing parameter with the required value then called "Execute Immediate" without "using" clause
lv_query := replace(lv_query, ':1',col_name);
EXECUTE IMMEDIATE lv_query INTO col_value;
I don't know this is optimal one but served purpose for what i am expecting..
Please advice if this one recommended or not...

how to run a dynamic select in PL/SQL

Bear with me a bit
I want to run a select statement and the result should look exactly as if I ran that select myself result should be a datagrid, not a dbms_output?
DECLARE
sql_stmt VARCHAR2(200);
sql_stmt2 VARCHAR2(200);
ids VARCHAR2(200);
BEGIN
ids := 5;
sql_stmt:='select query from query_table where id = :id';
EXECUTE IMMEDIATE sql_stmt using ids into sql_stmt2;
EXECUTE IMMEDIATE sql_stmt2;
END;
but this gives me a
ORA-06502: PL/SQL: numeric or value error
ORA-06512: at line 8
line 8 is
EXECUTE IMMEDIATE sql_stmt2
By my count line 8 is EXECUTE IMMEDIATE sql_stmt using ids into sql_stmt2;.
sql_stmt2 is defined as VARCHAR2(200).
I'd say your query returns a string with more than 200 characters.
1) You don't need dynamic SQL to execute your first statement - all identifiers are known at the time of execution. Static SQL like
select query from query_table where id = p_ds;
where p_id is a parameter is the relevant choice.
2) Line 8 points the expression
EXECUTE IMMEDIATE sql_stmt using ids into sql_stmt2;
which has the wrong syntax, INTO clause has to be the first one:
EXECUTE IMMEDIATE sql_stmt INTO sql_stmt2 USING ids;
See Oracle documentation please:
http://docs.oracle.com/cd/B13789_01/appdev.101/b10807/13_elems017.htm
To get the complete answer in your question please provide the real code and data example in your table.
Can you try
ids number;
or
ids :='5';

Calling preocedure by passing DB Link Dynamically

How to pass db link dynamically while calling a procedure?
Execute immediate will work or we need to use dbms_sql?
For DBMS_SQL i seen it used mostly with curosrs :(
Can any one help me ?
You can use EXECUTE IMMEDIATE. Something like
DECLARE
l_dblink_name VARCHAR2(30) := 'YourDBLink';
l_sql_stmt VARCHAR2(1000);
BEGIN
l_sql_stmt := 'BEGIN procedure_name#' || l_dblink_name || ' (:1, :2); END;';
EXECUTE IMMEDIATE l_sql_stmt
USING 17, 42;
END;
assuming that your procedure takes two parameters and you want to call it with parameter values 17 and 42.

Resources