【完全ガイド】ORA-00955: name is already used by an existing object の原因と解決方法|23ai IF NOT EXISTS・CREATE OR REPLACE 徹底解説

【完全ガイド】ORA-00955: name is already used by an existing object の原因と解決方法|23ai IF NOT EXISTS・CREATE OR REPLACE 徹底解説

Oracle DBA・開発者がDDL 実行時に頻繁に遭遇するエラー:

CREATE TABLE customers (id NUMBER, name VARCHAR2(100));
-- ORA-00955: name is already used by an existing object

**「その名前は既に使われている」**というシンプルなメッセージ。Oracle のオブジェクト名は同一スキーマ内で一意という制約がある以上、避けられないエラーです:

  • CREATE TABLE で既存テーブルと同名
  • CREATE INDEX で既存索引と同名
  • CREATE SEQUENCE で既存シーケンスと同名
  • CREATE VIEW で既存ビューと同名
  • CREATE USER でユーザーと同名
  • シノニムとの衝突
  • プライマリキーの暗黙索引との衝突
  • Rails マイグレーションの再実行
  • CI/CD パイプラインの冪等性欠如
  • 別スキーマからの共有オブジェクト混乱

多くの日本語記事が「名前を変えろ」で終わりますが、実務では:

  • Oracle 23ai の IF NOT EXISTS 新機能
  • CREATE OR REPLACE の対応/非対応オブジェクト
  • PK(プライマリキー)と UNIQUE INDEX の名前衝突(超危険)
  • シノニム経由の衝突
  • ALL_OBJECTS で全スキーマ検索
  • Rails マイグレーションでの冪等性
  • EXECUTE IMMEDIATE + 例外処理パターン
  • 予約語との衝突
  • CI/CD の冪等な DDL

さらに、実務では予期しない発生パターンがあります:

-- ❌ よくある罠:PK 削除 → UNIQUE INDEX 追加 → PK 再追加でエラー
ALTER TABLE t DROP PRIMARY KEY;
-- PK 用の暗黙索引が残る場合あり
CREATE UNIQUE INDEX idx_t_pk ON t(id);
ALTER TABLE t ADD CONSTRAINT pk_t PRIMARY KEY (id) USING INDEX idx_t_pk;
-- ORA-00955(idx_t_pk が既に存在)

または、Rails / Flyway などの ORM/マイグレーションツール使用時:

テストで rollback + migrate 繰り返し
→ 途中でエラーで停止
→ 部分的にオブジェクト残存
→ 次回 migrate で ORA-00955

本記事では、ORA-00955: name is already used by an existing object完全な原因と解決方法を、リファレンスとして実用的に整理します。10大発生パターン、6つの解決策、Oracle 23ai の IF NOT EXISTS、PK/UK と Index の衝突、Rails/Java/Python 対応、実践シナリオ、FAQまで完全網羅。この1本で ORA-00955 に冷静に対処できるようになります。


目次

結論:既存オブジェクトを確認

時間がない方向けに、最速の対処を先に示します。

エラーメッセージの読み方

ORA-00955: name is already used by an existing object

※ どのオブジェクトタイプで衝突しているかは表示されない
※ 自分で調査する必要がある

最速の診断

-- ① 現スキーマで検索
SELECT object_name, object_type, status, created
FROM user_objects
WHERE object_name = UPPER('customers');

-- ② 全スキーマで検索
SELECT owner, object_name, object_type
FROM all_objects
WHERE object_name = UPPER('customers');

-- ③ シノニムも確認
SELECT * FROM user_synonyms WHERE synonym_name = UPPER('customers');
SELECT * FROM all_synonyms WHERE synonym_name = UPPER('customers');

6つの解決策

#手法使う場面
IF NOT EXISTS (23ai)冪等な DDL
CREATE OR REPLACEView, Procedure, Function 等
別名で作成意図的に別オブジェクト
DROP + CREATE再作成が明確
EXECUTE IMMEDIATE + 例外動的 SQL
Editions24時間運用

