Oracle - Create an index only if not exists - oracle

Is there any way to create indexes in oracle only if they don't exists ?
Something like
CREATE INDEX IF NOT EXISTS ord_customer_ix
ON orders (customer_id);

Add an index only if not exists:
declare
already_exists exception;
columns_indexed exception;
pragma exception_init( already_exists, -955 );
pragma exception_init(columns_indexed, -1408);
begin
execute immediate 'create index ord_customer_ix on orders (customer_id)';
dbms_output.put_line( 'created' );
exception
when already_exists or columns_indexed then
dbms_output.put_line( 'skipped' );
end;

CREATE INDEX IN ORACLE IF NOT EXISTS.
ALTER SESSION SET CURRENT_SCHEMA = PROD_INTG;
DECLARE
INDEX_EXISTS NUMBER;
BEGIN
SELECT COUNT(1)
INTO INDEX_EXISTS
FROM ALL_INDEXES AI,
ALL_IND_COLUMNS AIC
WHERE AI.TABLE_OWNER = 'PROD_INTG'
AND AI.TABLE_NAME = 'PROCESS_APPLICATION'
AND AI.INDEX_NAME = AIC.INDEX_NAME
AND AI.OWNER = AIC.INDEX_OWNER
AND AIC.COLUMN_NAME IN ('PST_CODE', 'PIZ_TYPE_ID');
IF (INDEX_EXISTS) > 0
THEN
DBMS_OUTPUT.PUT_LINE('INDEX EXISTS :');
ELSE
EXECUTE IMMEDIATE 'ALTER SESSION SET CURRENT_SCHEMA = PROD_INTG';
EXECUTE IMMEDIATE 'CREATE INDEX PROD_INTG.IDX_IQC_APPS_IN_PROC_PST_PIZ
ON PROD_INTG.PROCESS_APPLICATION (PST_CODE, PIZ_TYPE_ID) PARALLEL 16';
EXECUTE IMMEDIATE 'ALTER INDEX PROD_INTG.IDX_IQC_APPS_IN_PROC_PST_PIZ NOPARALLEL';
DBMS_OUTPUT.PUT_LINE('INDEX created :');
END IF;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE IN (-2275, -955, -02431, -01430, -01451, -01408)
THEN
NULL;
ELSE
RAISE;
END IF;
END;
/

A script can be used to drop the index if it exists, then create it. With error checking on the drop if exists:
BEGIN
EXECUTE IMMEDIATE 'DROP INDEX ord_customer_ix';
EXCEPTION
WHEN OTHERS
THEN
IF SQLCODE != -955
THEN -- ORA-00955 index does not exist
RAISE;
END IF;
END;
/
CREATE INDEX ord_customer_ix
ON orders (customer_id);
;
This is pretty straight-forward and easy to code and understand.

Related

Procedure not processing 'table exists' exception handler in plsql

