How to build dynamic SQL with using - oracle

I am trying to make dynamic sql like this 'select col1,col2 from '|| my_table ||' it works fine but I want to write like this sql_stmt:='select col1,col2 from :myTable'; execute immediate sql_stmt using my_table; but I have error I have same error when I want to do something like this tooo v_filter := my_proc(); sql_stmt:='select col1,col2 from my_table where :filter' execute immediate sql_stmt using v_filter; is it impossible to build dynamic sql like this with using ? If it is impossible what is another way to avoid sql injections ?

When you want to use table names in dynamic SQL, yes - you'll have to concatenate them. In order to avoid SQL injection, use DBMS_ASSERT.SQL_OBJECT_NAME.
Here's an example:
SQL> create or replace procedure p_test (par_table in varchar2) is
2 l_table varchar2(30);
3 l_str varchar2(200);
4 l_cnt number;
5 begin
6 l_table := dbms_assert.sql_object_name(par_table);
7
8 l_str := 'select count(*) from ' || par_table;
9 execute immediate (l_str) into l_cnt;
10 dbms_output.put_line('Table contains ' || l_cnt || ' rows');
11 end;
12 /
Procedure created.
SQL>
SQL> exec p_test('dept');
Table contains 4 rows
PL/SQL procedure successfully completed.
SQL> exec p_test('delete from emp');
BEGIN p_test('delete from emp'); END;
*
ERROR at line 1:
ORA-44002: invalid object name
ORA-06512: at "SYS.DBMS_ASSERT", line 316
ORA-06512: at "SCOTT.P_TEST", line 6
ORA-06512: at line 1
SQL>
[EDIT: WHERE clause]
This works:
SQL> create or replace procedure p_test (par_table in varchar2,
2 par_filter in varchar2) is
3 l_table varchar2(30);
4 l_str varchar2(200);
5 l_cnt number;
6 begin
7 l_table := dbms_assert.sql_object_name(par_table);
8
9 l_str := 'select count(*) from ' || par_table ||
10 ' where deptno = :filter';
11 execute immediate (l_str) into l_cnt using par_filter;
12 dbms_output.put_line('Table contains ' || l_cnt || ' rows');
13 end;
14 /
Procedure created.
SQL> exec p_test('emp', '10');
Table contains 3 rows
PL/SQL procedure successfully completed.
SQL>
WHERE clause, modified so that it contains only the WHERE keyword, while the rest is to be used as a parameter:
SQL> create or replace procedure p_test (par_table in varchar2,
2 par_filter in varchar2) is
3 l_table varchar2(30);
4 l_str varchar2(200);
5 l_cnt number;
6 begin
7 l_table := dbms_assert.sql_object_name(par_table);
8
9 l_str := 'select count(*) from ' || par_table ||
10 ' where :filter';
11 execute immediate (l_str) into l_cnt using par_filter;
12 dbms_output.put_line('Table contains ' || l_cnt || ' rows');
13 end;
14 /
Procedure created.
SQL> exec p_test('emp', 'deptno = 10');
BEGIN p_test('emp', 'deptno = 10'); END;
*
ERROR at line 1:
ORA-00920: invalid relational operator
ORA-06512: at "SCOTT.P_TEST", line 11
ORA-06512: at line 1
SQL>
It won't work; is that what you're asking?
Some more reading about dynamic SQL on Oracle, as well as here, on Stack overflow (How can I create a dynamic WHERE clause.

Related

how to take second column if first column not found in table in oracle DB

I'm expecting to write a query which takes second column if first column not found in table in oracle DB. In my case 'name' column is not present in table 'employees'
NOTE : I'm using reference cursor
I tried below,
query1:='select id or name,age from employees';
when I execute above statement, getting error
ORA-00904 "name": invalid identifier
ORA-06512 : at "employees", line 21
Explicitly, I don't think you can (as you saw).
Though, you can select * from employees and it'll work:
SQL> declare
2 l_rc sys_refcursor;
3 l_row dept%rowtype;
4 begin
5 open l_rc for select * from dept;
6 fetch l_rc into l_row;
7 end;
8 /
PL/SQL procedure successfully completed.
SQL>
Alternatively, did you consider creating a select statement dynamically, by querying user_tab_columns? Something like this:
SQL> declare
2 l_str varchar2(500);
3 l_rc sys_refcursor;
4 begin
5 for cur_r in (select column_name from user_tab_columns
6 where table_name = 'DEPT'
7 )
8 loop
9 l_str := l_str || ', '|| cur_r.column_name;
10 end loop;
11
12 l_str := 'select ' || ltrim(l_str, ', ') || ' from dept';
13
14 dbms_output.put_line(l_str);
15
16 open l_rc for l_str;
17 end;
18 /
select DEPTNO, DNAME, LOC from dept --> this is the SELECT statement
PL/SQL procedure successfully completed.
SQL>

Is it possible to take a stored procedure name as input parameter in another stored procedure and execute it?

I have a set of stored procedures with the same interface and one of these stored procedures will be passed to a runner stored procedure as an input parameter. How can I execute this stored procedure within the runner proc.
I tried this by using dynamic SQL. The code snippet I wrote for this:
v_proc_query := ':1(:2, :3)';
execute immediate v_proc_query using p_proc_name, p_param1, p_param2;
But the above statement give error: ORA-00900: invalid SQL statement
I'm using Oracle 12c.
What is the right approach to achieve the goal?
Something like this, perhaps?
SQL> set serveroutput on
SQL> create or replace procedure p_test (par_deptno in number)
2 is
3 l_cnt number;
4 begin
5 select count(*)
6 into l_cnt
7 from emp
8 where deptno = par_deptno;
9 dbms_output.put_line('count = ' || l_cnt);
10 end;
11 /
Procedure created.
SQL> create or replace procedure p_test_2 (par_proc_name in varchar2, par_deptno in number)
2 is
3 l_str varchar2(200);
4 begin
5 l_str := 'begin ' ||
6 dbms_assert.sql_object_name(par_proc_name) ||
7 '(' || par_deptno || ');' ||
8 'end;';
9 execute immediate l_str;
10 end;
11 /
Procedure created.
SQL> exec p_test_2('p_test', 10);
count = 3
PL/SQL procedure successfully completed.
SQL>

Execute Immediate?

Why do we use execute immediate command in PL/SQL?
I'm looking at some procedures written by a previous colleague, and I see that the person has used execute immediate a lot many time to log the progress of the procedure and also when truncating the tables.
My question is why would he do so? Can we not just truncate tables just like that in pl/sql proc?
Oracle wouldn't allow execution of DDL inside executable block. This is not allowed
begin
alter table . . .
end;
Originally Oracle had SYS.DBMS_DDL package to do this job. But it was cumbersome to use, so oracle introduced execute immediate circa v9.
Apart from executing DDL from PL/SQL (which you've already been told), execute immediate is used to run dynamic SQL. What would that be? Creating statements that depend on information that is not known at the time you're creating the PL/SQL procedure. For example, selecting from several tables in your schema:
SQL> set serveroutput on
SQL> declare
2 l_str varchar2(1000);
3 l_cnt number;
4 begin
5 for cur_r in (select table_name from user_tables
6 where table_name in ('EMP', 'DEPT', 'BONUS')
7 )
8 loop
9 l_str := 'select count(*) from ' || cur_r.table_name;
10 execute immediate l_str into l_cnt;
11 dbms_output.put_line(cur_r.table_name ||': '|| l_cnt);
12 end loop;
13 end;
14 /
BONUS: 0
DEPT: 4
EMP: 14
PL/SQL procedure successfully completed.
SQL>
Similarly, you might create a function that uses table (or column) names dynamically, e.g.
SQL> create or replace function f_cnt (par_table_name in varchar2)
2 return number
3 is
4 l_str varchar2(1000);
5 l_cnt number;
6 begin
7 l_str := 'select count(*) from ' || dbms_assert.sql_object_name(par_table_name);
8 execute immediate l_str into l_cnt;
9 return l_cnt;
10 end;
11 /
Function created.
SQL> select f_cnt('emp') from dual;
F_CNT('EMP')
------------
14
SQL>

Error displaying result from execute immediate in oracle pl/sql

CREATE OR REPLACE PROCEDURE create_tbl_user
(tbl_name varchar2)
IS
sql_stat varchar2(500);
l_count number;
BEGIN
--checking the presence of tablespace
sql_stat := 'select count(*) from dba_tablespaces where tablespace_name = upper('||tbl_name||')';
dbms_output.put_line(sql_stat);
execute immediate sql_stat into l_count;
dbms_output.put_line(l_count);
/* IF (l_count != 0) THEN
dbms_output.put_line('Tablespace with '||tbl_name||' name already present');
END IF;*/
END;
set serveroutput on;
exec create_tbl_user('tbs');
Error starting at line : 18 in command -
BEGIN CREATE_TBL_USER('temp'); END;
Error report -
ORA-00942: table or view does not exist
ORA-06512: at "SAMAN.CREATE_TBL_USER", line 10
ORA-06512: at line 1
00942. 00000 - "table or view does not exist"
*Cause:
*Action:
Screenshot: the error i get in sqldeveloper
A slight modification; USING (line 11) is what you're looking for.
SQL> CREATE OR REPLACE PROCEDURE create_tbl_user (tbl_name VARCHAR2)
2 IS
3 sql_stat VARCHAR2 (500);
4 l_count NUMBER;
5 BEGIN
6 --checking the presence of tablespace
7 sql_stat :=
8 'select count(*) from dba_tablespaces where tablespace_name = upper(:1)';
9 DBMS_OUTPUT.put_line (sql_stat);
10
11 EXECUTE IMMEDIATE sql_stat INTO l_count USING tbl_name;
12
13 DBMS_OUTPUT.put_line (l_count);
14
15 IF (l_count != 0)
16 THEN
17 DBMS_OUTPUT.put_line (
18 'Tablespace with ' || tbl_name || ' name already present');
19 END IF;
20 END;
21 /
Procedure created.
SQL> EXEC create_tbl_user('temp');
select count(*) from dba_tablespaces where tablespace_name = upper(:1)
1
Tablespace with temp name already present
PL/SQL procedure successfully completed.
SQL>

Execute Immediate bind variable

I have the following function that calculates content of the table but when I pass any param it throws:
EXEC DBMS_OUTPUT.PUT_LINE(get_size('employees'))
Error report -
ORA-00903: invalid table name
ORA-06512: at "HR.GET_SIZE", line 5
ORA-06512: at line 1
00903. 00000 - "invalid table name"
Function
CREATE OR REPLACE FUNCTION get_size(v_table_name IN VARCHAR2)
RETURN NUMBER IS total_size NUMBER(16);
plsql_statement VARCHAR2(500) := 'SELECT COUNT(*) FROM :param';
BEGIN
EXECUTE IMMEDIATE plsql_statement INTO total_size USING v_table_name;
RETURN(total_size);
END;
/
EXEC DBMS_OUTPUT.PUT_LINE(get_size('employees'));
You can't bind table names, it needs to be constructed.
CREATE OR REPLACE FUNCTION get_size(v_table_name IN VARCHAR2)
RETURN NUMBER IS total_size NUMBER(16);
plsql_statement VARCHAR2(500)
BEGIN
plsql_statement := 'SELECT COUNT(*) FROM ' || v_table_name;
EXECUTE IMMEDIATE plsql_statement INTO total_size;
RETURN(total_size);
END;
/
EXEC DBMS_OUTPUT.PUT_LINE(get_size('employees'));
You cannot bind table names or column names. You can bind only variables.
In your case, you just need to EXECUTE IMMEDIATE the dynamic SQL.
SQL> CREATE OR REPLACE FUNCTION get_size(v_table_name IN VARCHAR2)
2 RETURN NUMBER IS total_size NUMBER(16);
3 plsql_statement VARCHAR2(500);
4 BEGIN
5 plsql_statement := 'SELECT COUNT(*) FROM ' || v_table_name;
6 EXECUTE IMMEDIATE plsql_statement INTO total_size;
7 RETURN(total_size);
8 END;
9 /
Function created.
SQL>
SQL>
SQL> EXEC DBMS_OUTPUT.PUT_LINE(get_size('EMP'));
14
PL/SQL procedure successfully completed.
SQL>

Resources