CREATE OR REPLACE 対応表

オブジェクトタイプCREATE OR REPLACE
VIEW
PROCEDURE
FUNCTION
PACKAGE / PACKAGE BODY
TRIGGER
TYPE / TYPE BODY
SYNONYM
DIRECTORY
TABLE❌(要 DROP)
INDEX
SEQUENCE
USER

TABLE, INDEX, SEQUENCE は CREATE OR REPLACE 不可、23ai の IF NOT EXISTS で対処。

詳細は以下で解説します。


Oracle 23ai の IF NOT EXISTS

待望の新機能

Oracle 23ai (23.4+) で追加された、他 DB では標準の構文:

-- 存在確認付き CREATE
CREATE TABLE IF NOT EXISTS customers (
  id NUMBER PRIMARY KEY,
  name VARCHAR2(100)
);
-- 既存: 何もしない
-- なし: 作成

-- 存在確認付き DROP
DROP TABLE IF EXISTS customers;
-- 存在: 削除
-- なし: 何もしない

対応 DDL 一覧

IF NOT EXISTS 対応(23ai):

CREATE TABLE
CREATE VIEW
CREATE SEQUENCE
CREATE INDEX
CREATE USER
CREATE ROLE
CREATE PROCEDURE / FUNCTION / PACKAGE
CREATE TYPE
CREATE SYNONYM
CREATE MATERIALIZED VIEW
CREATE TRIGGER
CREATE DIRECTORY
CREATE TABLESPACE

IF EXISTS 対応(DROP):

DROP TABLE
DROP VIEW
DROP SEQUENCE
DROP INDEX
DROP USER
DROP PROCEDURE / FUNCTION / PACKAGE
DROP TYPE
DROP SYNONYM
DROP MATERIALIZED VIEW
DROP TRIGGER

12c〜19c での代替

DECLARE
  v_count NUMBER;
BEGIN
  SELECT COUNT(*) INTO v_count
  FROM user_objects
  WHERE object_name = 'CUSTOMERS' AND object_type = 'TABLE';
  
  IF v_count = 0 THEN
    EXECUTE IMMEDIATE 'CREATE TABLE customers (id NUMBER, name VARCHAR2(100))';
  END IF;
END;
/

または EXCEPTION でハンドリング:

BEGIN
  EXECUTE IMMEDIATE 'CREATE TABLE customers (id NUMBER, name VARCHAR2(100))';
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE = -955 THEN
      NULL;   -- 既存なら無視
    ELSE
      RAISE;
    END IF;
END;
/

【原因①】CREATE TABLE 同名

症状

-- 既存テーブル
CREATE TABLE customers (id NUMBER);

-- 再作成試行
CREATE TABLE customers (id NUMBER, name VARCHAR2(100));
-- ORA-00955

診断

SELECT owner, object_name, object_type, status, created
FROM all_objects
WHERE object_name = 'CUSTOMERS';
-- 誰のスキーマに存在するか

解決

A. 23ai:

CREATE TABLE IF NOT EXISTS customers (...);

B. DROP + CREATE:

DROP TABLE customers CASCADE CONSTRAINTS;
CREATE TABLE customers (...);

C. 別名:

CREATE TABLE customers_v2 (...);

D. リネーム:

ALTER TABLE customers RENAME TO customers_old;
CREATE TABLE customers (...);

制約関連は ORA-02291: parent key not found の記事、ORA-02292: child record found の記事も参照してください。


【原因②】CREATE INDEX 同名

症状

CREATE INDEX idx_email ON users(email);

-- 別テーブルで同じ名前
CREATE INDEX idx_email ON customers(email);
-- ORA-00955

インデックス名は同一スキーマ内で一意。テーブル間で共有できない。

診断

SELECT index_name, table_name, uniqueness
FROM user_indexes
WHERE index_name = 'IDX_EMAIL';

解決

命名規則:

