代码之家  ›  专栏  ›  技术社区  ›  Alana Storm

Oracle:如果表存在

  •  292
  • Alana Storm  · 技术社区  · 15 年前

    我正在为一个Oracle数据库编写一些迁移脚本,希望Oracle有类似于MySQL的东西 IF EXISTS 构建。

    具体来说,每当我想在mysql中删除一个表时,我都会做一些类似的事情

    DROP TABLE IF EXISTS `table_name`;
    

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

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

    SELECT * FROM dba_tables where table_name = 'table_name';
    

    但是把它和 跌落 正在逃离我。

    15 回复  |  直到 6 年前
        1
  •  485
  •   Jeffrey Kemp    6 年前

    最好和最有效的方法是捕获“找不到表”异常:这样可以避免检查表是否存在两次的开销;并且不会遇到这样的问题:如果由于其他原因(可能很重要)导致放置失败,异常仍然会向调用方引发:

    BEGIN
       EXECUTE IMMEDIATE 'DROP TABLE ' || table_name;
    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
  •  121
  •   Marius Burz    11 年前
    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
  •  24
  •   Willem Renzema    10 年前

    我一直在寻找同样的方法,但最后我写了一个程序来帮助我:

    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
  •  12
  •   mishkin    12 年前

    只想发布一个完整的代码,它将创建一个表,并删除它,如果它已经存在,使用杰弗里的代码(向他致敬,而不是我!).

    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
  •  9
  •   trunkc    8 年前

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

    WHENEVER SQLERROR CONTINUE NONE
    DROP TABLE TABLE_NAME;
    
    WHENEVER SQLERROR EXIT SQL.SQLCODE
    DROP TABLE TABLE_NAME;
    

    CONTINUE NONE 报告了一个错误,但脚本将继续。用 EXIT SQL.SQLCODE 如果出现错误,脚本将终止。

    参见: WHENEVER SQLERROR Docs

        6
  •  3
  •   Erich    15 年前

    Oracle中没有“drop table if exists”,您必须执行select语句。

    试试这个(我不懂Oracle语法,所以如果我的变量是ify,请原谅我):

    declare @count int
    select @count=count(*) from all_tables where table_name='Table_name';
    if @count>0
    BEGIN
        DROP TABLE tableName;
    END
    
        7
  •  3
  •   Lukasz Szozda    6 年前

    一种方法是使用 DBMS_ASSERT.SQL_OBJECT_NAME :

    此函数验证输入参数字符串是否为现有SQL对象的限定SQL标识符。

    DECLARE
        V_OBJECT_NAME VARCHAR2(30);
    BEGIN
       BEGIN
            V_OBJECT_NAME  := DBMS_ASSERT.SQL_OBJECT_NAME('tab1');
            EXECUTE IMMEDIATE 'DROP TABLE tab1';
    
            EXCEPTION WHEN OTHERS THEN NULL;
       END;
    END;
    /
    

    DBFiddle Demo

        8
  •  2
  •   Leigh Riffel    9 年前

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

    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;
    
        9
  •  0
  •   Tom    15 年前

    不幸的是,没有这样的东西:如果存在,就删除;如果不存在,就创建。

    您可以编写一个plsql脚本来包含其中的逻辑。

    http://download.oracle.com/docs/cd/B12037_01/server.101/b10759/statements_9003.htm

    我对Oracle语法不太了解,但我认为@erich的脚本应该是这样的。

    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;
    
        10
  •  0
  •   Khb    15 年前

    你总是可以自己发现错误。

    begin
    execute immediate 'drop table mytable';
    exception when others then null;
    end;
    

    过度使用这种方法被认为是不好的做法,类似于其他语言中的空catch()。

    当做
    K

        11
  •  0
  •   granadaCoder    12 年前

    我更喜欢指定表和模式所有者。

    注意区分大小写。(见下文“上限”条款)。

    我扔了两个不同的东西来证明它可以在桌子以外的地方使用。

    ……

    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;  
    

    以及一个表格示例:

    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
  •  0
  •   Moinuddin Quadri    8 年前
    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。这只在单个编译中工作

        13
  •  0
  •   Andrei Nossov    7 年前

    如果要使其可重新输入并最小化放置/创建周期,可以使用DBMS元数据缓存DDL。获取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类型、名称和所有者为变量的循环。

        14
  •  0
  •   user9213118    7 年前

    像这样的块对你有用。

    DECLARE
        table_exist INT;
    
    BEGIN
        SELECT Count(*)
        INTO   table_exist
        FROM   dba_tables
        WHERE  owner = 'SCHEMA_NAME' 
        AND table_name = 'EMPLOYEE_TABLE';
    
        IF table_exist = 1 THEN
          EXECUTE IMMEDIATE 'drop table EMPLOYEE_TABLE';
        END IF;
    END;  
    
        15
  •  0
  •   Pavel S    6 年前

    我更喜欢经济上的解决办法

    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;