Oracle: если существует таблица
Я пишу несколько сценариев миграции для базы данных Oracle и надеялся, что Oracle имеет нечто похожее на конструкцию MySQL IF EXISTS
.
В частности, всякий раз, когда я хочу сбросить таблицу в MySQL, я делаю что-то вроде
DROP TABLE IF EXISTS `table_name`;
Таким образом, если таблица не существует, DROP
не вызывает ошибку, а script может продолжаться.
Есть ли у Oracle аналогичный механизм? Я понимаю, что могу использовать следующий запрос, чтобы проверить, существует ли таблица или нет.
SELECT * FROM dba_tables where table_name = 'table_name';
но синтаксис привязки, который вместе с a DROP
, ускользает от меня.
Ответы
Ответ 1
Лучший и наиболее эффективный способ - исключить исключение "table not found": это позволяет избежать накладных расходов на проверку, существует ли таблица дважды; и не страдает от проблемы, что, если DROP сбой по какой-то другой причине (что может быть важно), исключение все равно возникает для вызывающего:
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE mytable';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
ДОПОЛНЕНИЕ
Для справки, здесь приведены эквивалентные блоки для других типов объектов:
Последовательность
BEGIN
EXECUTE IMMEDIATE 'DROP SEQUENCE ' || sequence_name;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -2289 THEN
RAISE;
END IF;
END;
Вид
BEGIN
EXECUTE IMMEDIATE 'DROP VIEW ' || view_name;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
Триггер
BEGIN
EXECUTE IMMEDIATE 'DROP TRIGGER ' || trigger_name;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -4080 THEN
RAISE;
END IF;
END;
Индекс
BEGIN
EXECUTE IMMEDIATE 'DROP INDEX ' || index_name;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -1418 THEN
RAISE;
END IF;
END;
Столбец
BEGIN
EXECUTE IMMEDIATE 'ALTER TABLE ' || table_name
|| ' DROP COLUMN ' || column_name;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -904 THEN
RAISE;
END IF;
END;
Ссылка на базу данных
BEGIN
EXECUTE IMMEDIATE 'DROP DATABASE LINK ' || dblink_name;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -2024 THEN
RAISE;
END IF;
END;
Материализованный вид
BEGIN
EXECUTE IMMEDIATE 'DROP MATERIALIZED VIEW ' || mview_name;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -12003 THEN
RAISE;
END IF;
END;
Тип
BEGIN
EXECUTE IMMEDIATE 'DROP TYPE ' || type_name;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -4043 THEN
RAISE;
END IF;
END;
Ограничение
BEGIN
EXECUTE IMMEDIATE 'ALTER TABLE ' || table_name
|| ' DROP CONSTRAINT ' || constraint_name;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -2443 THEN
RAISE;
END IF;
END;
Задание планировщика
BEGIN
DBMS_SCHEDULER.drop_job(job_name);
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -27475 THEN
RAISE;
END IF;
END;
Пользователь/схема
BEGIN
EXECUTE IMMEDIATE 'DROP USER ' || user_name;
/* you may or may not want to add CASCADE */
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -1918 THEN
RAISE;
END IF;
END;
Пакет
BEGIN
EXECUTE IMMEDIATE 'DROP PACKAGE ' || package_name;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -4043 THEN
RAISE;
END IF;
END;
Процедура
BEGIN
EXECUTE IMMEDIATE 'DROP PROCEDURE ' || procedure_name;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -4043 THEN
RAISE;
END IF;
END;
Функция
BEGIN
EXECUTE IMMEDIATE 'DROP FUNCTION ' || function_name;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -4043 THEN
RAISE;
END IF;
END;
Табличное
BEGIN
EXECUTE IMMEDIATE 'DROP TABLESPACE' || tablespace_name;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -959 THEN
RAISE;
END IF;
END;
Ответ 2
declare
c int;
begin
select count(*) into c from user_tables where table_name = upper('table_name');
if c = 1 then
execute immediate 'drop table table_name';
end if;
end;
Это для проверки наличия таблицы в текущей схеме.
Для проверки того, существует ли данная таблица в другой схеме, вам нужно использовать all_tables
вместо user_tables
и добавить условие all_tables.owner = upper('schema_name')
Ответ 3
Я искал то же самое, но в итоге я написал процедуру, чтобы помочь мне:
CREATE OR REPLACE PROCEDURE DelObject(ObjName varchar2,ObjType varchar2)
IS
v_counter number := 0;
begin
if ObjType = 'TABLE' then
select count(*) into v_counter from user_tables where table_name = upper(ObjName);
if v_counter > 0 then
execute immediate 'drop table ' || ObjName || ' cascade constraints';
end if;
end if;
if ObjType = 'PROCEDURE' then
select count(*) into v_counter from User_Objects where object_type = 'PROCEDURE' and OBJECT_NAME = upper(ObjName);
if v_counter > 0 then
execute immediate 'DROP PROCEDURE ' || ObjName;
end if;
end if;
if ObjType = 'FUNCTION' then
select count(*) into v_counter from User_Objects where object_type = 'FUNCTION' and OBJECT_NAME = upper(ObjName);
if v_counter > 0 then
execute immediate 'DROP FUNCTION ' || ObjName;
end if;
end if;
if ObjType = 'TRIGGER' then
select count(*) into v_counter from User_Triggers where TRIGGER_NAME = upper(ObjName);
if v_counter > 0 then
execute immediate 'DROP TRIGGER ' || ObjName;
end if;
end if;
if ObjType = 'VIEW' then
select count(*) into v_counter from User_Views where VIEW_NAME = upper(ObjName);
if v_counter > 0 then
execute immediate 'DROP VIEW ' || ObjName;
end if;
end if;
if ObjType = 'SEQUENCE' then
select count(*) into v_counter from user_sequences where sequence_name = upper(ObjName);
if v_counter > 0 then
execute immediate 'DROP SEQUENCE ' || ObjName;
end if;
end if;
end;
Надеюсь, что это поможет
Ответ 4
просто хотел опубликовать полный код, который создаст таблицу, и отбросит ее, если она уже существует с использованием кода Джеффри (к нему относится, а не я!).
BEGIN
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE tablename';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
END;
EXECUTE IMMEDIATE 'CREATE TABLE tablename AS SELECT * FROM sourcetable WHERE 1=0';
END;
Ответ 5
С SQL * PLUS вы также можете использовать команду WHENEVER SQLERROR:
WHENEVER SQLERROR CONTINUE NONE
DROP TABLE TABLE_NAME;
WHENEVER SQLERROR EXIT SQL.SQLCODE
DROP TABLE TABLE_NAME;
С CONTINUE NONE
сообщается об ошибке, но script будет продолжен. С EXIT SQL.SQLCODE
script будет завершен в случае ошибки.
см. также: ДОПОЛНИТЕЛЬНЫЕ ДОКУМЕНТЫ SQLERROR
Ответ 6
В oracle нет "DROP TABLE IF EXISTS", вам нужно будет сделать оператор select.
попробуйте это (я не в синтаксисе оракула, поэтому, если мои переменные являются ify, пожалуйста, простите меня):
declare @count int
select @count=count(*) from all_tables where table_name='Table_name';
if @count>0
BEGIN
DROP TABLE tableName;
END
Ответ 7
Другой метод - это определить исключение, а затем только поймать это исключение, позволяющее всем другим распространяться.
Declare
eTableDoesNotExist Exception;
PRAGMA EXCEPTION_INIT(eTableDoesNotExist, -942);
Begin
EXECUTE IMMEDIATE ('DROP TABLE myschema.mytable');
Exception
When eTableDoesNotExist Then
DBMS_Output.Put_Line('Table already does not exist.');
End;
Ответ 8
Вы всегда можете поймать ошибку самостоятельно.
begin
execute immediate 'drop table mytable';
exception when others then null;
end;
Считается, что плохой практикой является чрезмерное использование этого, подобно пустым catch() на других языках.
С уважением
К
Ответ 9
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE "IMS"."MAX" ';
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE != -942 THEN
RAISE;
END IF;
EXECUTE IMMEDIATE '
CREATE TABLE "IMS"."MAX"
( "ID" NUMBER NOT NULL ENABLE,
"NAME" VARCHAR2(20 BYTE),
CONSTRAINT "MAX_PK" PRIMARY KEY ("ID")
USING INDEX PCTFREE 10 INITRANS 2 MAXTRANS 255
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "SYSAUX" ENABLE
) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 NOCOMPRESS LOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "SYSAUX" ';
END;
//Выполняя этот код, проверяет, существует ли таблица, а затем создает таблицу max. это просто работает в единой компиляции
Ответ 10
К сожалению, нет такой вещи, как drop if exists, или CREATE IF NOT EXIST
Вы можете написать plsql script, чтобы включить туда логику.
http://download.oracle.com/docs/cd/B12037_01/server.101/b10759/statements_9003.htm
Я не очень разбираюсь в синтаксисе Oracle, но я думаю, что @Erich script будет чем-то вроде этого.
declare
cant integer
begin
select into cant count(*) from dba_tables where table_name='Table_name';
if count>0 then
BEGIN
DROP TABLE tableName;
END IF;
END;
Ответ 11
Я предпочитаю указывать таблицу и владельца схемы.
Следите за чувствительностью к регистру. (см. раздел "верхняя" ниже).
Я бросил несколько разных объектов, чтобы показать, что их можно использовать в местах, кроме TABLEs.
.............
declare
v_counter int;
begin
select count(*) into v_counter from dba_users where upper(username)=upper('UserSchema01');
if v_counter > 0 then
execute immediate 'DROP USER UserSchema01 CASCADE';
end if;
end;
/
CREATE USER UserSchema01 IDENTIFIED BY pa$$word
DEFAULT TABLESPACE users
TEMPORARY TABLESPACE temp
QUOTA UNLIMITED ON users;
grant create session to UserSchema01;
И пример TABLE:
declare
v_counter int;
begin
select count(*) into v_counter from all_tables where upper(TABLE_NAME)=upper('ORDERS') and upper(OWNER)=upper('UserSchema01');
if v_counter > 0 then
execute immediate 'DROP TABLE UserSchema01.ORDERS';
end if;
end;
/
Ответ 12
И если вы хотите сделать это повторно доступным и минимизировать цикл drop/create, вы можете кэшировать DDL с помощью dbms_metadata.get_ddl и воссоздать все с помощью такой конструкции:
declare
v_ddl varchar2(4000);
begin
select dbms_metadata.get_ddl('TABLE','DEPT','SCOTT') into v_ddl from dual;
[COMPARE CACHED DDL AND EXECUTE IF NO MATCH]
exception when others then
if sqlcode = -31603 then
[GET AND EXECUTE CACHED DDL]
else
raise;
end if;
end;
Это всего лишь образец, должен быть цикл внутри с типом DDL, именем и владельцем, являющимися переменными.