CREATE INDEX users_email_idx ON users(email);
CREATE INDEX customers_email_idx ON customers(email);
-- テーブル名を含める

【原因③】PK/UK と Index の名前衝突(超危険)

罠のシナリオ

-- 手動で UNIQUE INDEX 作成
CREATE UNIQUE INDEX idx_users_pk ON users(id);

-- 後で PK 追加
ALTER TABLE users ADD CONSTRAINT pk_users 
  PRIMARY KEY (id) USING INDEX idx_users_pk;

-- 別の変更でトラブル
ALTER TABLE users DROP PRIMARY KEY;
-- USING INDEX の場合、INDEX が残ることも

-- 再度 PK 追加
ALTER TABLE users ADD CONSTRAINT pk_users PRIMARY KEY (id);
-- ORA-00955(暗黙的に作られる pk_users という INDEX が既存の idx_users_pk と衝突)

診断

-- 手動 INDEX と自動 INDEX の関係
SELECT c.constraint_name, c.index_name, i.uniqueness
FROM user_constraints c
LEFT JOIN user_indexes i ON c.index_name = i.index_name
WHERE c.constraint_type IN ('P', 'U');

解決

A. PK と Index を明示的に管理:

ALTER TABLE users DROP CONSTRAINT pk_users;
DROP INDEX idx_users_pk;

ALTER TABLE users ADD CONSTRAINT pk_users PRIMARY KEY (id);
-- 自動的に pk_users という INDEX が作成される

B. USING INDEX で明示:

CREATE UNIQUE INDEX idx_users_pk ON users(id);
ALTER TABLE users ADD CONSTRAINT pk_users 
  PRIMARY KEY (id) USING INDEX idx_users_pk;
-- 制約 pk_users は idx_users_pk を使用

【原因④】CREATE SEQUENCE 同名

症状

CREATE SEQUENCE seq_users_id;

CREATE SEQUENCE seq_users_id;
-- ORA-00955

解決

A. 23ai:

CREATE SEQUENCE IF NOT EXISTS seq_users_id;

B. 動的 SQL:

BEGIN
  EXECUTE IMMEDIATE 'CREATE SEQUENCE seq_users_id';
EXCEPTION WHEN OTHERS THEN
  IF SQLCODE = -955 THEN NULL;
  ELSE RAISE;
  END IF;
END;
/

【原因⑤】シノニムとの衝突

症状

-- 別スキーマのテーブルへのシノニム
CREATE PUBLIC SYNONYM customers FOR other_schema.customers;

-- 現スキーマでテーブル作成
CREATE TABLE customers (id NUMBER);
-- ORA-00955(PUBLIC シノニムが存在)

診断

SELECT * FROM all_synonyms 
WHERE synonym_name = 'CUSTOMERS';

解決

A. シノニムを DROP:

DROP PUBLIC SYNONYM customers;
CREATE TABLE customers (...);

B. 別名:

CREATE TABLE my_customers (...);

【原因⑥】Rails マイグレーション

シナリオ

class CreateUsers < ActiveRecord::Migration[8.0]
  def change
    create_table :users do |t|
      t.string :name
      t.timestamps
    end
  end
end

# 実行中にエラー → 部分的に作成
# 再実行 → ORA-00955

解決

A. 冪等なマイグレーション(23ai):

class CreateUsers < ActiveRecord::Migration[8.0]
  def change
    execute <<-SQL
      CREATE TABLE IF NOT EXISTS users (
        id NUMBER PRIMARY KEY,
        name VARCHAR2(100)
      )
    SQL
  end
end

B. 存在確認:

class CreateUsers < ActiveRecord::Migration[8.0]
  def change
    return if table_exists?(:users)
    create_table :users do |t|
      t.string :name
      t.timestamps
    end
  end
end

C. ロールバック + マイグレーション:

rails db:rollback
rails db:migrate

Rails 8 系の詳細は Rails 8 アップグレードガイドの記事、rails db:migrate 使い方の記事、pending migration エラーの記事も参照してください。


