How do you drop an index only if it exists?
It seems simple but I did found anything on the net.
The idea is to drop it only if it exists, because if not, I will have an error and my process stops.
I found this to find if the index exists:
select index_name
from user_indexes
where table_name = 'myTable'
and index_name='myIndexName'
But I don't know how to put it together with
DROP INDEX myIndexName
Don't check for existence. Try to drop, and capture the exception if necessary...
DECLARE
index_not_exists EXCEPTION;
PRAGMA EXCEPTION_INIT (index_not_exists, -1418);
BEGIN
EXECUTE IMMEDIATE 'drop index foo';
EXCEPTION
WHEN index_not_exists
THEN
NULL;
END;
/
DECLARE
COUNT_INDEXES INTEGER;
BEGIN
SELECT COUNT ( * )
INTO COUNT_INDEXES
FROM USER_INDEXES
WHERE INDEX_NAME = 'myIndexName';
-- Edited by UltraCommit, October 1st, 2019
-- Accepted answer has a race condition.
-- The index could have been dropped between the line that checks the count
-- and the execute immediate
IF COUNT_INDEXES > 0
THEN
EXECUTE IMMEDIATE 'DROP INDEX myIndexName';
END IF;
END;
/
In Oracle, you can't mix both DDL and DML. In order to do so, you need to work it around with the EXECUTE IMMEDIATE statement.
So, first check for the existence of the index.
Second, drop the index through the EXECUTE IMMEDIATE statement.
DECLARE v_Exists NUMBER;
BEGIN
v_Exists := 0;
SELECT 1 INTO v_Exists
FROM USER_INDEXES
WHERE TABLE_NAME LIKE 'myTable'
AND INDEX_NAME LIKE 'myIndexName'
IF v_Exists = 1 THEN
EXECUTE IMMEDIATE "DROP INDEX myIndexName"
ENDIF;
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
This code is out the top of my head and you may need to fix it up a little, but this gives an idea.
Hope this helps! =)
I made a procedure so it can be called several times:
DELIMITER €€
DROP PROCEDURE IF EXISTS ClearIndex€€
CREATE PROCEDURE ClearIndex(IN var_index VARCHAR(255),IN var_table VARCHAR(255))
BEGIN
SET #temp = concat('DROP INDEX ', var_index, ' ON ', var_table);
PREPARE stm1 FROM #temp;
BEGIN
DECLARE CONTINUE HANDLER FOR 1091 SELECT concat('Index ', var_index,' did not exist in ',var_table,', but was handled') AS 'INFO';
EXECUTE stm1;
END;
END €€
DELIMITER ;
Now it can be called more than once:
CALL ClearIndex('employees_no_index','employees');
CALL ClearIndex('salaries_no_index','salaries');
CALL ClearIndex('titles_no_index','titles');
I hope this will help. It's a combination of all solution :)
By the way thanks for the help !
CREATE OR REPLACE PROCEDURE CLEAR_INDEX(INDEX_NAME IN VARCHAR2) AS
BEGIN
EXECUTE IMMEDIATE 'drop index ' || INDEX_NAME;
EXCEPTION
WHEN OTHERS THEN
NULL;
END CLEAR_INDEX;
Related
I'm using the following code for create a column in an existing table, but, I'm getting this error:
ORA-00922: missing or invalid option
I've tried get the desired result (create column in table "only if this column does not exists") without the EXECUTE IMMEDIATE instruction, but, PL/SQL doesn't allow use the ALTER TABLE [...] in an IF [] THEN structure.
Is there something I'm missing?
This is the db<>fiddle sample:
CREATE TABLE "TMP_TABLE_SAMPLE"
( "ID_TABLE" NUMBER(9,0)
) ;
✓
SET SERVEROUTPUT ON;
CLEAR SCREEN;
DECLARE
V_COLUMN_EXISTS NUMBER := 0;
BEGIN
SELECT COUNT(1) CONTEO
INTO V_COLUMN_EXISTS
FROM USER_TAB_COLS
WHERE UPPER(COLUMN_NAME) = 'PNT_NCODE'
AND UPPER(TABLE_NAME) = 'TMP_TABLE_SAMPLE';
IF V_COLUMN_EXISTS = 0 THEN
EXECUTE IMMEDIATE 'ALTER TABLE TMP_TABLE_SAMPLE ADD PNT_NCODE NUMBER (9,0) ' ||
' COMMENT ON COLUMN TMP_TABLE_SAMPLE.PNT_NCODE IS ''Stores ID from TMP_TABLE_SAMPLE_2.''';
ELSE
DBMS_OUTPUT.PUT_LINE('Column already exists');
END IF;
END;
ORA-00922: missing or invalid option
The error you're getting is because you have set serveroutput on and clear screen in your script. db<>fiddle knows how to interpret SQL and PL/SQL. It doesn't support SQL*Plus commands.
If you remove those, the next error you'll get is that you have a single execute immediate statement that is trying to execute two separate statements. Creating the column and adding a comment on the column are separate operations so you need separate statements.
If I change your fiddle to this, it works the way you want
DECLARE
V_COLUMN_EXISTS NUMBER := 0;
BEGIN
SELECT COUNT(1) CONTEO
INTO V_COLUMN_EXISTS
FROM USER_TAB_COLS
WHERE UPPER(COLUMN_NAME) = 'PNT_NCODE'
AND UPPER(TABLE_NAME) = 'TMP_TABLE_SAMPLE';
IF V_COLUMN_EXISTS = 0 THEN
EXECUTE IMMEDIATE 'ALTER TABLE TMP_TABLE_SAMPLE ADD PNT_NCODE NUMBER (9,0) ';
EXECUTE IMMEDIATE 'COMMENT ON COLUMN TMP_TABLE_SAMPLE.PNT_NCODE IS ''Stores ID from TMP_TABLE_SAMPLE_2.''';
ELSE
DBMS_OUTPUT.PUT_LINE('Column already exists');
END IF;
END;
/
I am researching issues regarding oracle. I'm creating stored procedures and boot the following errors I also show them in the picture, I hope to help me resolve the error.
[]
You can add variable v_count number :=0; in your procedure to check if value exists.
Example:
CREATE OR REPLACE PROCEDURE PROCEDURE_NAME(PARAMETERS) IS
V_COUNT NUMBER := 0;
BEGIN
SELECT COUNT(1)
INTO V_COUNT
FROM YOUR_TABLE
WHERE .. .
IF V_COUNT = 0 THEN INSERT ...
ELSIF UPDATE ...
COMMIT;
END IF;
END;
Merge is one way to do this. Another way is
INSERT INTO..
SELECT ....
FROM DUAL
WHERE NOT EXISTS (SELECT * FROM...)
I'm not going to try and transcribe your screenshot
I've got problem with creating function. I'll try explain what the problem is:
The task is about creating function which is based on select first_name from employees where dept_id=10;. New function MUST have v_select_statement (which I wrote) as argument(sth like this: v_select_statement := select first_name from employees where dept_id=10;. Selet like you can see generating list of names. These names should be in new table which is created in this function too. New table should have name e.g new_table.
Problem is that I don't know how to do it. I tried something like this:
create or replace
FUNCTION create_new_tab (v_select_statement VARCHAR2) RETURN NUMBER
is
b first_name.employees%TYPE;
begin
--here i don`t know how to assign select statement to cursor or execute immediate. Any ideas? i tried create sth like:
-- execute immediate 'v_select_statement into b ';
execute immediate 'CREATE TABLE new_tab (i VARCHAR2(50))';
execute immediate 'insert into new_tab values (statement_result)';
--don`t know how and what to put as statement_result. I know that i could use cursor, but ---how in this case?
return 1;
exception
when others then
dbms_output.put_line(SQLERRM);
return 0;
END create_new_tab
;
Can you help me, because I give up. I have no idea how to solve this problem.
Please help me if you can.
What you can do is the following:
create or replace FUNCTION create_new_tab (v_select_statement VARCHAR2) RETURN NUMBER
is
b employees.first_name%TYPE;
cur SYS_REFCURSOR;
begin
OPEN cur for v_select_statement;
execute immediate 'CREATE TABLE new_tab (i VARCHAR2(50))';
LOOP
FETCH cur into b;
EXIT WHEN cur%NOTFOUND;
execute immediate 'insert into new_tab values (:statement_result)' USING b;
END LOOP;
CLOSE cur;
return 1;
exception
when others then
dbms_output.put_line(SQLERRM);
return 0;
END create_new_tab
;
Couldn't test it without the tables but it may point you in the right direction.
But as Avrajit already said, I would suggest creating the new_tab beforehand and then insert into that table normalle without Dynamic-SQL.
You also have to keep in mind, that this function can only be run once, as it will result in an error if you try to create the table when it already exists.
This is a Function created for your referenec.
What i suggest you to create a table beforehand and then
try to insert data into it. Please try this code and let me
know if it works for you. Thanks
Create or replace function test_func return number
as
cursor cr is select name from avrajit
where department='.NET';
name_av avrajit.name%type;
begin
for rec in cr
loop
insert into av_test(dpt) values(rec.name);
return 1;
end loop;
exception
when others then
return 0;
end test_func;
I dont know what is purpose of your function. But if you still want to do try this
create or replace
FUNCTION create_new_tab (v_select_statement VARCHAR2) RETURN NUMBER
is
b first_name.employees%TYPE;
begin
--here i don`t know how to assign select statement to cursor or execute immediate. Any ideas? i tried create sth like:
-- execute immediate 'v_select_statement into b ';
execute immediate 'CREATE TABLE new_tab (i VARCHAR2(50))';
execute immediate 'insert into new_tab values ( ' || v_select_statement || ' )'; --don`t know how and what to put as statement_result. I know that i could use cursor, but ---how in this case?
return 1;
exception
when others then
dbms_output.put_line(SQLERRM);
return 0;
END create_new_tab
;`
Is it possible to have conditional compilation in Oracle, where the condition is the existence of a database object (specifically, a table or view or synonym)? I'd like to be able to do something like this:
sp_some_procedure is
$IF /*check if A exists.*/ then
/* read from and write to A as well as other A-related non-DML stuff...*/
$ELSE /*A doesn't exist yet, so avoid compiler errors*/
dbms_output.put_line('Reminder: ask DBA to create A!')
$ENDIF
end;
Yes it is. Here a sample where the first stored procedure wants to select from XALL_TABLES, but if this table doesn't exist, select from dual. Finally, because I haven't got an XALL_TABLES object, the first stored procedure selects from dual. The second one, does the same thing on the ALL_TABLES object. Because the ALL_TABLES exists, the second stored procedure selects from all_tables but not from DUAL.
This kind of construction is useful where the package have to be deployed on all your database and use tables that are not deployed everywhere ... (ok, perhaps there is a conceptual problem, but it happens).
--conditionals compilation instructions accept only static condition (just with constants)
--passing sql bind variable doesn't work
--To pass a value to a conditional compilation instruction, I bypasses the use of input parameters of the script
--these 4 next lines affect a value to the first and the second input parameter of the script
--If your originally script use input script parameter, use the next free parameter ...
column param_1 new_value 1 noprint
select nvl(max(1), 0) param_1 from all_views where owner = 'SYS' and view_name = 'XALL_TABLES';
column param_2 new_value 2 noprint
select nvl(max(1), 0) param_2 from all_views where owner = 'SYS' and view_name = 'ALL_TABLES';
CREATE or replace PACKAGE my_pkg AS
function test_xall_tables return varchar2;
function test_all_tables return varchar2;
END my_pkg;
/
CREATE or replace PACKAGE BODY my_pkg AS
function test_xall_tables return varchar2 is
vch varchar2(50);
begin
$IF (&1 = 0) $THEN
select 'VIEW XALL_TABLES D''ONT EXISTS' into vch from dual;
$ELSE
select max('VIEW XALL_TABLES EXISTS') into vch from XALL_TABLES;
$END
return vch;
end test_xall_tables;
function test_all_tables return varchar2 is
vch varchar2(50);
begin
$IF (&2 = 0) $THEN
select 'VIEW ALL_TABLES D''ONT EXISTS' into vch from dual;
$ELSE
select max('VIEW ALL_TABLES EXISTS') into vch from ALL_TABLES;
$END
return vch;
end test_all_tables;
END my_pkg;
/
the test :
select my_pkg.test_xall_tables from dual;
give
VIEW XALL_TABLES D'ONT EXISTS
select my_pkg.test_all_tables from dual;
give
VIEW ALL_TABLES EXISTS
I would use 'EXECUTE IMMEDIATE' and a EXCEPTION clause.
Use dynamic SQL to create package constants to track which objects exist, and then use those constants in conditional compilation.
--E.g., say there are two possible tables, but only one of them exists.
--create table table1(a number);
create table table2(a number);
--Create a package with boolean constants to track the objects.
--(Another way to do this is to use ALTER SESSION SET PLSQL_CCFLAGS)
declare
table1_exists_string varchar2(10) := 'true';
table2_exists_string varchar2(10) := 'true';
temp number;
begin
begin
execute immediate 'select max(1) from table1 where rownum <= 1' into temp;
exception when others then
table1_exists_string := 'false';
end;
begin
execute immediate 'select max(1) from table2 where rownum <= 1' into temp;
exception when others then
table2_exists_string := 'false';
end;
execute immediate '
create or replace package objects is
table1_exists constant boolean := '||table1_exists_string||';
table2_exists constant boolean := '||table2_exists_string||';
end;
';
end;
/
--Look at the results in the source:
select * from user_source where name = 'OBJECTS';
--Create the object that refers to the tables.
create or replace function compile_test return varchar2 is
v_test number;
begin
$if objects.table1_exists $then
select max(1) into v_test from table1;
return 'table1 exists';
$elsif objects.table2_exists $then
select max(1) into v_test from table2;
return 'table 2 exists';
$else
return 'neither table exists';
$end
end;
/
--Check the dependencies - only TABLE2 is dependent.
select * from user_dependencies where name = 'COMPILE_TEST';
--Returns 'table 2 exists'.
select compile_test from dual;
Mixing dynamic SQL, dynamic PL/SQL, and conditional compilation is usually a very evil idea. But it will allow you to put all of your ugly dynamic SQL in one installation package, and maintain real dependency tracking.
This may work well in a semi-dynamic environment; for example a program that is installed with different sets of objects but does not frequently change between them.
(Also, if the whole point of this is just to replace scary error messages with friendly warnings, in my opinion that is a very bad idea. If your system is going to fail, the failure should be obvious so it can be immediately fixed. Most people ignore anything that starts with "Reminder...".)
No - that is not possible... but if you create a stored procedure referencing a non-existent DB object and try to compile it the compilation will show errors... the stored procedure will be there but "invalid"... and the compilation errors are accessible for the DBA whenever he looks at it... so I would just go ahead and create all needed stored procedures, if any compilation errors arise ask the DBA (sometimes the object exists but the stored procedure need permissions to access it...)... after the reason for the error(s) is fixed you can just recompile the stored procedure (via ALTER PROCEDURE MySchema.MyProcName COMPILE;) and all is fine...
IF you don't want code to be there you can just DROP the strored procedure and/or replace is via CREATE OR REPLACE... with dbms_output.put_line('Reminder: ask DBA to create A!') in the body.
The only other alternative is as kevin points out EXECUTE IMMEDIATE with proper EXCEPTION handling...
What I would do is check the existence via all_objects, something like:
declare
l_check_sql varchar2(4000);
l_cnt number;
begin
l_check_sql := q'{
select count(1)
from all_objects
where object_name = 'MY_OBJ'
and owner = 'MY_OWNER'
}';
execute immediate l_check_sql into l_cnt;
if (l_cnt > 0) then
-- do something referring to MY_OBJ
else
-- don't refer to MY_OBJ
end if;
end;
Am I correct in understanding that CREATE OR REPLACE basically means "if the object exists, drop it, then create it either way?"
If so, what am I doing wrong? This works:
CREATE TABLE foo (id NUMBER,
title VARCHAR2(4000) DEFAULT 'Default Title')
And this doesn't (ORA-00922: missing or invalid option):
CREATE OR REPLACE TABLE foo (id NUMBER,
title VARCHAR2(4000) DEFAULT 'Default Title')
Am I doing something stupid? I don't seem to be able to find much documentation about this syntax.
This works on functions, procedures, packages, types, synonyms, trigger and views.
Update:
After updating the post for the third time, I'll reformulate this:
This does not work on tables :)
And yes, there is documentation on this syntax, and there are no REPLACE option for CREATE TABLE.
One of the nice things about the syntax is that you can be sure that a CREATE OR REPLACE will never cause you to lose data (the most you will lose is code, which hopefully you'll have stored in source control somewhere).
The equivalent syntax for tables is ALTER, which means you have to explicitly enumerate the exact changes that are required.
EDIT:
By the way, if you need to do a DROP + CREATE in a script, and you don't care for the spurious "object does not exist" errors (when the DROP doesn't find the table), you can do this:
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE owner.mytable';
EXCEPTION
WHEN OTHERS THEN
IF sqlcode != -0942 THEN RAISE; END IF;
END;
/
There is no create or replace table in Oracle.
You must:
DROP TABLE foo;
CREATE TABLE foo (....);
CREATE OR REPLACE can only be used on functions, procedures, types, views, or packages - it will not work on tables.
Following script should do the trick on Oracle:
BEGIN
EXECUTE IMMEDIATE 'drop TABLE tablename';
EXCEPTION
WHEN OTHERS THEN
IF sqlcode != -0942 THEN RAISE;
END IF;
END;
-- To Create or Replace a Table we must first silently Drop a Table that may not exist
DECLARE
table_not_exist EXCEPTION;
PRAGMA EXCEPTION_INIT (table_not_exist , -00942);
BEGIN
EXECUTE IMMEDIATE('DROP TABLE <SCHEMA>.<TABLE NAME> CASCADE CONSTRAINTS');
EXCEPTION WHEN table_not_exist THEN NULL;
END;
/
Does not work with Tables, only functions etc.
Here is a site with some examples.
A usefull procedure for oracle databases without using exeptions (under circumstances you have to replace user_tables with dba_tables and/or constrain the tablespace in the query):
create or replace procedure NG_DROP_TABLE(tableName varchar2)
is
c int;
begin
select count(*) into c from user_tables where table_name = upper(tableName);
if c = 1 then
execute immediate 'drop table '||tableName;
end if;
end;
If you are doing in code then first check for table in database
by using query
SELECT table_name
FROM user_tables
WHERE table_name = 'XYZ'
if record found then truncate table otherwise create Table
Work like Create or Replace.
You can use CORT (www.softcraftltd.co.uk/cort). This tool allows to CREATE OR REPLACE table in Oracle.
It looks like:
create /*# or replace */ table MyTable(
... -- standard table definition
);
It preserves data.
So I've been using this and it has worked very well: - it works more like a DROP IF EXISTS but gets the job done
DECLARE
VE_TABLENOTEXISTS EXCEPTION;
PRAGMA EXCEPTION_INIT(VE_TABLENOTEXISTS, -942);
PROCEDURE DROPTABLE(PIS_TABLENAME IN VARCHAR2) IS
VS_DYNAMICDROPTABLESQL VARCHAR2(1024);
BEGIN
VS_DYNAMICDROPTABLESQL := 'DROP TABLE ' || PIS_TABLENAME;
EXECUTE IMMEDIATE VS_DYNAMICDROPTABLESQL;
EXCEPTION
WHEN VE_TABLENOTEXISTS THEN
DBMS_OUTPUT.PUT_LINE(PIS_TABLENAME || ' NOT EXIST, SKIPPING....');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
RAISE;
END DROPTABLE;
BEGIN
DROPTABLE('YOUR_TABLE_HERE');
END DROPTABLE;
/
Hope this helps
Also reference:
PLS-00103 Error in PL/SQL Developer
'Create or replace table' is not possible. As others stated, you can write a procedure and/or use begin execute immediately (...). Because I don't see an answer with how to (re)create the table, I putted a script as an answer.
PS: in line of what jeffrey-kemp mentioned: this beneath script will NOT save data that is already present in the table you are going to drop. Because of the risk of loosing data, at our company it is only allowed to alter existing tables on the production environment, and it is not allowed to drop tables. By using the drop table statement, sooner or later you will get the company police standing at your desk.
--Create the table 'A_TABLE_X', and drop the table in case it already is present
BEGIN
EXECUTE IMMEDIATE
'
CREATE TABLE A_TABLE_X
(
COLUMN1 NUMBER(15,0),
COLUMN2 VARCHAR2(255 CHAR),
COLUMN3 VARCHAR2(255 CHAR)
)';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -955 THEN -- ORA-00955: object name already used
EXECUTE IMMEDIATE 'DROP TABLE A_TABLE_X';
END IF;
END;
I would do something like this
begin
for i in (select table_name from user_tables where table_name = 'FOO') loop
execute immediate 'drop table '||i.table_name;
end loop;
end;
execute immediate 'CREATE TABLE FOO (id NUMBER,
title VARCHAR2(4000)) ';
If this is for MS SQL.. The following code will always run no matter what if the table exist already or not.
if object_id('mytablename') is not null //has the table been created already in the db
Begin
drop table mytablename
End
Create table mytablename (...