◀ 6.

MySQL/MariaDBの索引削除|不可視化・UNIQUE・外部キーの注意

▶

索引を消す前に、性能と制約を分けて確認する

索引の削除前後に結果と依存を照合し、不可視でも保護は残ると示す図

DROP INDEXはDROP TABLEではなく、今回の通常索引の検体では行と値を保持しました。ただしUNIQUEを外すと重複の防止も失い、外部キーが必要とする索引は削除を拒否される場合があります。「検索が遅くなるだけ」ではありません。テーブル関連の親ページから子ページを選択し、作成と比較・外部キーの目的も合わせて確認してください。

2026-10-26にMySQL8.4.13 / MariaDB11.8.9-ubu2404の新規InnoDB検体で確認した範囲を示します。本番の索引や保護は変更していません。他製品の既存章は残しますが、PostgreSQL / Oracle / SQL Serverの実行試験をした意味ではありません。

1. 対象と保持するものを読み取る

SELECT VERSION(), DATABASE(), @@SESSION.sql_mode, @@SESSION.foreign_key_checks;
SELECT TABLE_SCHEMA, TABLE_NAME, ENGINE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA='cwiki_w26' AND TABLE_NAME='orders';
SELECT TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, NON_UNIQUE,
       SEQ_IN_INDEX, COLUMN_NAME, SUB_PART, COLLATION, INDEX_TYPE
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA='cwiki_w26' AND TABLE_NAME='orders'
ORDER BY INDEX_NAME, SEQ_IN_INDEX;
SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE
FROM information_schema.TABLE_CONSTRAINTS
WHERE TABLE_SCHEMA='cwiki_w26' AND TABLE_NAME='orders'
ORDER BY CONSTRAINT_NAME;

同じ先頭列の索引でも、列順/末尾列/UNIQUE/長さ指定/式/昇降順/照合規則とhint利用が違えば同一ではありません。一般の利用回数0には、計測開始前の処理・月次処理・他接続・制約による利用が含まれない場合があります。1本のEXPLAINで見えなかったことだけを削除の根拠にしません。索引名の一覧だけでは外部キーや全列の対応も分からず、定義・制約・利用側を照合します。

2. 不可視化は削除の代用品ではない

性能影響の評価に使える機能ですが、実際にはDDLであり、勝手に本番へ適用しません。次の例は作成記事の新規検体に通常索引を作った後で、対象製品の組だけを実行します。両製品の構文を順に流す例ではありません。MySQLのINVISIBLEとMariaDB10.6以降のIGNOREDは似た目的でも設定/利用方法が異なります。

MySQL8.4検体だけ:不可視化し、同じSELECT/EXPLAINと定義を照合後、VISIBLEへ戻します。

-- MySQL 8.4 fixture only, not MariaDB syntax.
ALTER TABLE cwiki_w26.orders ALTER INDEX idx_orders_user_created_id INVISIBLE;
ALTER TABLE cwiki_w26.orders ALTER INDEX idx_orders_user_created_id VISIBLE;

MariaDB11.8検体だけ:IGNOREDにし、同じSELECT/EXPLAINと定義を照合後、NOT IGNOREDへ戻します。

-- MariaDB 11.8 fixture only, not MySQL syntax.
ALTER TABLE cwiki_w26.orders ALTER INDEX idx_orders_user_created_id IGNORED;
ALTER TABLE cwiki_w26.orders ALTER INDEX idx_orders_user_created_id NOT IGNORED;

検体では不可視/ignored時も6行と1,2,4の結果順序を保持し、索引は定義に残りました。別のUNIQUE検体では不可視/ignoredでも同じ非NULL値を1062で拒否しました。更新や一意性の保護・容量は残るため、これだけで削除後の書込みコストや保護消失を再現したとは扱えません。PRIMARY等には対象制限もあります。hintでその索引名を指定するSQLや、他接続の設定も調べます。

3. 削除と再作成を使い捨て検体で確かめる

作成記事の新規検体で結果/定義を記録してから、通常索引だけを対象とします。名前を実表へ置き換えて実行する手順ではありません。