【原因⑦】CI/CD パイプラインの冪等性欠如

シナリオ

# CI/CD で毎回実行
sqlplus $DB_USER/$DB_PW @init.sql
# CREATE TABLE ...
# 2回目実行 → ORA-00955

解決

冪等な init.sql:

-- 23ai
CREATE TABLE IF NOT EXISTS users (...);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);

-- 12c〜19c
DECLARE
  v_count NUMBER;
BEGIN
  SELECT COUNT(*) INTO v_count FROM user_tables 
  WHERE table_name = 'USERS';
  IF v_count = 0 THEN
    EXECUTE IMMEDIATE 'CREATE TABLE users (...)';
  END IF;
END;
/

【原因⑧】JIRA / ORM ツールでの CTAS

シナリオ

CREATE TABLE users_backup AS SELECT * FROM users;
CREATE TABLE users_backup AS SELECT * FROM users;
-- 2回目 → ORA-00955

バックアップ命名規則を追加:

CREATE TABLE users_backup_20260615 AS SELECT * FROM users;
-- タイムスタンプで一意化

【原因⑨】別スキーマからの参照

シナリオ

-- スキーマ A で
CREATE TABLE customers (...);

-- スキーマ B で
CREATE TABLE customers (...);
-- OK(別スキーマなので)

-- しかし、スキーマ A の PUBLIC SYNONYM がある場合
CREATE PUBLIC SYNONYM customers FOR a.customers;

-- スキーマ B で
CREATE TABLE customers (...);
-- ORA-00955(PUBLIC 名前空間で衝突)

解決

PRIVATE SYNONYM 使用:

-- スキーマ B のセッションで
CREATE SYNONYM my_customers FOR a.customers;
-- ローカルシノニム、衝突なし

【原因⑩】ゴミ箱(Recycle Bin)

症状

-- DROP でゴミ箱行き(Oracle 10g+)
DROP TABLE users;
-- 実は BIN$... の名前で保持

-- 同名で作成
CREATE TABLE users (...);
-- OK(一見動く)

-- しかし場合により ORA-00955

診断

-- ゴミ箱確認
SELECT * FROM user_recyclebin;
SHOW RECYCLEBIN;

解決

PURGE で完全削除:

-- 特定オブジェクトを完全削除
PURGE TABLE users;

-- ゴミ箱全削除
PURGE RECYCLEBIN;

-- または DROP 時に
DROP TABLE users PURGE;

診断ツール完全リファレンス

USER_OBJECTS(自スキーマ)

SELECT object_name, object_type, status, created, last_ddl_time
FROM user_objects
WHERE object_name = UPPER('name_to_check');

ALL_OBJECTS(アクセス可能全て)

SELECT owner, object_name, object_type, status
FROM all_objects
WHERE object_name = UPPER('name_to_check');

DBA_OBJECTS(DBA権限)

SELECT owner, object_name, object_type, status
FROM dba_objects
WHERE object_name = UPPER('name_to_check');

USER_SYNONYMS / ALL_SYNONYMS

-- シノニム確認
SELECT * FROM user_synonyms WHERE synonym_name = UPPER('name');
SELECT * FROM all_synonyms WHERE synonym_name = UPPER('name');

RECYCLE BIN

SELECT * FROM user_recyclebin WHERE original_name = UPPER('name');

V$RESERVED_WORDS

-- 予約語チェック
SELECT * FROM v$reserved_words WHERE keyword = UPPER('name');

予約語関連は ORA-00904: invalid identifier の記事も参照してください。


6つの解決策 完全リファレンス

解決策① IF NOT EXISTS (Oracle 23ai)

CREATE TABLE IF NOT EXISTS users (...);
CREATE INDEX IF NOT EXISTS idx_email ON users(email);
CREATE SEQUENCE IF NOT EXISTS seq_users_id;
CREATE VIEW IF NOT EXISTS v_active_users AS ...;

23ai の推奨方法

解決策② CREATE OR REPLACE

