SET SERVEROUTPUT ON
DECLARE
table_name varchar2(80) := 'dual';
BEGIN
SELECT * FROM table_name WHERE dummy = 'X';
END;
The above code throws error. I want to use a variable in the from clause.
For a dynamic query you can use EXECUTE IMMEDIATE.
DECLARE
table_name VARCHAR2 (80) := 'dual';
v_query VARCHAR2 (200);
BEGIN
v_query := 'SELECT *
FROM ' || table_name || '
WHERE dummy = ''X''';
EXECUTE IMMEDIATE v_query;
END;
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
I am trying to pass an expression into the WHERE clause of my query using dynamic SQL. The expression can contain multiple filters/columns.
Similar to other posts on SO, the following (example 1) works:
DECLARE
where_expression VARCHAR2(40) := q'[filter_column = 'some_value')]';
plsql_block VARCHAR2(500);
BEGIN
plsql_block := 'SELECT column FROM mytable';
EXECUTE IMMEDIATE plsql_block || ' WHERE ' || where_expression;
END;
/
And this approach (example 2) using placeholders does not work:
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;
/
Oracle returns an error: ORA-00920: invalid relational operator at line 8 (EXEC statement).
What am I doing wrong in example 2 and what's the correct way with placeholders?
What am I doing wrong in example 2 and what's the correct way with placeholders?
The placeholder syntax is for passing values to be checked when the statement is executed. The expected usage is something like this:
DECLARE
v_out_1 varchar2(32);
v_out_2 varchar2(32);
plsql_block VARCHAR2(500);
BEGIN
plsql_block := 'SELECT column FROM mytable WHERE filter_column = :a';
EXECUTE IMMEDIATE plsql_block INTO v_out_1 USING 'some value';
EXECUTE IMMEDIATE plsql_block INTO v_out_2 USING 'another value';
END;
/
Whitelist the possible filter columns in a CASE expression:
DECLARE
v_out VARCHAR2(32);
column_name VARCHAR2(30) := 'COLUMN1';
column_value VARCHAR2(30) := 'value1';
sql VARCHAR2(500);
BEGIN
sql := 'SELECT column
FROM mytable
WHERE CASE :name
WHEN ''COLUMN1'' THEN column1
WHEN ''COLUMN2'' THEN column2
WHEN ''COLUMN3'' THEN column3
END = :value';
EXECUTE IMMEDIATE sql INTO v_out USING column_name, column_value;
END;
/
I'm trying desperately to call an oracle stored procedure and have the output come out looking like they came from a select statement.
The goal is to embed the sql in other software that can execute sql queries.
Here's a sample of the kind of thing I'd like to be able to do.
/* P_DELETE_THIS_TEST IS A STORED PROCEDURE WITH 4 VARCHAR2 PARAMETERS DEFINED AS IN OUT VARIABLES */
DECLARE
char_1 VARCHAR2(255) := 'TEST';
char_2 VARCHAR2(255) := '';
char_3 VARCHAR2(255) := '';
char_4 VARCHAR2(255) := '';
BEGIN
P_DELETE_THIS_TEST(char_1, char_2, char_3, char_4);
SELECT char_1, char_2, char_3, char_4 FROM DUAL;
END;
You can try with dynamic sql.
The following PL/SQL block contains several examples of dynamic SQL:
DECLARE
sql_stmt VARCHAR2(200);
plsql_block VARCHAR2(500);
emp_id NUMBER(4) := 7566;
salary NUMBER(7,2);
dept_id NUMBER(2) := 50;
dept_name VARCHAR2(14) := 'PERSONNEL';
location VARCHAR2(13) := 'DALLAS';
emp_rec emp%ROWTYPE;
BEGIN
EXECUTE IMMEDIATE 'CREATE TABLE bonus (id NUMBER, amt NUMBER)';
sql_stmt := 'INSERT INTO dept VALUES (:1, :2, :3)';
EXECUTE IMMEDIATE sql_stmt USING dept_id, dept_name, location;
sql_stmt := 'SELECT * FROM emp WHERE empno = :id';
EXECUTE IMMEDIATE sql_stmt INTO emp_rec USING emp_id;
plsql_block := 'BEGIN emp_pkg.raise_salary(:id, :amt); END;';
EXECUTE IMMEDIATE plsql_block USING 7788, 500;
sql_stmt := 'UPDATE emp SET sal = 2000 WHERE empno = :1
RETURNING sal INTO :2';
EXECUTE IMMEDIATE sql_stmt USING emp_id RETURNING INTO salary;
EXECUTE IMMEDIATE 'DELETE FROM dept WHERE deptno = :num'
USING dept_id;
EXECUTE IMMEDIATE 'ALTER SESSION SET SQL_TRACE TRUE';
END;
I have to deploy my sql script and for that I have defined variable in one file and the create script in another file.
File 1:
Define_Variable.sql
DEFINE hr_SCHEMA = hr;
File 2:
Createfile.sql
#Define_variable.sql
declare
v_str varchar2(3000);
lc_cnt number;
BEGIN
v_str :='select count(1) into l_cnt from dba_tab_cols where owner=''' || &hr_SCHEMA ||''' and TABLE_NAME=''employees''';
execute immediate v_str;
IF l_cnt = 0 then
-----perform some operations
end if;
end ;
/
I'm getting the following error.
ORA-06550 and PLS-00201: identifier 'hr' must be declared.
Here the value is getting substituted but how to write the value within quotes. Like my output should execute
select count(1) into l_cnt from dba_tab_cols where owner= 'hr' and TABLE_NAME='employees';
This is just an example of my big script, but the objective is how to substiture a string varible in a where query of a dynamic sql.
Alternatively, you can use the variable inside the SQL string, just remember to add the quotes where owner= ''&HR_schema''
set serveroutput on
define HR_schema = hr
declare
v_str varchar2(3000);
l_cnt number;
begin
--v_str := 'select count(1) from dba_tab_cols where owner=:owner and TABLE_NAME=''employees''';
v_str := 'select count(1) from dba_tab_cols where owner= ''&HR_schema'' and TABLE_NAME=''employees''';
execute immediate v_str into l_cnt ; --using '&HR_schema';
if l_cnt = 0
then
dbms_output.put_line(l_cnt);
-----perform some operations
end if;
end;
Its best to user bind variables and the using clause.
declare
v_str varchar2(3000);
lc_cnt number;
begin
v_str := 'select count(1) from dba_tab_cols where owner=:owner and TABLE_NAME=''employees''';
execute immediate v_str into l_cnt using '&HR_schema';
if l_cnt = 0
then
-----perform some operations
end if;
end;
I am trying to run the sql below and retun all the recs with the batchid, declared on top, but i keep getting error, please advice
EXPECTED RESULT:- I need all the batches with the ID declared above, but the dyamanic sql generates for it, how can this sql generate just result for it ???
CREATE OR REPLACE PROCEDURE LAITEST
IS
declare
l_owner varchar2(30) := 'XXFMSLS';
l_batch varchar2(300) := 'PL_XFER_4';
l_sql varchar2(32000);
begin
l_sql := 'select * from XXFM_FAH_EVNT_CTRL where owner = l_owner and bch_id= l_batch';
dbms_output.put_line( l_sql);
end;
/
1) l_owner and l_batch should be dynamically added to the query, the sql sent to the database should have these values substituted.
2) since these are strings, you need to add in the necessary quotes.
3) You don't need DELCARE
CREATE OR REPLACE PROCEDURE LAITEST
IS
l_owner varchar2(30) := 'XXFMSLS';
l_batch varchar2(300) := 'PL_XFER_4';
l_sql varchar2(32000);
begin
l_sql := 'select * from XXFM_FAH_EVNT_CTRL where owner = ''' || l_owner || ''' and bch_id= ''' || l_batch || '''';
dbms_output.put_line( l_sql);
end;
/
set serveroutput on;
execute LAITEST;
select * from XXFM_FAH_EVNT_CTRL where owner = 'XXFMSLS' and bch_id= 'PL_XFER_4'