Dynamic Select from table at database link - sql

I want to select data from a table in a remote database which I have its database link in a variable, how I can do that?
My query is something like this:
select `table_column` form any_table#:any_variable_1 where any_column= :any_variable_2;
Note:
any_variable_1 is a string variable contains the name of the database link
any_variable_2 is a string variable contains the string for filtering
* this code is to be executed in function in powerbuilder 8 or 9
* the database which I am connecting to is oracle 11 g

Try this:
DECLARE
l_var number(10); -- data type you are expecting from the table_column column
l_cursor sys_refcursor;
l_any_variable_1 varchar2(50) := 'YOUR_DBLINK_HERE';
l_any_variable_2 varchar2(50);
BEIGN
open l_cursor for 'select table_column from any_table# '||
l_any_variable_1 ||
' where any_column = :any_variable_2'
using l_any_variable_2;
loop
fetch l_cursor into l_var;
exit when l_cursor%NOTFOUND;
DBMS_OUTPUT.put_line(l_var);
end loop;
close l_cursor;
END;

string ls_sql
ls_sql="Select column_name from table_name#"+var_db_link_name+""+&
" where column_name='"+var_value+"' and another_column_name='"+another_var+"'"
DECLARE var_cursor DYNAMIC CURSOR FOR SQLSA ;
PREPARE SQLSA FROM :ls_sql ;
OPEN DYNAMIC var_cursor ;
FETCH var_cursor INTO :another_var ;//another_var holds the result of the cursor
CLOSE var_cursor;

Related

PL/SQL Procedure Use String Select Statement in for loop