-- View, Procedure, Function 等
CREATE OR REPLACE VIEW v_users AS SELECT * FROM users;
CREATE OR REPLACE PROCEDURE proc_name IS ... END;
CREATE OR REPLACE FUNCTION fn_name RETURN NUMBER IS ...;
CREATE OR REPLACE PACKAGE pkg_name IS ...;
CREATE OR REPLACE SYNONYM s FOR t;

-- ⚠️ TABLE, INDEX, SEQUENCE は不可

解決策③ 別名で作成

CREATE TABLE users_new (...);
-- 意図的に別オブジェクト

解決策④ DROP + CREATE

DROP TABLE users CASCADE CONSTRAINTS PURGE;
CREATE TABLE users (...);

-- SEQUENCE
DROP SEQUENCE seq_users_id;
CREATE SEQUENCE seq_users_id;

解決策⑤ EXECUTE IMMEDIATE + EXCEPTION

BEGIN
  EXECUTE IMMEDIATE 'CREATE TABLE users (...)';
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE = -955 THEN
      -- 既存の場合、必要ならログ
      NULL;
    ELSE
      RAISE;
    END IF;
END;
/

解決策⑥ Edition-Based Redefinition

-- 24時間運用での DDL
CREATE EDITION new_ed AS CHILD OF ora$base;
ALTER SESSION SET EDITION = new_ed;

-- 新 Edition で作成
CREATE TABLE users (...);

EBR の詳細は ORA-04068: existing state of packages の記事も参照してください。


Rails / Java / Python 対応

Rails ActiveRecord

マイグレーションの冪等性:

class CreateUsers < ActiveRecord::Migration[8.0]
  def change
    unless table_exists?(:users)
      create_table :users do |t|
        t.string :name
        t.timestamps
      end
    end
  end
end

エラーハンドリング:

begin
  ActiveRecord::Base.connection.execute("CREATE TABLE users (id NUMBER)")
rescue ActiveRecord::StatementInvalid => e
  if e.message.include?("ORA-00955")
    Rails.logger.info "テーブル既存"
  else
    raise
  end
end

Rails 8 系の詳細は Rails 8 アップグレードガイドの記事、find/find_by/where の記事も参照してください。

Java (JDBC)

try {
    stmt.executeUpdate("CREATE TABLE users (id NUMBER)");
} catch (SQLException e) {
    if (e.getErrorCode() == 955) {
        logger.info("Table already exists");
    } else {
        throw e;
    }
}

// または存在チェック
try (ResultSet rs = stmt.executeQuery(
    "SELECT COUNT(*) FROM user_tables WHERE table_name = 'USERS'"
)) {
    if (rs.next() && rs.getInt(1) == 0) {
        stmt.executeUpdate("CREATE TABLE users (id NUMBER)");
    }
}

Python (oracledb)

import oracledb

try:
    cursor.execute("CREATE TABLE users (id NUMBER)")
except oracledb.DatabaseError as e:
    error_obj, = e.args
    if error_obj.code == 955:
        print("テーブル既存")
    else:
        raise

# または存在確認
cursor.execute("""
    SELECT COUNT(*) FROM user_tables WHERE table_name = 'USERS'
""")
if cursor.fetchone()[0] == 0:
    cursor.execute("CREATE TABLE users (id NUMBER)")

実践シナリオ

シナリオ1:冪等な init.sql

-- 23ai
CREATE TABLE IF NOT EXISTS users (
  id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  name VARCHAR2(100) NOT NULL,
  created_at TIMESTAMP DEFAULT SYSTIMESTAMP
);

CREATE INDEX IF NOT EXISTS idx_users_name ON users(name);

CREATE SEQUENCE IF NOT EXISTS seq_orders START WITH 1;

-- 12c〜19c 対応
DECLARE
  v_count NUMBER;