I've created a procedure to create backup tables which is failing to pick up the IF statement under the 'table_exists' exception handler. It is meant to drop and recreate the backup table if it already exists AND the do_not_delete column in the user.record table is set to 'NO':
{CREATE OR REPLACE PROCEDURE USER.CREATE_BACKUP_TABLE (t_owner varchar2, t_name varchar2, t_retention number, t_can_delete_tag varchar2) as
/*
*******************************************************************************************************
Object Name: CREATE_BACKUP_TABLE
*/-- Private variables
icnt number;
sqlcmd varchar2(1000);
backup_table varchar2(1000);
l_affx varchar2(30) := 'BKP';
l_date varchar2(30) := to_char(sysdate,'yyyy_mm_dd');
table_exists exception;
table_does_not_exist exception;
pragma exception_init(table_exists, -00955);
pragma exception_init(table_does_not_exist, -00942);
l_delete USER.RECORD.DO_NOT_DELETE%type;
--
--
begin
for tn in (
select table_name, owner
from all_tables
where table_name = t_name
and owner = t_owner
)
LOOP
BEGIN
backup_table := tn.owner||'.'||tn.table_name||'_'||l_date||'_'||l_affx;
sqlcmd := 'create table '||backup_table||' AS SELECT * FROM '||tn.owner||'.'||tn.table_name;
dbms_output.put_line(sqlcmd);
execute immediate sqlcmd;
--
exception
when table_exists then
sqlcmd := 'select do_not_delete into :l_delete from user.record where backup_table = '''||backup_table||'''';
execute immediate sqlcmd;
if l_delete = 'NO' then
sqlcmd := 'drop table '||backup_table||' cascade constraints';
dbms_output.put_line(sqlcmd);
execute immediate sqlcmd;
sqlcmd := 'create table '||backup_table||' AS SELECT * FROM '||tn.owner||'.'||tn.table_name;
execute immediate sqlcmd;
else if l_delete = 'YES' then
dbms_output.put_line('DML Action: Rollback');
Rollback;
end if;
end if;
when others then
dbms_output.put_line('ERROR: Preceeding statement failed with '||substr(sqlerrm(sqlcode),1,120));
dbms_output.put_line('DML Action: Rollback');
Rollback;
end;
END LOOP;
end;
/}
It's not the correct way to get the result from an execute immediate:
use "execute immediate ... returning into..." instead.
And by the way, in your case you don't need to use execute immediate to get the do_not_delete value, you can "select ... into " directly since the name of the table in the FROM clause is not a variable.

Oracle: execute immediate multiple statements

I need to create columns on multiple tables as well as indexes, I need to catch first if columns already exist as to ignore for the multiple executes, is the following the correct way of handling a single exeption for multiple columns adds executes?
----------------------------------
-- Alter column null --
----------------------------------
DECLARE
col_allready_exists EXCEPTION;
PRAGMA EXCEPTION_INIT(col_allready_exists, -01430);
BEGIN
execute immediate 'ALTER TABLE XtkEnumValue ADD iOrder NUMBER(20) DEFAULT 0';
execute immediate 'ALTER TABLE XtkReport ADD iDisabled NUMBER(3) DEFAULT 0';
execute immediate 'ALTER TABLE XtkWorkflow ADD iDisabled NUMBER(3) DEFAULT 0';
execute immediate 'ALTER TABLE XtkRights ADD tsLastModified TIMESTAMP(6) WITH TIME ZONE';
execute immediate 'ALTER TABLE NmsSeedMember ADD tsLastModified TIMESTAMP(6) WITH TIME ZONE';
execute immediate 'ALTER TABLE NmsTrackingUrl ADD tsLastModified TIMESTAMP(6) WITH TIME ZONE';
EXCEPTION
WHEN col_allready_exists THEN
dbms_output.put_line('Already exists, skipping...');
END;
/
----------------------------------
-- Index creation --
----------------------------------
DECLARE
already_exists EXCEPTION;
columns_indexed EXCEPTION;
PRAGMA EXCEPTION_INIT ( already_exists, -955 );
PRAGMA EXCEPTION_INIT (columns_indexed, -1408);
BEGIN
EXECUTE IMMEDIATE 'CREATE INDEX XTKRIGHTS_TSLASTMODIFIED_IDX ON XTKRIGHTS(tsLastModified)';
EXECUTE IMMEDIATE 'CREATE INDEX ER_TSLASTMODIFIED_IDX_CC057ED6 ON NmsSeedMember(tsLastModified)';
EXECUTE IMMEDIATE 'CREATE INDEX RL_TSLASTMODIFIED_IDX_E5F04BF5 ON NmsTrackingUrl(tsLastModified)';
EXCEPTION
WHEN already_exists OR columns_indexed THEN
dbms_output.put_line('Index already exists, skipping...');
END;
/
Update
Is the following also correct?
DECLARE
allready_null EXCEPTION;
object_allready_exists EXCEPTION;
PRAGMA EXCEPTION_INIT(allready_null, -01442);
PRAGMA EXCEPTION_INIT(object_allready_exists, -955);
BEGIN
execute immediate 'ALTER TABLE NMSACTIVECONTACT MODIFY (ISOURCEID NULL)';
execute immediate 'CREATE TABLE NMSACTIVECONTACT_CPY as SELECT * FROM NMSACTIVECONTACT where 1=0';
EXCEPTION
WHEN allready_null THEN
dbms_output.put_line('ISOURCEID is already null, skipping...');
WHEN object_allready_exists THEN
dbms_output.put_line('NMSACTIVECONTACT_CPY already exists, continuing...');
END;
/
No, catch the exception for each column. If you don't then it may fail on the first column and then it will skip all the remaining statements as the exception handling block is processed.
DECLARE
col_allready_exists EXCEPTION;
PRAGMA EXCEPTION_INIT(col_allready_exists, -01430);
BEGIN
BEGIN
execute immediate 'ALTER TABLE XtkEnumValue ADD iOrder NUMBER(20) DEFAULT 0';
EXCEPTION
WHEN col_allready_exists THEN
dbms_output.put_line('Already exists, skipping...');
END;
BEGIN
execute immediate 'ALTER TABLE XtkReport ADD iDisabled NUMBER(3) DEFAULT 0';
EXCEPTION
WHEN col_allready_exists THEN
dbms_output.put_line('Already exists, skipping...');
END;
BEGIN
execute immediate 'ALTER TABLE XtkWorkflow ADD iDisabled NUMBER(3) DEFAULT 0';
EXCEPTION
WHEN col_allready_exists THEN
dbms_output.put_line('Already exists, skipping...');
END;
BEGIN
execute immediate 'ALTER TABLE XtkRights ADD tsLastModified TIMESTAMP(6) WITH TIME ZONE';
EXCEPTION
WHEN col_allready_exists THEN
dbms_output.put_line('Already exists, skipping...');
END;
BEGIN
execute immediate 'ALTER TABLE NmsSeedMember ADD tsLastModified TIMESTAMP(6) WITH TIME ZONE';
EXCEPTION
WHEN col_allready_exists THEN
dbms_output.put_line('Already exists, skipping...');
END;
BEGIN
execute immediate 'ALTER TABLE NmsTrackingUrl ADD tsLastModified TIMESTAMP(6) WITH TIME ZONE';
EXCEPTION
WHEN col_allready_exists THEN
dbms_output.put_line('Already exists, skipping...');
END;
END;
/
I normally put the DDL and exception handling into a procedure and call that for each column to be added:
declare
procedure add_column
( p_table_name user_tab_columns.table_name%type
, p_column_name user_tab_columns.column_name%type
, p_column_spec varchar2 )
as
column_already_exists exception;
pragma exception_init(column_already_exists, -1430);
k_ddl constant long :=
'alter table '||p_table_name||' add '||p_column_name||' '||p_column_spec;
begin
execute immediate k_ddl;
dbms_output.put_line(p_table_name||'.'||p_column_name||' added');
exception
when column_already_exists then
dbms_output.put_line(p_table_name||'.'||p_column_name||' already exists, skipping...');
when others then
raise_application_error(-20000, 'Command failed: '||k_ddl, true);
end;
begin
add_column('XtkEnumValue', 'iOrder', 'number(20) default 0');
add_column('XtkReport', 'iDisabled', 'number(3) default 0');
add_column('XtkWorkflow', 'iDisabled', 'number(3) default 0');
add_column('XtkRights', 'tsLastModified','timestamp(6) with time zone');
add_column('NmsSeedMember', 'tsLastModified','timestamp(6) with time zone');
add_column('NmsTrackingUrl','tsLastModified','timestamp(6) with time zone');
end;
You could use similar logic for modifying columns, adding indexes etc.
If I was adding the same columns to multiple tables, I might construct a cursor For loop using a Cartesian product to generate all the table/column combinations and and a minus to subtract the ones that already exist in user_tab_columns.

Create a table with oracle pl/sql

I get this error ORA-00933: SQL command not properly ended, but I cannot see what the problem is.
Creates the package:
CREATE OR REPLACE PACKAGE "TOOL_PKG" IS
PROCEDURE CREATE_ZTEMP_CLAIMS_TABLES;
END TOOL_PKG;
/
Create a procedure for the package. I get the error trying to run the procedure.
What I want to do is create a new table with an existing table. I need it dynamic because I'll be adding a lot more tables to the list.
CREATE OR REPLACE PACKAGE BODY CUBS_OWNER."TOOL_PKG" IS
PROCEDURE CREATE_NEW_TABLE IS
type array_t is table of varchar2(50) index by pls_integer;
my_table_t array_t;
elem varchar2(50);
c int;
BEGIN
my_table_t (1) := 'MY_TABLE';
elem := my_table_t.first;
while elem is not null loop
select count(*) into c from user_tables where table_name = upper('ZTEST_'||my_table_t(elem));
if c=0 then
Begin
execute immediate 'CREATE TABLE upper(NEWX_'||my_table_t(elem)||') AS SELECT * FROM upper('||my_table_t(elem)||') where rownum=0';
COMMIT;
DBMS_OUTPUT.put_line ('ZTEST_'||my_table_t(elem)||' created.');
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
'tool_pkg.create_new_table...failed creating table '
|| CHR (10)
|| SQLERRM);
END;
end if;
elem := my_table_t.next(elem);
end loop;
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
'tool_pkg.create_new_table...failed creating table '
|| CHR (10)
|| SQLERRM);
END;
END tool_pkg;
/
I had to make sure the methods upper were outside the quotes.
Instead of this
execute immediate 'CREATE TABLE upper(NEWX_'||my_table_t(elem)||') AS SELECT * FROM upper('||my_table_t(elem)||') where rownum=0';
It had to be in this form
execute immediate 'CREATE TABLE '||upper('NEWX_'||my_table_t(elem))||' AS SELECT * FROM '||upper(my_table_t(elem))||' where rownum=0';

Oracle, drop table if it exists AND empty

I need to drop an Oracle table only if it 1) exists AND 2) Is NOT Empty
I wrote this code but if the table does not exist the code does not work:
DECLARE
rec_cnt1 NUMBER :=0;
rec_cnt2 NUMBER :=0;
BEGIN
SELECT COUNT(*) INTO rec_cnt1 FROM ALL_TABLES WHERE TABLE_NAME = 'MyTable';
SELECT num_rows INTO rec_cnt2 FROM USER_TABLES WHERE TABLE_NAME = 'MyTable';
IF rec_cnt1 = 1 THEN
BEGIN
IF rec_cnt2 < 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE MyTable cascade constraints';
END IF;
END;
END IF;
END;
/
What am I doing wrong? Please help.
Many thanks in advance
If you want to drop a table if it exists and empty(as the title of the question states) you could do this as follows:
create or replace procedure DropTableIfEmpty(p_tab_name varchar2)
is
l_tab_not_exists exception;
pragma exception_init(l_tab_not_exists, -942);
l_is_empty number;
l_query varchar2(1000);
l_table_name varchar2(32);
begin
l_table_name := dbms_assert.simple_sql_name(p_tab_name);
l_query := 'select count(*)
from ' || l_table_name ||
' where rownum = 1';
execute immediate l_query
into l_is_empty;
if l_is_empty = 0
then
execute immediate 'drop table ' || l_table_name;
dbms_output.put_line('Table "'|| p_tab_name ||'" has been dropped');
else
dbms_output.put_line('Table "'|| p_tab_name ||'" exists and is not empty');
end if;
exception
when l_tab_not_exists
then dbms_output.put_line('Table "'|| p_tab_name ||'" does not exist');
end;
When you are trying to drop a table, or query a table, which does not exist, Oracle will raise ORA-00942 exception and execution of a pl/sql block halts. We use pragma exception_init statement to associate ORA-00942 exception with our locally defined exception l_tab_not_exists in order to handle it appropriately.
Test case:
SQL> exec droptableifempty('tb_test'); -- tb_test table does not exists
Table "tb_test" does not exist
SQL> create table tb_test(
2 col number
3 );
table TB_TEST created.
SQL> exec droptableifempty('tb_test');
Table "tb_test" has been dropped
As a side note. Before querying num_rows column of [dba][all][user]_tables in order to determine number of rows a table has, you need to gather table statistic by executing dbms_stats.gather_table_stats(user, '<<table_name>>');, otherwise you wont get the actual number of rows.
In PL/SQL it is 'normal' to catch the exception.
If it is the correct exception then continue with the next part of your code.
DECLARE
rec_cnt1 NUMBER :=0;
rec_cnt2 NUMBER :=0;
BEGIN
SELECT COUNT(*) INTO rec_cnt1 FROM ALL_TABLES WHERE TABLE_NAME = 'MyTable';
SELECT num_rows INTO rec_cnt2 FROM USER_TABLES WHERE TABLE_NAME = 'MyTable';
IF rec_cnt1 = 1 THEN
BEGIN
IF rec_cnt2 < 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE MyTable cascade constraints';
END IF;
END;
END IF;
EXCEPTION
DBMS_OUTPUT.PUT_LINE('OH DEAR AN EXCEPTION WAS THROWN DUE TO' || SQLERRM);
DBMS_OUTPUT.PUT_LINE('THE ORACLE CODE IS ' || SQLCODE);
-- if it is the oracle code for no such table, or no data selected
-- everything is fine.
END;
Of course it won't work if the table doesn't exist. Your second select would get a "No data found" exception, and you're not doing any exception handling. At least you should move the second select inside the first IF block. Best to add exception handling.
here is an easy way to solve this problem:
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE [sssss]';
EXCEPTION WHEN OTHERS THEN NULL;
END;

anonymous block in 11g

I need a help on this.
I am trying to create a pl/sql anonymous block and when I run that it shows its completed but it don't run the code. It should give me an error saying name is already used by an existing object. Can someone help me on this.
I am actually creating procedures but just trying this code as a sample.
DECLARE
V_REF_TBL VARCHAR2(100);
V_SQL LONG;
begin
V_REF_TBL :='My_TABLE';
v_SQL :='truncate table '||V_REF_TBL ;
EXECUTE IMMEDIATE V_SQL;
EXECUTE IMMEDIATE 'CREATE TABLE '|| V_REF_TBL ||' parallel 9 nologging pctfree 0 as
select * from dual';
End;
Possibly you're looking for this:
<<my_block>>
Declare
table_name varchar2(30);
counter number;
Begin
table_name := 'my_table';
select count(*)
into counter
from user_tables
where table_name = Upper(Trim(my_block.table_name)) and
dropped = 'NO';
if counter = 0
then
execute immediate 'create table '||table_name||' as ... etc';
else
execute immediate 'truncate table '||table_name;
execute immediate 'insert into '||table_name' select ... etc';
end if;
end my_block;

Resources