I am trying to export available SQL profiles into a staging table before upgrade. Since there are many SQL profiles I am trying to use a PL/SQL block to load all of them in a table.
declare
v_profname varchar2(1000);
cursor c_profname is select name from dba_sql_profiles;
r_profname c_profname%ROWTYPE;
v_sql varchar2(5000);
v_pack varchar2(8000);
begin
open c_profname;
v_sql := 'execute dbms_sqltune.create_stgtab_sqlprof(table_name=>''stg_sql_profiles'',schema_name=>''DBA_UTIL'');';
execute immediate :v_sql;
loop
fetch c_profname into r_profname;
exit when c_profname%NOTFOUND;
dbms_output.put_line('sql profile name:' || r_profname.name);
v_pack := 'execute dbms_sqltune.pack_stgtab_sqlprof(staging_table_name =>''stg_sql_profiles'',profile_name=>''r_profname.name'');';
execute immediate :v_pack;
end loop;
close c_profname;
dbms_output.put_line('All sql profiles exported');
end;
/
In v_pack dynamic SQL I am getting r_profname.name instead of actual profile name.
Need assistance on how to fix this.
Present Output:
sql profile name:SYS_SQLPROF_0245a98adf750000
dynamic sql statement is:execute dbms_sqltune.pack_stgtab_sqlprof(staging_table_name =>'stg_sql_profiles',profile_name=>'r_profname.name');
sql profile name:SYS_SQLPROF_015b877f29480000
dynamic sql statement is:execute dbms_sqltune.pack_stgtab_sqlprof(staging_table_name =>'stg_sql_profiles',profile_name=>'r_profname.name');
sql profile name:SYS_SQLPROF_015b882d68ec0000
dynamic sql statement is:execute dbms_sqltune.pack_stgtab_sqlprof(staging_table_name =>'stg_sql_profiles',profile_name=>'r_profname.name');
All sql profiles exported
Expected:
execute dbms_sqltune.pack_stgtab_sqlprof(staging_table_name =>'stg_sql_profiles',profile_name=>'SYS_SQLPROF_015b882d68ec0000');
The immediate answer is to concatenate in the name:
v_pack := 'execute dbms_sqltune.pack_stgtab_sqlprof(staging_table_name =>''stg_sql_profiles'',profile_name=>''' || r_profname.name || ''');';
or use a bind variable for it; but the execute is wrong (it's a client shorthand for an anonymous block), and you don't need dynamic SQL at all:
loop
fetch c_profname into r_profname;
exit when c_profname%NOTFOUND;
dbms_output.put_line('sql profile name:' || r_profname.name);
dbms_sqltune.pack_stgtab_sqlprof(staging_table_name =>'stg_sql_profiles',profile_name=>r_profname.name);
end loop;
Use a bind variable in the dynamic SQL and a USING clause on the EXECUTE IMMEDIATE to pass the value in:
declare
v_profname varchar2(1000);
cursor c_profname is select name from dba_sql_profiles;
r_profname c_profname%ROWTYPE;
v_sql varchar2(5000);
v_pack varchar2(8000);
begin
open c_profname;
v_sql := 'execute dbms_sqltune.create_stgtab_sqlprof(table_name=>''stg_sql_profiles'',schema_name=>''DBA_UTIL'');';
execute immediate :v_sql;
loop
fetch c_profname into r_profname;
exit when c_profname%NOTFOUND;
dbms_output.put_line('sql profile name:' || r_profname.name);
v_pack := 'execute dbms_sqltune.pack_stgtab_sqlprof(staging_table_name =>''stg_sql_profiles'',profile_name=>:1);';
execute immediate :v_pack USING r_profname.name;
end loop;
close c_profname;
dbms_output.put_line('All sql profiles exported');
end;
/
Related
I'm trying to write queries in Oracle. I wanted to make sure it worked with the correct schema, so I thought the below code would solve my issue. I guess syntax is wrong.
Could you fix it?
DECLARE
v_current_schema varchar2(30);
BEGIN
v_current_schema := SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA');
IF V_CURRENT_SCHEMA <> 'PRODUCTION' THEN
ALTER SESSION SET CURRENT_SCHEMA = "PRODUCTION" ;
END IF;
END;
ORA-06550: row 6, column 13:PLS-00103: Encountered symbol "ALTER", expected one of the following:( start report status go to exit if loop mod empty pragma remove back select update while with <determinant>
Two things
DDL commands must run always by execute immediate in PL/SQL.
You don't need the variable at all, as you can use sys_context directly inside the if-then statement.
Code simplified
declare
begin
if sys_context('USERENV', 'CURRENT_SCHEMA') <> 'PRODUCTION'
then
execute immediate 'ALTER SESSION SET CURRENT_SCHEMA = PRODUCTION ' ;
end if;
end;
It is dynamic SQL you should use:
declare
v_current_schema varchar2(30);
begin
v_current_schema := sys_context('USERENV', 'CURRENT_SCHEMA');
if v_current_schema <> 'PRODUCTION' then
execute immediate 'ALTER SESSION SET CURRENT_SCHEMA = PRODUCTION' ;
end if ;
end;
/
I have a long DDL data migration script which has 15 transaction blocks. For each block, it takes data from one table and then inserts into another table.
But now I have come across a scenario in which the database does not have that table. In this case my script does not compile even.
What I actually want is to check and return early if the table does not exist. If it does, then only continue further execution.
But the script does not even compiles since it has statement which contains a table name which is non-existent in that particular database.
After some googling, I found that we can use those statements in EXECUTE IMMEDIATE block. I have tried this, but haven't been able to get it compiled.
Here is the block -
DECLARE
V_OBJECT_NAME1 VARCHAR2(50);
V_STRING VARCHAR2(1000);
V_CONTINUE Boolean;
BEGIN
V_CONTINUE := true;
SELECT TABLE_NAME INTO V_OBJECT_NAME1 FROM USER_TABLES WHERE TABLE_NAME = 'OOLD_SFWID_CHG_LOG_HEADER';
EXCEPTION WHEN NO_DATA_FOUND THEN
V_CONTINUE := false;
IF V_CONTINUE then
--scubbber code
V_STRING := 'DECLARE
CURSOR C1 IS
SELECT * FROM OOLD_SFWID_CHG_LOG_HEADER;
BEGIN
OPEN C1;
CLOSE C1;
END;';
EXECUTE IMMEDIATE V_STRING
END IF;
END;
Please suggest how can we write it..
I have a question about "dynamic using clause" in execute immediate statement. I need to set dynamically the "execute immediate statement" and the using clause as well. I don't know the table structure, but I know only the name of the table, and I need to do an operation update on it.
So I wrote a function (through user_tab_columns and user user_constraints tables) to set a variable with the update statement and the bind_variable but now I need to set the using clause with the list of variable.
Example:
CREATE TABLE table1
(
rec1 VARCHAR2(10 BYTE) NULL,
rec2 DATE NULL,
rec3 number(9) not null
);
declare
TYPE cur_type IS REF CURSOR;
cur cur_type;
table_list table1%ROWTYPE;
sqlstring varchar2(400);
begin
OPEN cur FOR sqlstring;
LOOP
FETCH cur INTO table_list;
EXIT WHEN cur%NOTFOUND;
sqlstring:=function1('table1');
-- that returns sqlstring:='update table1 set rec1=:1 , rec2=:2 , rec3=:3 where rec_id=:c4';
execute immediate sqlstring using table_list.rec1, table_list.rec2, table_list.rec3, table_list.rec_id;
END LOOP;
close cur;
end;
I need to implement dynamically the list of variables of the cursor table_list.
"execute immediate sqlstring using table_list.rec1, table_list.rec2, table_list.rec3, table_list.rec_id"
Does anybody know how to solve this problem?
Thanks a lot for your replies.
The problem is that I'm assuming I don't know the table's structure and so the list of variables of the cursor table_list table1%ROWTYPE.
So I can't explicit table_list.rec1, table_list.rec2 ... in the using clause.
If I use only table_list as variable
begin
OPEN cur FOR sqlstring;
LOOP
FETCH cur INTO table_list;
EXIT WHEN cur%NOTFOUND;
sqlstring:=function1('table1');
execute immediate sqlstring using table_list;
END LOOP;
close cur;
I got the error:" 00457 Expressions have to be of SQL types"
http://psoug.org/oraerror/PLS-00457.htm
Error Cause:
An expression of wrong type is in USING or dynamic RETURNING clause. In USING or dynamic RETURNING clause, an expression cannot be of non-SQL types such as BOOLEAN, INDEX TABLE, and record.
I need a way to retrive not only the values but also the list of variables of the cursor table_list first.
But maybe it's impossible and I have to find a work around.
If I will find something interesting I will post.
Thankyou.
Try to replace your execute immediate to full use of dbms_sql.
http://docs.oracle.com/cd/B28359_01/appdev.111/b28419/d_sql.htm#i996891
And usefull for you will be bind_array function from this package.
Use dynamic PL/SQL, unless you can re-factor the original statement and just plug the values into it.
declare
v_string constant varchar2(32767) := 'update test1 set a = :1, b = :2';
v_using_string varchar2(32767);
begin
--Create dynamic using string.
--For example, let's say you want to pass in the values "1" for each NUMBER column.
select listagg(1, ',') within group (order by null)
into v_using_string
from user_tab_columns
where table_name = 'TEST1'
and data_type = 'NUMBER';
--Execute the original dynamic SQL, adding the USING string.
execute immediate '
begin
execute immediate '''||v_string||''' using '||v_using_string||';
end;
';
end;
/
You can either use DBMS_SQL package:
open a cursor using dbms_sql.open_cursor
parse the statement using dbms_sql.parse
bind variables in a loop using dbms_sql.bind_variable
execute the statement using dbms_sql.execute
and finally close the cursor using dbms_sql.close_cursor
Or EXECUTE IMMEDIATE of anonymous PL/SQL block, which performs a dynamically created EXECUTE IMMEDIATE (this approach is not suitable for returning data). See Answer of #JonHeller.
Thanks for all,We can create a table dynamically with the help of execute immediate query. But when we create a table it is been created but if i wanted to create table dynamically with dynamic no of columns then question was raised. Actually I had created a table but when I created no of columns along with the table then a lots of errors raised. The following is the code which I had written in the Oracle in a procedure.
declare
no_of_cols number:=&no_of_cols;
colname varchar2(20);
coldata varchar2(20);
i number;
begin
execute immediate 'create table smap1(nam varchar2(10))';
age:='age';
datf:='number'
if(no_of_cols>=2) then
for i in 2..no_of_cols loop
colname:=age;
coldata:=datf;
execute immediate 'alter table smapl add '||colname||' '||coldata;
end loop;
end if;
end;
then this code executing with four columns with same type if no_of_cols is 5.Then i had modified the code and run the plsql program.the program is as follows
declare
no_of_cols number:=&no_of_cols;
colname varchar2(20);
age varchar2(20);
datf varchar2(20);
coldata varchar2(20);
i number;
begin
execute immediate 'create table smap1(nam varchar2(10))';
if(no_of_cols>=2) then
for i in 2..no_of_cols loop
age :=&age;
datf:=&datf;
colname:=age;
coldata:=datf;
execute immediate 'alter table smapl add '||colname||' '||coldata;
end loop;
end if;
end;
The following are the errors which are generated when the above procedure is created
[Error] Execution (13: 19): ORA-06550: line 13, column 19:
PLS-00103: Encountered the symbol ";" when expecting one of the following:
( - + case mod new not null <an identifier>
<a double-quoted delimited-identifier> <a bind variable>
continue avg count current exists max min prior sql stddev
sum variance execute forall merge time timestamp interval
date <a string literal with character set specification>
<a number> <a single-quoted SQL string> pipe
<an alternatively-quoted string literal with character set specification>
<an alternatively
i had done some modifications for the above plsql,then the plsql code will as follows
declare
no_of_cols number:=&no_of_cols;
colname varchar2(20):='&colname';
coldata varchar2(20):='&coldata';
i number;
begin
execute immediate 'create table smap1(nam varchar2(10))';
if(no_of_cols>=2) then
for i in 2..no_of_cols loop
execute immediate 'alter table smapl add '||colname||' '||coldata;
end loop;
end if;
end;
then after executing i am getting the following error and it doenot read column name dynamically
[Error] Execution (1: 1): ORA-02263: need to specify the datatype for this column
ORA-06512: at line 10
You cannot use semicolons in EXECUTE IMMEDIATE for single statements
Here's a quote from the documentation:
Except for multi-row queries, the dynamic string can contain any SQL statement (without the final semicolon) or any PL/SQL block (with the final semicolon).
Remove the semicolon from EXECUTE IMMEDIATE.
execute immediate 'create table smap1(nam varchar2(10));'; -- this is your code
execute immediate 'create table smap1(nam varchar2(10))'; -- correct code, no semicolon at end
But there's another problem.
You need to understand how substitution variables (&variable) works
SQL*Plus will prompt for substituion variables only once: just before the script compiles, before running it. And then the variables are replaced in the script verbatim, after which it will get compiled and executed.
For example, when you run your script, SQL*Plus recognizes that there are two unknown literals (&colname and &coldata), and will prompt for you. If you supply the values 'age', and 'number' for them, SQL*Plus will rewrite the script like this:
declare
-- omitted to add clarity
begin
execute immediate 'create table smap1(nam varchar2(10));';
if(no_of_cols>=2) then
for i in 2..no_of_cols loop
colname:=age;
coldata:=number;
execute immediate 'alter table smapl add '||colname||' '||coldata;
end loop;
end if;
end;
So if you want to assign a string literal to a variable, and you want to get that string from a substitution variable, you need to do this:
colname varchar2(30) := '&colname'; -- notice the single quotes
Assuming you provided 'age' for colname SQL*Plus will happily convert this to:
colname varchar2(30) := 'age';
So, placing a substitution variable inside a loop will not make SQL*Plus repeatedly prompt you for it's value.
For better understanding and analyze you should do it like this:
sqlcmd varhchar(30000);
begin
sqlcmd := 'create table smap1(nam varchar2(10))';
DBMS_OUTPUT.PUT_LINE(sqlcmd);
execute immediate sqlcmd;
if(no_of_cols>=2) then
for i in 2..no_of_cols loop
age :=&age;
datf:=&datf;
colname:=age;
coldata:=datf;
sqlcmd := 'alter table smapl add '||colname||' '||coldata;
DBMS_OUTPUT.PUT_LINE(sqlcmd);
execute immediate sqlcmd;
end loop;
end if;
I've a query that creates a SQL Statement as a field. I want to execute this statement and return the recordset in SSRS report.
select 'select '||FILE_ID||' FILE_ID,'||
ltrim(sys_connect_by_path('REC_FLD_'||FIELD_NUMBER||' "'||FIELD_NAME||'"',','),',')||
' from RESPONSE_DETAILS where FILE_ID=' ||FILE_ID||';'
from (select t.*,count(*) over (partition by FILE_ID) cnt from RESPONSE_METADATA t)
where cnt=FIELD_NUMBER start with FIELD_NUMBER=1
connect by prior FILE_ID=FILE_ID and prior FIELD_NUMBER=FIELD_NUMBER-1
This generates a SQL stetment - however I want this SQL to be executed.
This is an extension of this question.
I've tried to use execute immediate , cursors, dbms_sql but it does not produce output. Using it on toad. All it says is "PL/SQL procedure successfully completed"
Using the following
Declare
sql_stmt VARCHAR2(3000);
l_cursor SYS_REFCURSOR;
TYPE RefCurTyp IS REF CURSOR;
v_cursor RefCurTyp;
CURSOR c1 is
select 'select '||FILE_ID||' FILE_ID,'||
ltrim(sys_connect_by_path('REC_FLD_'||FIELD_NUMBER||' "'||FIELD_NAME||'"',','),',')||
' from RESPONSE_DETAILS where FILE_ID=' ||FILE_ID||';'
from (select t.*,count(*) over (partition by FILE_ID) cnt from RESPONSE_METADATA t)
where cnt=FIELD_NUMBER start with FIELD_NUMBER=1
connect by prior FILE_ID=FILE_ID and prior FIELD_NUMBER=FIELD_NUMBER-1;
BEGIN
open c1;
FETCH C1 into sql_stmt ;
dbms_output.put_line(sql_stmt);
close c1;
EXECUTE IMMEDIATE sql_stmt;
open v_cursor for sql_stmt;
return l_cursor;
close l_cursor ;
END;
An anonymous PL/SQL block cannot return any data to the caller. If you want to return a SYS_REFCURSOR to the calling application, you would need to create a function (or a procedure). For example
CREATE OR REPLACE FUNCTION get_results
RETURN sys_refcursor
IS
l_sql_stmt VARCHAR2(3000);
l_cursor SYS_REFCURSOR;
BEGIN
select 'select '||FILE_ID||' FILE_ID,'||
ltrim(sys_connect_by_path('REC_FLD_'||FIELD_NUMBER||' "'||FIELD_NAME||'"',','),',')||
' from RESPONSE_DETAILS where FILE_ID = ' ||FILE_ID||';'
into l_sql_stmt
from (select t.*,count(*) over (partition by FILE_ID) cnt from RESPONSE_METADATA t)
where cnt=FIELD_NUMBER
start with FIELD_NUMBER=1
connect by prior FILE_ID=FILE_ID
and prior FIELD_NUMBER=FIELD_NUMBER-1;
dbms_output.put_line(l_sql_stmt);
open l_cursor for sql_stmt;
return l_cursor;
END;
I am assuming from your code that you expect your SELECT statement to return a single SQL statement-- your code is fetching only one row from a query that potentially returns multiple SQL statements. I'm assuming that you only fetch one because you only expect the SELECT statement to return one row. Otherwise, since your query lacks an ORDER BY, you are executing arbitrarily one of N SQL statements that your code is generating.
If you are regularly going to be calling this method, you would almost certainly want to use bind variables in your dynamic SQL statement for the file_id rather than generating non-sharable SQL statements. I haven't made that change here.
There is another StackOverflow thread on calling a stored function returning a sys_refcursor from SSRS.