BEGIN
  -- users
  SELECT COUNT(*) INTO v_count FROM user_tables WHERE table_name = 'USERS';
  IF v_count = 0 THEN
    EXECUTE IMMEDIATE q'[
      CREATE TABLE users (
        id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
        name VARCHAR2(100) NOT NULL
      )
    ]';
  END IF;
END;
/

シナリオ2:Rails マイグレーションの冪等性

class CreateUsersIfNotExists < ActiveRecord::Migration[8.0]
  def change
    return if table_exists?(:users)
    
    create_table :users do |t|
      t.string :name
      t.string :email
      t.timestamps
    end
    
    add_index :users, :email, unique: true, if_not_exists: true
  end
end

シナリオ3:CI/CD デプロイパイプライン

# .github/workflows/deploy.yml
- name: Deploy schema
  run: |
    sqlplus -s $DB_USER/$DB_PASSWORD@$DB_DSN <<EOF
    WHENEVER SQLERROR EXIT SQL.SQLCODE
    @schema/tables.sql
    @schema/indexes.sql
    @schema/sequences.sql
    EOF

tables.sql の中身:

CREATE TABLE IF NOT EXISTS users (...);   -- 23ai
-- または PL/SQL ブロックで存在確認

Kamal 2 デプロイの詳細は Kamal 2 デプロイの記事を参照してください。

シナリオ4:PK/UK の名前衝突解消

-- 現状確認
SELECT c.constraint_name, c.constraint_type, c.index_name, i.uniqueness
FROM user_constraints c
LEFT JOIN user_indexes i ON c.index_name = i.index_name
WHERE c.table_name = 'USERS';

-- ゴミ掃除
BEGIN
  FOR c IN (SELECT constraint_name FROM user_constraints 
            WHERE table_name = 'USERS' AND constraint_type IN ('P', 'U')) LOOP
    EXECUTE IMMEDIATE 'ALTER TABLE users DROP CONSTRAINT ' || c.constraint_name;
  END LOOP;
  
  FOR i IN (SELECT index_name FROM user_indexes WHERE table_name = 'USERS') LOOP
    BEGIN
      EXECUTE IMMEDIATE 'DROP INDEX ' || i.index_name;
    EXCEPTION WHEN OTHERS THEN NULL;
    END;
  END LOOP;
END;
/

-- 綺麗にした状態で再作成
ALTER TABLE users ADD CONSTRAINT pk_users PRIMARY KEY (id);

制約関連は ORA-01400: NOT NULL の記事、ORA-00001: unique constraint の記事も参照してください。

シナリオ5:本番でのゴミ箱管理

-- 定期的にゴミ箱クリーニング
BEGIN
  DBMS_SCHEDULER.CREATE_JOB(
    job_name => 'PURGE_RECYCLEBIN_JOB',
    job_type => 'PLSQL_BLOCK',
    job_action => 'BEGIN EXECUTE IMMEDIATE ''PURGE RECYCLEBIN''; END;',
    repeat_interval => 'FREQ=WEEKLY',
    enabled => TRUE
  );
END;
/

シナリオ6:Data Pump インポートでの対処

# 既存テーブルへの対応
impdp system/pw dumpfile=data.dmp \
  TABLE_EXISTS_ACTION=REPLACE
# REPLACE = DROP + CREATE

# または SKIP
impdp system/pw dumpfile=data.dmp \
  TABLE_EXISTS_ACTION=SKIP
# 既存はスキップ

Data Pump の詳細は Oracle Data Pump 使い方の記事も参照してください。

シナリオ7:Docker Oracle でのテスト

docker exec -it oracle-xe sqlplus scott/tiger <<EOF
-- 冪等性テスト
CREATE TABLE t (id NUMBER);

-- 再実行 → ORA-00955
CREATE TABLE t (id NUMBER);

-- 23ai なら
CREATE TABLE IF NOT EXISTS t (id NUMBER);
EOF

Docker 関連は docker daemon 接続エラーの記事、Docker no space left on device の記事も参照してください。

シナリオ8:Java Spring での初期化

@Configuration
public class DatabaseInitializer {
    
