I am trying to write a script that will end sessions. I get the SQL command not properly ended when trying to EXECUTE IMMEDIATE and I can't figure out what I am doing wrong and why I am getting the error message. I have removed the ; form the line cur_show_connection and that didn't work as well.
|| ''' IMMEDIATE' sqlstatement
Here is my full code
declare
cursor cur_show_connections is
SELECT 'ALTER SYSTEM DISCONNECT SESSION '''
|| SID
|| ','
|| SERIAL#
|| ''' IMMEDIATE;' sqlstatement
FROM v$session
WHERE machine = 'XXXXX';
sqlstr varchar2(3200) :='';
begin
for rec_show_connections in cur_show_connections loop
sqlstr := sqlstr || rec_show_connections.sqlstatement || chr(13) || chr(10);
--Tried here and this didnt work either
--EXECUTE IMMEDIATE sqlstr;
end loop;
dbms_output.put_line(sqlstr);
EXECUTE IMMEDIATE sqlstr;
--This seems to work hardcoding the values
--EXECUTE IMMEDIATE q'[ALTER SYSTEM DISCONNECT SESSION '425,7516' IMMEDIATE]';
end;
Any help would be appreciated.
Don't end your ALTER SYSTEM command with a semi-colon. The EXECUTE IMMEDIATE command will take care of that.
cursor cur_show_connections is
SELECT 'ALTER SYSTEM DISCONNECT SESSION '''
|| SID
|| ','
|| SERIAL#
|| ''' IMMEDIATE' sqlstatement
FROM v$session
WHERE machine = 'XXXXX';
You're also concatenating your commands into a single EXECUTE IMMEDIATE statement. That won't work the way you're doing it: EXECUTE IMMEDIATE can only run one command or PL/SQL block at a time.
for rec_show_connections in cur_show_connections loop
dbms_output.put_line(rec_show_connections.sqlstatement);
EXECUTE IMMEDIATE rec_show_connections.sqlstatement;
end loop;
OR
sqlstr = 'BEGIN ';
for rec_show_connections in cur_show_connections loop
sqlstr := sqlstr || dbms_output.put_line(rec_show_connections.sqlstatement) || '; ';
end loop;
sqlstr := sqlstr || ' END;'
EXECUTE IMMEDIATE sqlstr;
Depending on how many sessions you might need to disconnect, the first option is less likely to have issues with character string size limitations. PL/SQL has a 32K limitation that could affect how big a single PL/SQL concatenated block could get, potentially throwing an error if you tried to kill too many sessions at once.
Related
I have a script that I need to use to deploy some changes in the database. For it I need to kill all sessions before to start the changes.
set serveroutput on;
begin
for rec in (SELECT se.sid,se.serial#,se.inst_id FROM gv$session se where type <> 'BACKGROUND' and username not in ('SYSRAC','SYS') and sid <> (select sys_context('userenv','sid') from dual) and status <> 'KILLED')
loop
execute immediate 'Alter System Kill Session '''|| rec.Sid|| ',' || rec.Serial# || ',#' ||rec.inst_id||''' IMMEDIATE';
dbms_output.put_line('Eliminada: '||rec.sid);
end loop;
end;
/
The problem is: I have a database with more than 3000 connections. While this script is executing it is normal for some sessions to disconnect by themselves and it result in the following error:
ERROR at line 1:
ORA-00030: User session ID does not exist.
ORA-06512: at line 4
ORA-06512: at line 4
How can I control the loop to ignore the sessions that disconnect from the database for themselves?
I would do this:
set serveroutput on;
declare
sess_not_exist exception;
pragma exception_init(sess_not_exist, -30);
begin
for rec in (SELECT se.sid,se.serial#,se.inst_id FROM gv$session se where type <> 'BACKGROUND' and username not in ('SYSRAC','SYS')
and sid <> (select sys_context('userenv','sid') from dual) and status <> 'KILLED')
loop
begin
execute immediate 'Alter System Kill Session '''|| rec.Sid|| ',' || rec.Serial# || ',#' ||rec.inst_id||''' IMMEDIATE';
dbms_output.put_line('Eliminada: '||rec.sid);
exception
when sess_not_exist then null;
when others then raise;
end;
end loop;
end;
/
The reasons:
if the session no longer exists between the cursor and the execution, you should not raise an error
you must encapsulate the unit which runs the execute immediate to treat the exception in any entry in the loop, hence the double begin end section.
I think you want to raise any other exception, therefore I put when others then raise.
oracle execute immediate command concatenation. e.g.
BEGIN EXECUTE IMMEDIATE 'DROP TABLE Foo'; END;
works.
BEGIN EXECUTE IMMEDIATE '''DROP TABLE ' || tableName || ''''; END;
BEGIN EXECUTE IMMEDIATE '''DROP TABLE ' || 'Foo'''; END;
not working.
Table name is an input parameter in stored procedure.
Your single quotes are improper.
Use following:
BEGIN EXECUTE IMMEDIATE 'DROP TABLE ' || tableName; END;
BEGIN EXECUTE IMMEDIATE 'DROP TABLE ' || 'Foo'; END;
Cheers!!
I need to assign a value to variable from select query output and call the variable into sql commands
For eg: I get PDB_NAME from v$pdbs and assign value to v_pdb
I want to use v_pdb in multiple sql commands to run against PDBs
I tried to assign value from SELECT query to v_pdb and call the v_pdb in 'alter session set container=v_pdb';, it looks like working, but i get ORA-00922: missing or invalid option error
set serveroutput on;
declare
v_sql varchar2(80);
v_pdb varchar2(30);
BEGIN
FOR pdb IN (select name from v$pdbs where con_id=3 and OPEN_MODE='READ WRITE')
LOOP
v_sql := 'alter session set container='||pdb.name;
DBMS_OUTPUT.PUT_LINE('Executing: ' || v_sql);
--execute immediate 'alter session set container='||pdb.name||';';
execute immediate v_sql;
--v_sql := 'show con_name';
--execute immediate 'show con_name';
--execute immediate v_sql;
v_sql := 'create tablespace APPDATA datafile '+DATA' size 1G autoextend on next 100M maxsize 5G ENCRYPTION USING 'AES256' DEFAULT STORAGE (ENCRYPT)';
DBMS_OUTPUT.PUT_LINE('Executing: ' || v_sql);
execute immediate v_sql;
v_sql := 'drop user bigschema cascade';
DBMS_OUTPUT.PUT_LINE('Executing: ' || v_sql);
--execute immediate 'drop user bigschema cascade';
execute immediate v_sql;
v_sql := 'create user bigschema identified by B67_kuca_ecdf default tablespace APPDATA temporary tablespace TEMP profile DEFAULT account unlock';
DBMS_OUTPUT.PUT_LINE('Executing: ' || v_sql);
execute immediate v_sql;
v_sql := 'alter user bigschema quota unlimited on APPDATA';
DBMS_OUTPUT.PUT_LINE('Executing: ' || v_sql);
execute immediate v_sql;
v_sql := 'grant dba to bigschema';
DBMS_OUTPUT.PUT_LINE('Executing: ' || v_sql);
execute immediate v_sql;
v_sql := 'conn bigschema/"B67_kuca_ecdf"#'||pdb.name;
DBMS_OUTPUT.PUT_LINE('Executing: ' || v_sql);
execute immediate v_sql;
v_sql := 'drop table MV2OCI';
DBMS_OUTPUT.PUT_LINE('Executing: ' || v_sql);
execute immediate v_sql;
v_sql := 'create table MV2OCI tablespace APPDATA as select * from dba_objects';
DBMS_OUTPUT.PUT_LINE('Executing: ' || v_sql);
execute immediate v_sql;
v_sql := 'alter table MV2OCI nologging';
DBMS_OUTPUT.PUT_LINE('Executing: ' || v_sql);
execute immediate v_sql;
v_sql := 'show user';
DBMS_OUTPUT.PUT_LINE('Executing: ' || v_sql);
execute immediate v_sql;
v_sql := 'insert into MV2OCI select * from dba_objects';
DBMS_OUTPUT.PUT_LINE('Executing: ' || v_sql);
execute immediate v_sql;
v_sql := 'insert into MV2OCI select * from MV2OCI';
DBMS_OUTPUT.PUT_LINE('Executing: ' || v_sql);
execute immediate v_sql;
END LOOP;
END;
/
I simply want to get the value for variable v_pdb from "select name from v$pdbs where con_id=3 and OPEN_MODE='READ WRITE'"
And call the v_pdb as follows:
alter session set container=v_pdb;
run other sql commands
...
......
I believe the problem is in the trailing semi-colon in your dynamic SQL. Dynamic SQL does not include a trailing semi-colon -- since the dynamic SQL is a single statement, no statement-separator is required.
After dropping the trailing semi-colon (and the "show" command (a client command)) this works ok. But I don't know of a good way to get DBMS_OUTPUT going unless you are already in a given PDB. That has been dropped in this example.
declare
v_sql varchar2(80);
BEGIN
FOR pdb IN (select name from v$pdbs where con_id=3 and OPEN_MODE='READ WRITE')
LOOP
v_sql := 'alter session set container='||pdb.name;
execute immediate V_SQL;
DBMS_OUTPUT.ENABLE;
v_sql := 'CREATE TABLE TEST_TABLE(LOREM_IPSUM NUMBER)';
execute immediate V_SQL;
END LOOP;
END;
/
Result:
PL/SQL procedure successfully completed.
Navigating over to the PDB, TEST_TABLE now exists there.
I do not think that it actually has anything to do with your pdb variable...
When you use execute immediate you can not have a ; in the string
So for each of your execute immediate statments remove the ; eg
execute immediate 'alter session set container='||pdb.name||';';
becomes
execute immediate 'alter session set container='||pdb.name;
There are several ways to improve the code and the coding process:
Exclude statement terminators from dynamic SQL: As others have mentioned, remove the ; from the end of SQL statements used in dynamic SQL.
Escape strings: Strings in strings need to be escaped. The string 'DATA' should be ''DATA''.
Pay attention to the full error message: Always display the entire error message, including the line number and column number. That information points exactly to the problem.
Use the smallest possible example: A smaller example would have less errors, making it easier to find the real problem. And in the process of simplifying the example you will likely find the answer yourself.
I have a query string that have execute immediate.
how can I Execute Immediate this PL/SQL?
query string = 'Execute immediate select ....';
Want do this: Execute immediate 'query string';
That became this: Execute immediate 'Execute immediate select ....;';
Do you know How can I do this?
Without commenting on whether it is normal or wise: yes, I believe you can do it. That is, I've never seen anything written about EXECUTE IMMEDIATE that suggested it is not re-entrant. Plus, it works if you try it.
Here is a simple, typical EXECUTE IMMEDIATE call:
DECLARE
l_count NUMBER;
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) INTO :l_count FROM DBA_OBJECTS WHERE ROWNUM <= 100' INTO l_count;
DBMS_OUTPUT.PUT_LINE ('l_count = ' || l_count);
END;
Here is basically the same thing, but with EXECUTE IMMEDIATE calls nested to two levels:
DECLARE
l_outer_count NUMBER;
BEGIN
EXECUTE IMMEDIATE q'!
BEGIN
EXECUTE IMMEDIATE 'SELECT COUNT(*) INTO :x FROM DBA_OBJECTS WHERE ROWNUM <= 100' INTO :l_outer_count;
END;
!'
USING IN OUT l_outer_count;
DBMS_OUTPUT.PUT_LINE('l_outer_count = ' || l_outer_count);
END;
I have never encountered a need to do this.
I have a requirement to run a SP across multiple DBs at the same time and one part of it is to eliminate some duplicate records from each DB. Now since the SP can be run multiple times i've included a backup table and and what's needed to truncate and drop it in case the SP is run twice in a row.
Now, since i'm creating tables via DBLINK i've researched that i need to use dbms_utility.exec_ddl_statement - but in this case even though the procedure executes, the truncate and drop queries seem to do nothing, because when i run the SP the second time it fails telling me the name of the backup table is already in use (even though i've included the DROP execution before CREATE).
loop
fetch v_data into v_database_name;
exit when v_data%NOTFOUND;
sql_update := 'BEGIN'
||' EXECUTE IMMEDIATE ''truncate table iSecurity2_dupes_bak'';'
||' EXCEPTION'
||' WHEN OTHERS THEN'
||' IF SQLCODE != -942 THEN'
||' RAISE;'
||' END IF;'
||' END;';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_database_name||'(:sql_update); end;' using sql_update;
commit;
sql_update := 'BEGIN'
||' EXECUTE IMMEDIATE ''DROP TABLE iSecurity2_dupes_bak'';'
||' EXCEPTION'
||' WHEN OTHERS THEN'
||' IF SQLCODE != -942 THEN'
||' RAISE;'
||' END IF;'
||' END;';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_database_name||'(:sql_update); end;' using sql_update;
commit;
sql_update := 'create table iSecurity2_dupes_bak as select * from iSecurity2';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_database_name||'(:sql_update); end;' using sql_update;
commit;
.................
ORA-00955: name is already used by an existing object
ORA-06512: at "SYS.DBMS_UTILITY", line 478
ORA-06512: at line 1
ORA-06512: at "database.procedure_name", line 53
ORA-06512: at line 2
The rest of the cursor which includes deletes, inserts, updates and creation of GLOBAL TEMP tables seems to work just fine and everything executes. If i manually delete the backup table even the CREATE that fails executes.
I'm perplexed :(
UPDATE 08/12/2016
With the help provided by #Jon Heller I was able to transform my code in the below which works as long as i use a static name for the DB_LINK. But when i try to use the variable it fails.
Tried both of the below versions but stil lcan't get to run however i keep modifying them - am i missing something here?
Note: Now, i added the alter session because without it, re-running the original procedure kept failing due to ORA-04062: timestamp of procedure "cw_drop_table" has been changed;
1st version
loop
fetch v_data into v_database_name;
exit when v_data%NOTFOUND;
sql_update := 'alter session set REMOTE_DEPENDENCIES_MODE=SIGNATURE';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_database_name||'(:sql_update); end;' using sql_update;
commit;
begin
dbms_utility.exec_ddl_statement#v_database_name (
q'[
create or replace procedure cw_drop_table is sql_drop varchar2(2000);
begin
sql_drop := 'BEGIN'
||' EXECUTE IMMEDIATE ''DROP TABLE iSecurity2_dupes_bak'';'
||' EXCEPTION'
||' WHEN OTHERS THEN IF SQLCODE != -942 THEN NULL; END IF; END;';
execute immediate sql_drop;
commit;
end; ]' );
execute immediate 'begin cw_drop_table#'||v_database_name||'; end;';
end;
sql_update := 'create table iSecurity2_dupes_bak as select * from iSecurity2';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_database_name||'(:sql_update); end;' using sql_update;
commit;
PLS-00352: Unable to access another database 'V_DATABASE_NAME'
PLS-00201: identifier 'DBMS_UTILITY#V_DATABASE_NAME' must be declared
PL/SQL: Statement ignored
2nd version
loop
fetch v_data into v_database_name;
exit when v_data%NOTFOUND;
sql_update := 'alter session set REMOTE_DEPENDENCIES_MODE=SIGNATURE';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_database_name||'(:sql_update); end;' using sql_update;
commit;
declare v_db_name varchar2(100);
begin select v_database_name into v_db_name from dual;
execute immediate 'dbms_utility.exec_ddl_statement#'||v_db_name||' ('
||' q''[ '
||' create or replace procedure cw_drop_table is sql_drop varchar2(2000);'
||' begin '
||' sql_drop := ''BEGIN'' '
||' ||'' EXECUTE IMMEDIATE ''DROP TABLE iSecurity2_dupes_bak'';'' '
||' ||'' EXCEPTION'' '
||' ||'' WHEN OTHERS THEN IF SQLCODE != -942 THEN NULL; END IF; END;''; '
||' execute immediate sql_drop;'
||' commit;'
||' end; ]'' ); '
||' execute immediate ''begin cw_drop_table#'||v_db_name||'; end;''; ';
end;
sql_update := 'create table iSecurity2_dupes_bak as select * from iSecurity2';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_database_name||'(:sql_update); end;' using sql_update;
commit;
PLS-00103: Encountered the symbol "DROP" when expecting one of the following:
* & = - + ; < / > at in is mod remainder not rem
<an exponent (**)> <> or != or ~= >= <= <> and or like LIKE2_
LIKE4_ LIKEC_ between || member SUBMULTISET_
SOLUTION
After much contemplation and a shower i abandoned the above methodology and went with the below. Not sure why i didn't think about it earlier :|
Note: if anyone ever reads through this long-winded question and knows what i did wrong in the 08/12/2016 Update, i am curious to find out :)
loop
fetch v_data into v_database_name;
exit when v_data%NOTFOUND;
sql_update := 'alter session set REMOTE_DEPENDENCIES_MODE=SIGNATURE';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_database_name||'(:sql_update); end;' using sql_update;
commit;
begin
sql_update:='DROP TABLE iSecurity2_dupes_bak';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_database_name||'(:sql_update); end;' using sql_update;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -942 THEN
NULL; -- suppresses ORA-00942 exception
ELSE
RAISE;
END IF;
END;
DBMS_UTILITY.EXEC_DDL_STATEMENT only reliably runs DDL. If you try to run it with a PL/SQL block it will silently fail and not run anything.
This can be demonstrated by running a PL/SQL block that should obviously fail. The code below should generate ORA-01476: divisor is equal to zero. But instead it does nothing.
begin
dbms_utility.exec_ddl_statement#myself(
q'[declare v_test number; begin v_test := 1/0; end;]'
);
end;
/
Use a temporary procedure to run a PL/SQL block remotely. Create the procedure with DBMS_UTILITY.EXEC_DDL_STATEMENT and then call it with native dynamic SQL.
begin
dbms_utility.exec_ddl_statement#myself(
q'[
create or replace procedure test_procedure
is
v_test number;
begin
v_test := 1/0;
end;
]'
);
execute immediate 'begin test_procedure#myself; end;';
end;
/
RESULTS:
ORA-01476: divisor is equal to zero
ORA-06512: at "JHELLER.TEST_PROCEDURE", line 5
ORA-06512: at line 1
ORA-06512: at line 12
I think this behavior is a bug. Oracle should throw an error instead of simply not doing anything.
Welcome to concatenation hell. Strings get messy when they're embedded 4 levels deep. But there are a few things you can do to make life easier:
Use nested alternative-quoting mechanism. For example, q'[ ... ]', inside a q'< ... >', etc.
Use multi-line strings. There's no need to concatenate multiple lines, just use a single string.
Use extra spacing to help identify the start and end of strings. When things get this crazy it's worth putting a string delimiter on a line all by itself, so that everything is easy to line up.
Use REPLACE instead of concatenation.
I re-formatted part of your code using those tips. Stackoverflow does not understand the alternative quoting mechanism, but the strings should look better in a good Oracle SQL editor.
declare
v_db_name varchar2(30) := 'myself';
sql_update varchar2(32767);
begin
execute immediate replace(
q'[
begin
dbms_utility.exec_ddl_statement##DB_NAME#
(
q'<
create or replace procedure cw_drop_table is
sql_drop varchar2(2000);
begin
sql_drop :=
q'{
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE iSecurity2_dupes_bak';
EXCEPTION WHEN OTHERS THEN
IF SQLCODE != -942 THEN
NULL;
END IF;
END;
}';
execute immediate sql_drop;
end;
>'
);
execute immediate 'begin cw_drop_table##DB_NAME#; end;';
end;
]', '#DB_NAME#', v_db_name);
sql_update := 'create table iSecurity2_dupes_bak as select * from iSecurity2';
execute immediate 'begin dbms_utility.exec_ddl_statement#'||v_db_name||
'(:sql_update); end;' using sql_update;
commit;
end;
/