SELECT id, user_id, created_at, note
FROM cwiki_w26.orders
WHERE user_id=10 AND created_at >= '2026-01-01 00:00:00'
ORDER BY created_at, id;
EXPLAIN SELECT id, user_id, created_at, note
FROM cwiki_w26.orders
WHERE user_id=10 AND created_at >= '2026-01-01 00:00:00'
ORDER BY created_at, id;
-- Disposable fixture only. This is not an instruction to drop a production index.
DROP INDEX idx_orders_user_created_id ON cwiki_w26.orders;

検体ではDROP後も6行・元の値・結果順序は同じで、対象索引だけが一覧からなくなりました。START TRANSACTION→DROP→ROLLBACKとしても索引は戻りませんでした。再作成には定義・時間・容量・lockが必要で、削除の取消ボタンではありません。列名と名前だけでなく全属性を事前保存します。UNIQUEを消してから重複が入った別検体では、同じUNIQUEの再作成が1062で拒否されました。重複行を消して復旧扱いにしません。

外部キーの必要索引を削除した別検体は1553で拒否され、索引・親子行・制約を保持しました。削除を通すために先にFKを外したり、foreign_key_checksを解除したりしません。主キー/UNIQUEの削除も単なる性能調整と分けます。

4. IF EXISTSと他製品の構文を混ぜない

同じ「存在すれば削除」でも、DROP INDEX IF EXISTSはMySQL8.4.13では1064となり、MariaDB11.8検体では存在しない索引について1091の警告になりました。IF EXISTSがあっても、意図しない実在索引の削除は防げません。エラーを避けるためにDROPの対象や製品を推測で書き換えません。

SQL ServerはON句の現行構文を優先します。table.indexの後方互換構文は公式資料で将来削除予定とされ、IF EXISTSはSQL Server2016以降の対応です。PostgreSQLのCONCURRENTLYにも待機があり、transaction block内・partitioned index・複数対象・制約依存等の制限があります。Oracleの制約用索引も対象制約と定義を照合し、制約を先に消せばよいという案内にはしません。

SQLでインデックスを削除するには、DROP INDEX文を使う。MySQLではDROP INDEX インデックス名 ON テーブル名;またはALTER TABLE テーブル名 DROP INDEX インデックス名;、PostgreSQL・OracleではDROP INDEX インデックス名;、SQL ServerではDROP INDEX インデックス名 ON スキーマ名.テーブル名;の現行構文を優先するだが、対象とするRDBMSによって構文が異なる点に注意したい。

この記事の要点
  • インデックスの削除にはDROP INDEX文を使用する。
  • テーブル名の指定方法(ON テーブル名が必要か等)はRDBMSによって異なる。
  • MySQLではALTER TABLE ... DROP INDEXという書き方も使える。
  • 削除前にSHOW INDEX FROM テーブル名などで名前に加えて定義・制約/依存・利用SQL・結果と復旧条件を確認します。
  • 主キー(PRIMARY KEY)や一意制約(UNIQUE)は制約として扱われ、別の操作で削除する必要がある場合が多い。
  • 不要・重複したインデックスや、書き込み性能を劣化させているインデックスが削除の主な対象となる。

 

DROP INDEX文の基本構文

インデックスを削除する基本的なSQL文がDROP INDEXである。どのインデックスを削除するかを名前で指定し、RDBMSによってはどのテーブルに属するインデックスかも併せて指定する。代表的なRDBMSごとの書き方を順に見ていく。

MySQL / MariaDBの場合

MySQLおよびMariaDBでは、削除対象のインデックスがどのテーブルに属するかをON テーブル名で指定する。

旧例の注意:通常索引の別例です。対象/制約/hint/復旧条件を照合せず実表へ流しません。

DROP INDEX idx_user_email ON users;

 

また、ALTER TABLE文の一部としてインデックスを削除することもできる。こちらの書き方は、同じALTER TABLE文の中で列の追加や変更とまとめて記述できる点が便利である。

旧例の注意:直前のDROPと代替の構文で、同じ対象へ順次実行しません。

ALTER TABLE users DROP INDEX idx_user_email;

 

どちらの構文も結果は同じで、idx_user_emailというインデックスがusersテーブルから削除される。なお、MySQLではINDEXの代わりにKEYと書くこともできる。

PostgreSQLの場合

PostgreSQLでは、インデックス名がスキーマ内で一意であるため、テーブル名を指定せずインデックス名だけで削除する。