    @Bean
    public CommandLineRunner initialize(JdbcTemplate jdbc) {
        return args -> {
            String sql = """
                DECLARE
                  v_count NUMBER;
                BEGIN
                  SELECT COUNT(*) INTO v_count FROM user_tables 
                  WHERE table_name = 'APP_CONFIG';
                  IF v_count = 0 THEN
                    EXECUTE IMMEDIATE 'CREATE TABLE app_config (...)';
                  END IF;
                END;
                """;
            jdbc.execute(sql);
        };
    }
}

シナリオ9:Python での冪等スキーマ作成

def ensure_table_exists(cursor, table_sql, table_name):
    cursor.execute("""
        SELECT COUNT(*) FROM user_tables WHERE table_name = :1
    """, (table_name.upper(),))
    
    if cursor.fetchone()[0] == 0:
        cursor.execute(table_sql)
        print(f"{table_name} 作成")
    else:
        print(f"{table_name} 既存")

# 使用
ensure_table_exists(cursor, 
    "CREATE TABLE users (id NUMBER, name VARCHAR2(100))",
    "USERS")

シナリオ10:Autonomous DB での対応

-- Autonomous DB (23ai)
CREATE TABLE IF NOT EXISTS my_table (...);
-- 完全対応

-- 監視
SELECT * FROM user_recyclebin;

トラブルシューティング

何のオブジェクトか分からない

SELECT owner, object_type FROM all_objects 
WHERE object_name = UPPER('name');

予約語と衝突

SELECT * FROM v$reserved_words WHERE keyword = UPPER('name');

PUBLIC SYNONYM 削除権限なし

-- DBA 権限必要
GRANT DROP PUBLIC SYNONYM TO scott;

CREATE OR REPLACE できない

TABLE, INDEX, SEQUENCE は不可:

-- ❌
CREATE OR REPLACE TABLE users (...);

-- ✅ 23ai
CREATE TABLE IF NOT EXISTS users (...);

-- または DROP + CREATE

ゴミ箱が原因

PURGE TABLE t;
-- または
DROP TABLE t PURGE;

大文字小文字

Oracle のオブジェクト名は引用符なし = 大文字化:

CREATE TABLE Users (...);
-- 実際は USERS
SELECT * FROM user_objects WHERE object_name = 'USERS';

よくある質問(FAQ)

Q1. IF NOT EXISTS はどのバージョンから

Oracle 23ai (23.4+) から。それ以前は PL/SQL で対応。

Q2. CREATE OR REPLACE 対応

View / Procedure / Function / Package / Trigger / Type / Synonym / Directory 等。TABLE / INDEX / SEQUENCE は不可

Q3. PUBLIC vs PRIVATE SYNONYM

  • PUBLIC: 全ユーザーからアクセス、名前空間全体
  • PRIVATE: 自スキーマのみ、他と衝突しない

Q4. ゴミ箱の役割

SHOW RECYCLEBIN;
-- 削除したオブジェクト(BIN$... 名)
-- FLASHBACK で復元可能

Q5. Rails での対応

23ai + IF NOT EXISTS or table_exists? チェック

Q6. CI/CD での冪等な DDL

  • IF NOT EXISTS(23ai)
  • PL/SQL 存在確認
  • Flyway / Liquibase

Q7. PK と UK の共存

別列なら共存可、同列に対する PK と UK は矛盾。

Q8. 別スキーマとの衝突

PUBLIC SYNONYM が原因の可能性。

Q9. パフォーマンスへの影響

IF NOT EXISTS はチェックのみ、オーバーヘッド小。

Q10. スキーマ整理の推奨

  • 命名規則統一
  • 用途別プレフィックス
  • 定期的なゴミ箱掃除

Q11. Flyway / Liquibase での対応

  • Flyway: V1__create_table.sql(1回のみ実行)
  • Liquibase: preconditions で存在確認