I am building a procedure, where I`m first creating a select statement and store it in an VARCAHR variable.
I now want to execute that query and store the whole result set in an variable to loop through it or use directly in a for loop.
I only find examples where the Select is hard written in the for loop definition.
How do i exchange the Select statement with my variable that holds my select statement?
for r IN (SELECT ... FROM ...)
loop
--do sth;
end loop;
how i want to use it :
statement := 'SELECT .... FROM ...';
for r IN (statement) -- HOW TO DO THIS
loop
--do sth;
end loop;
For a dynamic ref cursor, you need to define everything explicitly:
declare
sqlstring long := 'select 123 as id, ''demo'' as somevalue from dual where dummy = :b1';
resultset sys_refcursor;
type demo_rectype is record
( id integer
, somevalue varchar2(30) );
demorec demo_rectype;
begin
open resultset for sqlstring using 'X';
loop
fetch resultset into demorec;
exit when resultset%notfound;
dbms_output.put_line('id=' || demorec.id || ' somevalue=' || demorec.somevalue);
end loop;
close resultset;
end;
You can parse the cursor and figure out the column names and datatypes with DBMS_SQL. Example here: www.williamrobertson.net/documents/refcursor-to-csv.shtml

plsql: execute query: invalid identifier

Why can't I execute the stmt query?
It shows tabb is invalid identifier.
TYPE tab_row IS TABLE OF tab_name%ROWTYPE;
tabb tab_row;
TYPE cur_ref Is Ref Cursor;
c cur_ref;
stmt_string Varchar2(1000);
stmt Varchar2(1000);
Begin
stmt_string := 'Select * from ' || tab1 || ' mft
Where mft.mfmt_id IS NOT NULL
and mft.MFMT_FLAG IS NULL';
Open c For stmt_string;
Fetch c bulk collect into tabb;
Close c;
stmt:= 'Update '|| tab || ' m
Set m.mfmt_mat_bnr = tabb(i).mfmt_mat_bnr,
m.mfmt_mat_type = tabb(i).mfmt_mat_type,
m.mfmt_be_seg = tabb(i).mfmt_be_seg
Where m.mfmt_id = tabb(i).mfmt_id';
For i in 1..tabb.count loop
Execute Immediate stmt;
End loop;
End;
I have not seen where is tabb being declared. As you mentioned in your comment, it is a collection.
From your code I can understand you are looping through the values of this collection. This means these values are variables i.e. changing values every iteration. Therefore, you need to use them as such.
You current dynamic statement, this values are static string. This Set m.mfmt_mat_bnr = tabb(i).mfmt_mat_bnr, will result in the same string every iteration. Hence, you need to change that to Set m.mfmt_mat_bnr = '|| tabb(i).mfmt_mat_bnr||', ...etc
I can rewrite your code as follows:
TYPE tab_row IS TABLE OF tab_name%ROWTYPE;
tabb tab_row;
TYPE cur_ref Is Ref Cursor;
c cur_ref;
stmt_string Varchar2(1000);
stmt Varchar2(1000);
Begin
stmt_string := 'Select * from ' || tab1 || ' mft
Where mft.mfmt_id IS NOT NULL
and mft.MFMT_FLAG IS NULL';
Open c For stmt_string;
Fetch c bulk collect into tabb;
Close c;
stmt:= 'Update '|| tab || ' m
Set m.mfmt_mat_bnr = '||tabb(i).mfmt_mat_bnr||',
m.mfmt_mat_type = '||tabb(i).mfmt_mat_type||',
m.mfmt_be_seg = '||tabb(i).mfmt_be_seg||'
Where m.mfmt_id = '||tabb(i).mfmt_id;
For i in 1..tabb.count loop
Execute Immediate stmt;
End loop;
End;
Since this code is not tested, one thing I strongly recommend to confirm first. Is that tabb is correctly declared within the scope.
If the problem is in your loop where you execute your statement, maybe try using EXECUTE IMMEDIATE .. USING statement?
The syntax is:
EXECUTE IMMEDIATE <SQL Command>
[INTO <variable list>]
[USING <bind variable list>];
You might try the code below, then:
DECLARE
-- your tab1 may contain some insignificant columns with values (especially huge ones e.g. CLOB/BLOB)
-- which will extremely affect procedure's efficiency, so it's a better practice to select only those values that we need
TYPE t_tab_rec IS RECORD (
mfmt_mat_bnr tab_name.mfmt_mat_bnr%TYPE
,mfmt_mat_type tab_name.mfmt_mat_type%TYPE
,mfmt_be_seg tab_name.mfmt_be_seg%TYPE
,mfmt_id tab_name.mfmt_id%TYPE
);
TYPE t_tab_arr IS TABLE OF t_tab_rec;
tabb t_tab_arr := NEW t_tab_arr();
TYPE ref_cur IS REF CURSOR RETURN t_tab_rec; -- to assure cursor is returning t_tab_rec type records
c cur_ref;
stmt_select VARCHAR2(1000);
stmt_upadte VARCHAR2(1000);
BEGIN
stmt_select :=
'SELECT
mft.mfmt_mat_bnr
,mft.mfmt_mat_type
,mft.mfmt_be_seg
,mft.mfmt_id
FROM
'||tab1||' mft
WHERE
mft.mfmt_id IS NOT NULL
AND mft.mfmt_flag IS NOT NULL';
OPEN c FOR stmt_select;
FETCH c BULK COLLECT INTO tabb;
CLOSE c;
stmt_upadte :=
'UPDATE '||tab||'
SET mfmt_mat_bnr = :p_mfmt_mat_bnr
,mfmt_mat_type = :p_mfmt_mat_type
,mfmt_be_seg = :p_mfmt_be_seg
WHERE
mfmt_id = :p_mfmt_id';
FOR idx IN tabb.FIRST .. tabb.LAST
LOOP
EXECUTE IMMEDIATE stmt_update USING tabb(idx).mfmt_mat_bnr, tabb(idx).mfmt_mat_type, tabb(idx).mfmt_be_seg, tabb(idx).mfmt_id;
END LOOP;
END;
/

output data, dynamic sql

I have some problem with dynamic SQL.
I created table, after that inserted some data - it works fine.
But i have no idea how to display data. My code:
declare
begin
execute immediate 'create table name(tabl_name varchar2(30),id number)';
execute immediate 'insert into name(tabl_name,id) (select ''something'',id from table3)';
commit;
and now i would like to display name table content. How to do that? Should i use cursor with dynamic sql? Thanks in advance.
You can use cursor to loop through the records:
declare
v_tabl_name varchar2(30);
v_id number;
res_cur SYS_REFCURSOR;
begin
execute immediate 'create table name(tabl_name varchar2(30),id number)';
execute immediate 'insert into name(tabl_name,id) (select ''something'',id from table3)';
open res_cur for 'select tabl_name, id from name';
LOOP
FETCH res_cur INTO v_tabl_name, v_id;
EXIT WHEN res_cur%NOTFOUND;
dbms_output.put_line(v_tabl_name);
dbms_output.put_line(v_id);
END LOOP;
close res_cur;
end;

Executing Dynamic Native SQL with Oracle Table type gives invalid identifier error

We have created Oracle Table type. And we have created an array of it. This is done as we dont know how many values can come which may be too much for a IN clause in sql query.
--Code Snippet -----
create or replace
TYPE "INPUTCODE"
as object
(pc varchar2(100) )
create or replace
TYPE "INPUTCODEARR"
IS TABLE OF inputcode;
create or replace
PROCEDURE "TEST_PROC" (testCodes IN inputcodeArr, timeHorizon IN NUMBER, p_recordset OUT SYS_REFCURSOR)
AS
var_sqlStmt VARCHAR2(4096);
BEGIN
var_sqlStmt := 'select t.a,t.b, t.c';
var_sqlStmt := var_sqlStmt || 'from test t';
if testCodes is not null then
var_sqlStmt := var_sqlStmt || ', table(testCodes) tc';
var_sqlStmt := var_sqlStmt || 'where tc.pc = t.name';
end if;
dbms_output.put_line('Final SQL Statement::' || var_sqlStmt);
open p_recordset for var_sqlStmt;
END TEST_PROC;
All the above ones are compiles and when you run the TEST_PROC procedure with few testCode values it will fail with error Invalid identifier for testCodes.
In the procedure, final sql statement which is printing in my console is correct and when you run this as a static sql statement inside procedure it runs without any error. But inside the dynamic sql it fails.
I tried executing using DYNAMIC_SQL package, but it results in same error.
Also, i tried giving it as a bind variable for 'table(testCodes)'. That also failed.
Please suggest.
You are using dynamic SQL, so you must tell Oracle which word is an identifier and which word is a variable.
Consider the following statement running directly in SQLPlus:
select t.a,t.b, t.c from test t, table(testCodes) tc
It will fail because no object is named testCodes in your DB. You have to tell the SQL engine that testCodes is in fact a variable. You have to do this because you have chosen to use dynamic SQL whereas variable binding is automatic in static SQL.
In most cases, you can bind "object" variables in the same way as standard variables. In PL/SQL there are several ways to do this, for instance with cursors you would use USING:
SQL> DECLARE
2 l_cur SYS_REFCURSOR;
3 l_tab inputcodeArr := inputcodeArr(INPUTCODE('A'), INPUTCODE('B'));
4 l_obj varchar2(100);
5 BEGIN
6 OPEN l_cur FOR 'SELECT pc FROM TABLE(:my_variable)' -- notice the ":"
7 USING l_tab; -- binding by position
8 LOOP
9 FETCH l_cur
10 INTO l_obj;
11 EXIT WHEN l_cur%NOTFOUND;
12 dbms_output.put_line(l_obj);
13 END LOOP;
14 CLOSE l_cur;
15 END;
16 /
A
B
PL/SQL procedure successfully completed
In your case however I wouldn't bother with dynamic SQL since you can open a cursor conditionally:
CREATE OR REPLACE PROCEDURE "TEST_PROC"(testCodes IN inputcodeArr,
timeHorizon IN NUMBER,
p_recordset OUT SYS_REFCURSOR) AS
BEGIN
IF testCodes IS NOT NULL THEN
OPEN p_recordset FOR
SELECT t.a, t.b, t.c FROM test t, TABLE(testCodes) tc
WHERE tc.pc = t.NAME;
ELSE
OPEN p_recordset FOR
SELECT t.a, t.b, t.c FROM test t;
END IF;
END TEST_PROC;
My advice would be to stick with static SQL as long as possible, as it's a lot easier to make mistakes with dynamic SQL.
Update following comment:
If your number of input is not constant and you have to use dynamic SQL because there are many combinations of filters, you can use the following strategy:
CREATE OR REPLACE PROCEDURE "TEST_PROC"(testCodes IN inputcodeArr,
timeHorizon IN NUMBER,
p_recordset OUT SYS_REFCURSOR) AS
l_sql LONG := 'SELECT t.a, t.b, t.c FROM test t WHERE';
BEGIN
-- filter #1
IF testCodes IS NOT NULL THEN
l_sql := l_sql || ' t.name IN (SELECT pc FROM TABLE(:filter1))';
ELSE
l_sql := l_sql || ' :filter1 IS NULL';
END IF;
-- filter #2
IF timeHorizon IS NOT NULL THEN
l_sql := l_sql || ' AND t.horizon = :filter2';
ELSE
l_sql := l_sql || ' AND :filter2 IS NULL';
END IF;
-- open cursor
OPEN p_recordset FOR l_sql USING testCodes, timeHorizon;
END TEST_PROC;
/
I'm making sure that the final SQL will always have the same number of variables in the same order, however each condition where the filter is NULL will be a tautology (NULL IS NULL).

How to add a table name in EXECUTE IMMEDIATE query?

I have one question about "EXECUTE IMMEDIATE".
I have dynamicly changed table name in next plsql statement
DECLARE
TYPE CurTyp IS REF CURSOR;
cur CurTyp;
str1 VARCHAR2(30);
str2 VARCHAR2(30);
table_name VARCHAR2(30);
BEGIN
select data
into table_name
from ref
where o_id = 111
and a_id = 222;
OPEN cur FOR
'select name, sname from :1 b,myobjects a where a.obj_id = b.obj_id'
USING table_name;
LOOP
FETCH cur INTO str1, str2;
EXIT WHEN cur%NOTFOUND;
dbms_output.put_line(str1||str2);
END LOOP;
CLOSE cur;
END
Is it possible to read the result of next Execute Immediate query to cursor?
'select name, sname from :1 b,myobjects a where a.obj_id = b.obj_id'
USING table_name;
Or maybe is there any way to do this?
Thanks in advance.
For object names you must use concatenation, not bind variables.
From the Dynamic SQL chapter of the PL/SQL Language Reference:
The database uses the values of bind variables exclusively and does
not interpret their contents in any way.
Bind variables help with security and performance. But they won't work with objects like tables. If you pass in a table name then Oracle must interpret the contents, which will negate the security and performance benefits.
You can use ref_cursor
see http://docs.oracle.com/cd/B10500_01/appdev.920/a96590/adg09dyn.htm
the example:
CREATE OR REPLACE PROCEDURE query_invoice(
month VARCHAR2,
year VARCHAR2) IS
TYPE cur_typ IS REF CURSOR;
c cur_typ;
query_str VARCHAR2(200);
inv_num NUMBER;
inv_cust VARCHAR2(20);
inv_amt NUMBER;
BEGIN
query_str := 'SELECT num, cust, amt FROM inv_' || month ||'_'|| year
|| ' WHERE invnum = :id';
OPEN c FOR query_str USING inv_num;
LOOP
FETCH c INTO inv_num, inv_cust, inv_amt;
EXIT WHEN c%NOTFOUND;
-- process row here
END LOOP;
CLOSE c;
END;
/
but as #jonearles said, you can't insert the table names as params