旧例の注意:PG用です。接続先schemaと制約/依存を確認し、MySQLへ転用しません。

DROP INDEX idx_user_email;

 

存在しないインデックスを指定するとエラーになるため、存在する場合のみ削除したいときはIF EXISTSを付ける。また、テーブルへの書き込みをブロックせずに削除したい場合はCONCURRENTLYを指定できる(この場合トランザクションブロック内では実行できない)。

旧例の注意:PGのIF EXISTSとCONCURRENTLYの別例です。無待機/全索引対応ではなく、制約/partitioned index/transaction block等の制限も調べます。

DROP INDEX IF EXISTS idx_user_email;

DROP INDEX CONCURRENTLY idx_user_email;

 

Oracle Databaseの場合

OracleもPostgreSQLと同様に、インデックス名のみを指定して削除する。

旧例の注意:Oracle用です。制約用索引の依存やownerを確認し、保護解除で通す手順にしません。

DROP INDEX idx_user_email;

 

SQL Serverの場合

SQL Serverのtable.index形式は後方互換用で、公式資料では将来削除予定です。新しいコードではON句の現行形式を優先します。次の旧例は記録として保持します。

旧例の注意:SQL Serverの後方互換構文の記録です。新しい実装では直後のON句を優先します。

DROP INDEX users.idx_user_email;

 

SQL Serverでは次のON句を優先し、schemaも確認します。IF EXISTSはSQL Server2016以降の対応で、ON句の利用条件と同じではありません。

旧例の注意:SQL Server用の代替例です。IF EXISTSの対象版/schemaも確認し、両DROPを続けて実行しません。

DROP INDEX idx_user_email ON users;

DROP INDEX IF EXISTS idx_user_email ON users;

 

RDBMS別の構文の違い

同じ「インデックスの削除」でも、テーブル名の指定方法やオプションはRDBMSごとに差がある。主要なRDBMSの書き方を比較すると次のようになる。

RDBMS 基本構文 テーブル名の指定 主な補足
MySQL / MariaDB DROP INDEX idx ON テーブル; ON テーブル名で指定(必須) ALTER TABLE ... DROP INDEXも可
PostgreSQL DROP INDEX idx; 不要(名前のみ) IF EXISTS / CONCURRENTLYが使える
Oracle DROP INDEX idx; 不要(名前のみ) 制約との関係を確認。索引を消すための制約解除を自動選択しない
SQL Server DROP INDEX idx ON スキーマ.テーブル; ON句を優先。table.indexは後方互換用 IF EXISTSはSQL Server2016以降。対象製品/版を照合

 

このように、MySQLとSQL Serverはテーブル名の指定が必要で、PostgreSQLとOracleはインデックス名のみで削除できる、という違いがある。バージョンによって対応する構文や使えるオプションが異なる場合があるため、利用するRDBMSの公式ドキュメントも確認すると確実である。

削除前にインデックスを確認する

削除を実行する前に、対象のテーブルにどのようなインデックスが存在し、それぞれの名前が何かを確認しておくとよい。インデックス名を間違えるとエラーになったり、意図しないインデックスを消してしまったりするおそれがあるためである。インデックスの一覧を確認する方法もRDBMSによって異なる。

旧例の注意:製品別の読取例です。PGのpsqlメタコマンドをSQLとして流さず、名前一覧だけで全定義を確認したと扱いません。PGはschema、SQL Serverはobject/schemaも限定します。

-- MySQL / MariaDB

SHOW INDEX FROM users;

 

-- PostgreSQL(psql のメタコマンド、または pg_indexes ビュー)

\d users

SELECT indexname FROM pg_indexes WHERE tablename = 'users';

 

-- Oracle

SELECT index_name FROM user_indexes WHERE table_name = 'USERS';

 

-- SQL Server

SELECT name FROM sys.indexes WHERE object_id = OBJECT_ID('users');

 

これらの照会だけでは全列/依存まで分からないものがあります。schema・列順・制約・hint・観測期間・結果と復旧条件も調べ、名前の確認だけで安全とは断定しません。

いつインデックスを削除するか