Q12. Data Pump での TABLE_EXISTS_ACTION

  • SKIP(デフォルト)
  • APPEND(追記)
  • TRUNCATE(既存を空に)
  • REPLACE(DROP + CREATE)

参考リンク

Oracle 公式


まとめ

ORA-00955: name is already used by an existing object の要点を再整理します。

エラーの本質

CREATE 対象の名前が既存オブジェクトと衝突
- 同一スキーマ内で名前は一意
- PUBLIC SYNONYM は全ユーザー共通名前空間
- Recycle Bin にも古いオブジェクト存在

診断3ステップ

-- ① 自スキーマ
SELECT * FROM user_objects WHERE object_name = UPPER('name');

-- ② 全スキーマ
SELECT * FROM all_objects WHERE object_name = UPPER('name');

-- ③ シノニム
SELECT * FROM all_synonyms WHERE synonym_name = UPPER('name');

-- ④ ゴミ箱
SELECT * FROM user_recyclebin WHERE original_name = UPPER('name');

10大原因

#原因対処
CREATE TABLE 同名IF NOT EXISTS
CREATE INDEX 同名命名規則
PK/UK/Index 衝突明示的管理
CREATE SEQUENCE 同名IF NOT EXISTS
シノニムとの衝突DROP SYNONYM
Rails マイグレーションtable_exists?
CI/CD 冪等性欠如存在チェック
CTAS 再実行タイムスタンプ命名
別スキーマとの衝突PRIVATE SYNONYM
ゴミ箱残存PURGE

6つの解決策

-- ① 23ai IF NOT EXISTS(推奨)
CREATE TABLE IF NOT EXISTS t (...);

-- ② CREATE OR REPLACE(View, Proc 等)
CREATE OR REPLACE VIEW v AS SELECT ...;

-- ③ 別名
CREATE TABLE t_v2 (...);

-- ④ DROP + CREATE
DROP TABLE t PURGE;
CREATE TABLE t (...);

-- ⑤ EXECUTE IMMEDIATE + EXCEPTION
BEGIN
  EXECUTE IMMEDIATE 'CREATE ...';
EXCEPTION
  WHEN OTHERS THEN
    IF SQLCODE = -955 THEN NULL;
    ELSE RAISE;
    END IF;
END;

-- ⑥ Edition-Based Redefinition
CREATE EDITION new_ed;
ALTER SESSION SET EDITION = new_ed;
CREATE TABLE t (...);

CREATE OR REPLACE 対応表

○ 対応× 不可
VIEWTABLE
PROCEDUREINDEX
FUNCTIONSEQUENCE
PACKAGEUSER
TRIGGERROLE
TYPECONSTRAINT
SYNONYM
DIRECTORY

Oracle 23ai IF NOT EXISTS

CREATE TABLE IF NOT EXISTS ...
CREATE INDEX IF NOT EXISTS ...
CREATE SEQUENCE IF NOT EXISTS ...
CREATE USER IF NOT EXISTS ...

DROP TABLE IF EXISTS ...
DROP INDEX IF EXISTS ...

冪等な DDL パターン

23ai:  IF NOT EXISTS
12-19c: PL/SQL + user_objects 確認
汎用:  EXECUTE IMMEDIATE + WHEN OTHERS THEN IF SQLCODE = -955

アプリでの対応

Rails:  table_exists?, if_not_exists: true
Java:   try-catch + ErrorCode == 955
Python: try-except + code == 955

これらの知識は、Oracle DBA の運用・スキーマ設計・CI/CD パイプライン・Rails / Java / Python 開発・データ移行・DDL 実行・23ai 新機能活用など、あらゆる場面で活用できます。本記事をブックマークしておけば、ORA-00955 に出会っても冷静に的確に対処できるようになります。


本記事は2026年6月時点の情報をもとに、Oracle Database 19c〜23ai での動作確認・公式ドキュメントに基づき作成しています。Oracle のバージョンにより挙動が異なる場合があるため、最新の情報は Oracle 公式ドキュメント(docs.oracle.com)もあわせてご確認ください。