I am trying to use Dynamic query in my code below but getting error (00900. 00000 - "invalid SQL statement"). Kindly suggest where i am mistaking in the code.
create or replace PROCEDURE CreateInsertTmpTable
AS
crttmp VARCHAR2(200);
intrtmp VARCHAR2(200);
printTableValues VARCHAR2(1000);
BEGIN
crttmp := 'CREATE GLOBAL TEMPORARY TABLE my_temp_table ON COMMIT PRESERVE ROWS AS SELECT * FROM VWBLKDATA WHERE 1=0';
EXECUTE IMMEDIATE crttmp;
intrtmp := 'INSERT INTO my_temp_table SELECT * FROM VWBLKDATA';
EXECUTE IMMEDIATE intrtmp;
printTableValues := ' for data in(SELECT * from my_temp_table)
loop
dbms_output.put_line(data.ID);
end loop';
EXECUTE IMMEDIATE printTableValues;
COMMIT;
END CreateInsertTmpTable;
I think you're overdoing the EXECUTE IMMEDIATE; you can run INSERT statements and PL/SQL without them like:
begin
for i in 1..10 loop
insert into test (some_column) values (to_char(i));
end loop;
end;
But anyways, it looks like you're last EXECUTE IMMEDIATE is trying to execute a partial PL/SQL anonymous block; it's missing a "begin" and "end;"
I would suggest just executing the for loop like so:
for data in (SELECT * from my_temp_table)
loop
dbms_output.put_line(data.ID);
end loop;
or else you'll need to add a begin/end around it in the text (and the "end loop" needs a trailing ";"):
printTableValues := 'begin
for data in (SELECT * from my_temp_table)
loop
dbms_output.put_line(data.ID);
end loop;
end;';
Related
I want to dynamically use c in this for loop and rename.
I tried these two below, but that didn't work.
BEGIN
FOR c IN (select name from v$tempfile)
LOOP
EXECUTE IMMEDIATE q'[alter database rename file c to '/tmp/temp03.dbf']';
END LOOP;
END;
BEGIN
FOR c IN (select name from v$tempfile)
LOOP
EXECUTE IMMEDIATE q'[alter database rename file '(|| c.name || )' to '/tmp/temp03.dbf']';
END LOOP;
END;
I didnĀ“t check the alter database sintax but i think the rigth code is:
BEGIN
FOR c IN (select name from v$tempfile)
LOOP
EXECUTE IMMEDIATE 'alter database rename file '''|| c.name || ''' to ''/tmp/temp03.dbf''';
END LOOP;
END;
Note you have to escape quotes with double quotes
I have a function like this (unfortunately, I cannot change it to send one array instead of multiple parameters):
CREATE FUNCTION (p1_ IN OUT VARCHAR2(100), p2_ IN OUT VARCHAR2(100), <...>, p10_ IN OUT VARCHAR2(100)
IS
BEGIN
gather_value(p1_);
gather_value(p2_);
<...>
gather_value(p8_);
END;
To clean it up a little bit, I would prefer to change the body as follows:
IS
stmt VARCHAR2(1000);
BEGIN
FOR i IN 1 .. 8
LOOP
stmt := 'gather_value(p:1_);'
EXECUTE IMMEDIATE stmt USING i;
END LOOP;
END;
However, in that case no such parameters are found.
Is there a way to work it around?
I want to update all the tables having ABC column.Need to skip the tables which doesn't have data.I am having problem in checking the count of the table in a cursor loop.
PLSQL code
create or replace procedure testp is
CURSOR c_testp
IS
SELECT table_name,
column_name
FROM all_tab_columns
WHERE column_name IN('ABC')
ORDER BY table_name;
c int;
BEGIN
FOR table_rec IN c_testp
LOOP
BEGIN
SELECT COUNT(*)
INTO c
FROM table_rec.table_name;
IF(c>0) THEN
query := 'update '||table_rec.table_name||' set '||table_rec.column_name ||'= xyz';
EXECUTE IMMEDIATE query;
COMMIT;
END IF;
EXCEPTION
WHEN no_data_found THEN
dbms_output.put_line('data not found');
WHEN OTHERS THEN
dbms_output.put_line('others');
END;
END LOOP;
END;
In your code, use this:
EXECUTE IMMEDIATE 'SELECT count(*) FROM ' || table_rec.table_name INTO c;
instead of this:
SELECT COUNT(*)
INTO c
FROM table_rec.table_name;
However, as mentioned in comments - there is actually no need to perform that condition check, as no update will be performed when table is empty.
How to read array of n elements dynamically and display the elements in plsql.
Below is my code (i am new to plsql programming)
set serveroutput on
set verify on
declare
type myarray is table of number index by binary_integer;
x myarray;
i pls_integer;
n number;
begin
-- populate array
dbms_output.put_line('Enter number of array elements');
n := &n;
dbms_output.put_line('Enter elements one by one');
for i in 1..n loop
dbms_output.get_lines(&&x(i),n);
end loop;
i :=0;
-- print array
loop
i := i + 1;
begin
dbms_output.put_line(x(i));
exception
when no_data_found then exit;
end;
end loop;
end;
/
quit;
I stumble into the same issue and wanted to try something.
You could do this with a brutal approach that consists in using you shell to call the client (i.e. sqlplus) any time you want to add a value to your array. I wrote a little package that stores the values of the array in a table. Then you can change the way you store the values, but principle would remain the same:
-- this package uses "execute immediate" statements for everything
-- because it assumes the temporary table T_MY_ARRAY can be non-existent.
create or replace package my_array
authid current_user
is
TYPE a_TBL_Number IS TABLE OF Number INDEX BY BINARY_INTEGER;
procedure init;
procedure add(v in number);
procedure display;
function to_var return a_TBL_Number;
end my_array;
/
create or replace package body my_array
is
procedure init is
-- create table if needed, then make sure its empty;
begin
begin
execute immediate 'create table T_MY_ARRAY(c1 number)';
exception when others then
-- dbms_output.put_line(sqlerrm);
-- we're never sure the temp. table already exists. So create it and catch if existing.
null;
end;
execute immediate 'truncate table T_MY_ARRAY';
end init;
procedure add(v in number) is
-- add new value
begin
execute immediate 'insert into T_MY_ARRAY (c1) values ('||v||')';
end add;
function to_var return a_TBL_Number is
-- hand out an array with the values
t_TBL_n a_TBL_Number;
begin
execute immediate 'select c1 from T_MY_ARRAY ' bulk collect into t_TBL_n;
return t_TBL_n;
end to_var;
procedure display is
t_TBL_n a_TBL_Number;
begin
t_TBL_n:=my_array.to_var();
for i in 1..t_TBL_n.count loop
dbms_output.put_line(t_TBL_n(i));
end loop;
end display;
end my_array;
/
Then here is how you could call this from a shell (here it is for an old ksh):
read SQL_CONNECT?"Enter your SQL connection string:"
read n?"Enter number of array elements:"
# first initialize the array:
sqlplus -s ${SQL_CONNECT} <<_EOF
set serveroutput off
set termout off
set feedback off
begin
my_array.init;
end;
/
_EOF
# then loop on the given number of elements
typeset -i10 i=0
while [[ ${i} -lt ${n} ]]
do
i=i+1
echo iter:${i}
read MY_VALUE?"Enter elements one by one:"
sqlplus -s ${SQL_CONNECT} <<_EOF
set serveroutput off
set termout off
set feedback off
begin
my_array.add(${MY_VALUE});
end;
/
commit;
_EOF
done
# at the end, use stored values to display result:
sqlplus -s ${SQL_CONNECT} <<_EOF
set serveroutput on
set feedback off
begin
dbms_output.put_line('---------------');
dbms_output.put_line('your input was:');
my_array.display;
end;
/
_EOF
I have a stored procedure that has following pl/sql block. This block was using select query in for statement but i need to change that static variable to dynamic query. As I changed that it has error. Is there any way to use variable with FOR LOOP in implicit cursor.
declare
sql_query varchar2(32767) := 'select ctlchar ';
kpiNameQuery varchar2(600);
isWg boolean := true;
begin
IF isWG then
kpiNameQuery := 'select distinct KPI_NAME from weeklykpi where kpi_name in (select kpi_wg from auxillary.kpi_types) order by 1';
Else
kpiNameQuery := 'select distinct KPI_NAME from weeklykpi where kpi_name in (select kpi_wg1 from auxillary.kpi_types) order by 1';
End IF;
for KPI_NAME in kpiNameQuery
loop
sql_query := sql_query || ' , min(case when KPI_NAME = '''||x.KPI_NAME||''' then KPI_VALUE end) as '||x.KPI_NAME;
dbms_output.put_line(sql_query);
end loop;
end;
You can achieve similar functionality with the following using cursor
declare
type t_cursor is ref cursor;
c_cursor t_cursor;
l_sql varchar2(512);
l_var number;
begin
l_sql := 'select count(*) from emp'; -- do dynamic check before here for
-- correct sql
open c_cursor for l_sql;
loop
fetch c_cursor
into l_var;
exit When c_cursor%notfound;
DBMS_OUTPUT.put_line ('val '||l_var);
end loop;
close c_cursor;
end;
Unfotunately no, the doc states:
If the dynamic SQL statement is a SELECT statement that returns multiple rows, native dynamic SQL gives you these choices:
Use the EXECUTE IMMEDIATE statement with the BULK COLLECT INTO clause.
Use the OPEN FOR, FETCH, and CLOSE statements.
So you will have to use a REF cursor (or EXECUTE IMMEDIATE and loop over the results).
Incidentally, in your case you could go for static SQL and have comparable performance:
BEGIN
FOR cc IN (SELECT DISTINCT KPI_NAME
FROM weeklykpi
WHERE kpi_name IN (SELECT CASE WHEN l_variable = 1
THEN kpi_wg
ELSE kpi_wg1
END
FROM auxillary.kpi_types) LOOP
ORDER BY 1
-- do something
END LOOP;
END;
You'll have to use some other type than boolean though since it's unknown to SQL.