インデックスは検索(SELECT)を高速化するために作成するものだが、すべてのインデックスが常に有益とは限らない。次のようなケースでは、インデックスの削除を検討する価値がある。

  • 使われていない不要なインデックス:作成したものの、観測したクエリでは使われない索引。定期処理/他接続/UNIQUE/FK/hint等の必要性も照合し、利用回数0だけで不要としません。
  • 重複したインデックス:先頭列が同じだけでは重複ではありません。列順/長さ/一意性/式/昇降順/照合規則・被覆/結合/並べ替えや依存を比較して評価します。
  • 書き込み性能を劣化させているインデックス:インデックスはINSERT / UPDATE / DELETEのたびに更新されるため、数が多いと書き込み性能が低下する。検索の恩恵より更新のコストが上回るインデックスは削除を検討する。

逆に、新しくインデックスを作成する方法については関連記事のインデックスの作成を参照してほしい。削除と作成はセットで理解しておくと、インデックスの設計・見直しがしやすくなる。

削除時の落とし穴

注意すべきポイント
  • 主キー・一意制約は制約として扱われる:製品/engine/制約との関係で操作が違います。MySQL/MariaDB検体のUNIQUE索引はDROP INDEXで削除でき、重複の保護もなくなりました。全て制約側の操作が必須とは限りません。主キー削除や他製品の制約削除は対象/依存/保持要件に応じた別の設計判断です。索引削除のエラーを通すために制約解除へ切り替えません。
  • 削除中のロックに注意:インデックスの削除はテーブルに対してロックを取得する場合があり、その間は他の処理が待たされることがある。大規模なテーブルや稼働中の本番環境では、影響の少ない時間帯に実施するか、PostgreSQLのCONCURRENTLYのようにロックを抑える仕組みを利用するとよい。
  • 消すと検索が遅くなることがある:そのインデックスを利用していたクエリは、削除後にフルスキャンになり大幅に遅くなる可能性がある。本当に不要かを実行計画(EXPLAINなど)で確認してから削除するのが望ましい。

 

よくある質問(FAQ)

Q. 存在しないインデックスを削除しようとするとどうなりますか?

多くのRDBMSでは、指定したインデックスが存在しない場合にエラーとなる。エラーを避けて「存在する場合のみ削除」したいときは、MariaDB、PostgreSQL、SQL Server2016以降等の対応を確認します。MySQL8.4のDROP INDEXにはIF EXISTSがなく、今回の検体も1064でした。例えばPostgreSQLではDROP INDEX IF EXISTS idx_user_email;のように記述する。

Q. インデックスを削除するとテーブルのデータも消えますか?

消えない。DROP INDEXはインデックス(検索を高速化するための補助的なデータ構造)だけを削除するものであり、テーブル本体の行データには影響しない。削除後はそのインデックスが効いていた検索が遅くなる可能性はあるが、データそのものはそのまま残る。

Q. 削除したインデックスを元に戻すにはどうすればよいですか?

削除を取り消す専用の操作はないため、再度CREATE INDEXで同じ定義のインデックスを作り直す。列構成や名前だけでなく全定義/依存を保存し、再作成の容量/時間/lockも確認します。UNIQUE削除後の重複等で再作成できない場合もあり、ROLLBACKの代用品ではありません。具体的な作成方法はインデックスの作成を参照してほしい。

元の書式付きSQLの記録

元記事の構文表も保持します。日本語は置換位置を示す記号で、そのまま実行しません。元作成v1〜3のCREATE NDEXは誤記で、修正済みv5のCREATE INDEXを掲載しています。削除の表は対象/依存/復旧条件の確認なしに実表へ実行する手順ではありません。

ALTER TABLE テーブル名 DROP INDEX インデックス名;

確認範囲と公式資料

新規の小規模検体で構文・失敗・行/索引の保持を確認したもので、速度倍率/巨大負荷/全engine/全製品版/本番の停止時間/完全復旧を保証しません。本番schema・索引・設定・認証は変更していません。

子ページ

子ページはありません

同階層のページ
  1. テーブル作成
  2. カラムの定義変更
  3. カラム名の変更
  4. カラム追加 / 削除
  5. インデックスの作成
  6. インデックスの削除
  7. AUTO_INCREMENTの仕様について
  8. AUTO_INCREMENTの追加と確認方法
  9. 外部キーの追加/削除と制約名の確認方法