Oracle:如果表存在

Ala*_*orm 322 sql oracle sql-drop

我正在为Oracle数据库编写一些迁移脚本,并且希望Oracle有类似MySQL的IF EXISTS构造.

具体来说,每当我想在MySQL中删除表时,我都会这样做

DROP TABLE IF EXISTS `table_name`;
Run Code Online (Sandbox Code Playgroud)

这样,如果表不存在,DROP则不会产生错误,脚本可以继续.

Oracle是否有类似的机制?我意识到我可以使用以下查询来检查表是否存在

SELECT * FROM dba_tables where table_name = 'table_name';
Run Code Online (Sandbox Code Playgroud)

但是把它与a绑在一起的语法DROP正在逃避我.

Jef*_*emp 540

最好和最有效的方法是捕获"未找到表"异常:这避免了检查表是否存在两次的开销; 并且不会遇到这样的问题:如果DROP由于某些其他原因(可能很重要)而失败,则仍会向调用者提出异常:

BEGIN
   EXECUTE IMMEDIATE 'DROP TABLE ' || table_name;
EXCEPTION
   WHEN OTHERS THEN
      IF SQLCODE != -942 THEN
         RAISE;
      END IF;
END;
Run Code Online (Sandbox Code Playgroud)

ADDENDUM 作为参考,以下是其他对象类型的等效块:

序列

BEGIN
  EXECUTE IMMEDIATE 'DROP SEQUENCE ' || sequence_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -2289 THEN
      RAISE;
    END IF;
END;
Run Code Online (Sandbox Code Playgroud)

视图

BEGIN
  EXECUTE IMMEDIATE 'DROP VIEW ' || view_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -942 THEN
      RAISE;
    END IF;
END;
Run Code Online (Sandbox Code Playgroud)

触发

BEGIN
  EXECUTE IMMEDIATE 'DROP TRIGGER ' || trigger_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -4080 THEN
      RAISE;
    END IF;
END;
Run Code Online (Sandbox Code Playgroud)

指数

BEGIN
  EXECUTE IMMEDIATE 'DROP INDEX ' || index_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -1418 THEN
      RAISE;
    END IF;
END;
Run Code Online (Sandbox Code Playgroud)

BEGIN
  EXECUTE IMMEDIATE 'ALTER TABLE ' || table_name
                || ' DROP COLUMN ' || column_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -904 AND SQLCODE != -942 THEN
      RAISE;
    END IF;
END;
Run Code Online (Sandbox Code Playgroud)

数据库链接

BEGIN
  EXECUTE IMMEDIATE 'DROP DATABASE LINK ' || dblink_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -2024 THEN
      RAISE;
    END IF;
END;
Run Code Online (Sandbox Code Playgroud)

物化视图

BEGIN
  EXECUTE IMMEDIATE 'DROP MATERIALIZED VIEW ' || mview_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -12003 THEN
      RAISE;
    END IF;
END;
Run Code Online (Sandbox Code Playgroud)

类型

BEGIN
  EXECUTE IMMEDIATE 'DROP TYPE ' || type_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -4043 THEN
      RAISE;
    END IF;
END;
Run Code Online (Sandbox Code Playgroud)

约束

BEGIN
  EXECUTE IMMEDIATE 'ALTER TABLE ' || table_name
            || ' DROP CONSTRAINT ' || constraint_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -2443 AND SQLCODE != -942 THEN
      RAISE;
    END IF;
END;
Run Code Online (Sandbox Code Playgroud)

调度程序作业

BEGIN
  DBMS_SCHEDULER.drop_job(job_name);
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -27475 THEN
      RAISE;
    END IF;
END;
Run Code Online (Sandbox Code Playgroud)

用户/架构

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;
Run Code Online (Sandbox Code Playgroud)

BEGIN
  EXECUTE IMMEDIATE 'DROP PACKAGE ' || package_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -4043 THEN
      RAISE;
    END IF;
END;
Run Code Online (Sandbox Code Playgroud)

程序

BEGIN
  EXECUTE IMMEDIATE 'DROP PROCEDURE ' || procedure_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -4043 THEN
      RAISE;
    END IF;
END;
Run Code Online (Sandbox Code Playgroud)

功能

BEGIN
  EXECUTE IMMEDIATE 'DROP FUNCTION ' || function_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -4043 THEN
      RAISE;
    END IF;
END;
Run Code Online (Sandbox Code Playgroud)

表空间

BEGIN
  EXECUTE IMMEDIATE 'DROP TABLESPACE' || tablespace_name;
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE != -959 THEN
      RAISE;
    END IF;
END;
Run Code Online (Sandbox Code Playgroud)

  • 为了删除USER,要忽略的SQLCODE是-1918. (12认同)
  • 一个人需要写一个程序吗?有没有更好的方法呢? (12认同)
  • 如果我添加许多`EXECUTE IMMEDIATE'DROP TABLE mytable';`句子(脚本中每个表一个),我是否必须为每个表添加一个异常处理程序,或者它是否足以将所有的句子包装在一个`BEGIN中...... EXCEPTION ... END;`阻止? (8认同)
  • @jpmc26:MS SQL的等价物是`IF OBJECT_ID('TblName')IS NOT NULL DROP TABLE TblName`.似乎SQL语言的冗长与价格成正比. (8认同)
  • @JeffreyKemp你不会这么认为,但我已经一次又一次地发现Oracle让一切变得艰难.当你每个模糊的语法错误平均花费一个小时或试图弄清楚如何在另一个数据库中做一些明显和容易的事情(比如有条件地删除一个元素)并且每天都会出现这些问题,它会加起来.快速. (6认同)
  • @WilsonFreitas - 不,你不需要编写程序.您可以使用匿名块,如此处的示例所示. (3认同)
  • @JeffreyKemp*什么?*这是*非常不真实.随着应用程序的变化,删除过时的东西是完全理智的.例如,您可能会更改列的类型,因为旧类型不再符合您的要求.您甚至可以替换整个表.或者就我而言,我替换了一整套视图,因为我完全重写了使用它们的代码.没有必要说你应该立即删除对象*,因为这会使回滚变得更难,但是一旦它们不再使用一两个版本,就可以了. (3认同)
  • @Throoze,每个都需要一个单独的异常处理程序,否则第一个异常会导致后续的drop被跳过. (2认同)
  • @JeffreyKemp对于许多`DROP`语句(比如20个视图,我必须`DROP`),一个过程(可能在`DECLARE`中创建)可能更具可读性.对威尔逊来说,你必须编写一个PL/SQL块而不是普通的SQL,这看起来有些过分.[PostgreSQL](http://www.postgresql.org/docs/9.3/static/sql-droptable.html)和[My SQL](http://dev.mysql.com/doc/refman/5.6/en/ drop-table.html)都为`DROP TABLE`(和许多其他类型的`DROP`s)语句添加了一个`IF EXISTS`选项来简化这个用例.有趣的是,SQL Server还没有实现这样的功能. (2认同)
  • 为了完整起见,我扩展了这个答案,以涵盖大多数对象类型. (2认同)

Mar*_*urz 126

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;
Run Code Online (Sandbox Code Playgroud)

这是用于检查当前模式中的表是否存在.要检查给定的表是否已存在于不同的模式中,您必须使用all_tables而不是user_tables添加条件all_tables.owner = upper('schema_name')

  • +1这样做更好,因为不要在异常解码时进行中继以了解要做什么.代码将更容易保持和理解 (31认同)
  • 同意@daitangio - 性能通常不会破坏一次性部署脚本的可维护性. (4认同)
  • @Matthew,DROP是一个DDL命令,因此它将首先发出一个COMMIT,删除该表,然后发出第二个COMMIT.当然,在这个例子中没有事务(因为它只发出了一个查询)所以它没有任何区别; 但是如果用户以前发布过一些DML,则会在执行任何DDL之前隐式提交. (2认同)

小智 25

我一直在寻找相同的但我最终写了一个程序来帮助我:

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;
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助


mis*_*kin 12

只是想发布一个完整的代码来创建一个表,如果已经存在使用Jeffrey的代码就放弃它(对他而言,不是我!).

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;
Run Code Online (Sandbox Code Playgroud)

  • 就个人而言,我会将 CREATE TABLE 放在单独的步骤中,因为它不需要动态完成并且不需要异常处理程序。 (2认同)

tru*_*nkc 9

使用SQL*PLUS,您还可以使用WHENEVER SQLERROR命令:

WHENEVER SQLERROR CONTINUE NONE
DROP TABLE TABLE_NAME;

WHENEVER SQLERROR EXIT SQL.SQLCODE
DROP TABLE TABLE_NAME;
Run Code Online (Sandbox Code Playgroud)

随着CONTINUE NONE报告错误,但脚本会继续.在EXIT SQL.SQLCODE出现错误的情况下脚本将被终止.

另见:WHEREVER SQLERROR Docs


Lei*_*fel 6

另一种方法是定义一个异常,然后只捕获该异常,让所有其他异常传播。

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;
Run Code Online (Sandbox Code Playgroud)


小智 5

我更喜欢以下经济解决方案

BEGIN
    FOR i IN (SELECT NULL FROM USER_OBJECTS WHERE OBJECT_TYPE = 'TABLE' AND OBJECT_NAME = 'TABLE_NAME') LOOP
            EXECUTE IMMEDIATE 'DROP TABLE TABLE_NAME';
    END LOOP;
END;
Run Code Online (Sandbox Code Playgroud)