索引を消す前に、性能と制約を分けて確認する
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文の基本構文
インデックスを削除する基本的な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のたびに更新されるため、数が多いと書き込み性能が低下する。検索の恩恵より更新のコストが上回るインデックスは削除を検討する。
逆に、新しくインデックスを作成する方法については関連記事のインデックスの作成を参照してほしい。削除と作成はセットで理解しておくと、インデックスの設計・見直しがしやすくなる。
削除時の落とし穴
| 注意すべきポイント |
|---|
|
よくある質問(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・索引・設定・認証は変更していません。
- MySQL8.4 CREATE INDEX / DROP INDEX / 不可視索引
- MariaDB DROP INDEX / Ignored Indexes
- PostgreSQL18 DROP INDEX / SQL Server DROP INDEX
子ページはありません
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- MySQL/MariaDBのテーブル一覧|表・ビュー・権限と絞り込み 2026-10-26 07:32:39
- MySQL/MariaDBのDB選択|USEと接続・既定DBの違い 2026-10-26 07:32:39
- MySQL/MariaDBのDB一覧|SHOW DATABASESと権限の違い 2026-10-26 07:32:39
- MySQLへの接続|接続先・認証・TLSとエラーの確認 2026-10-26 07:32:39
- MySQL/MariaDBのカラム定義確認|権限・既定値・ORMとの違い 2026-10-26 06:18:53
- MySQL/MariaDBのメタデータ|列・索引・制約の確認ガイド 2026-10-26 06:18:53
- MySQL/MariaDBのテーブル定義確認|DESC・SHOW CREATEと復旧の範囲 2026-10-26 06:18:53
- MySQL/MariaDBのAUTO_INCREMENT追加・確認|既存IDと属性の保持 2026-10-26 05:42:11
- MySQL/MariaDBのAUTO_INCREMENT|欠番・取得ID・カウンタの違い 2026-10-26 05:42:11
- MySQL/MariaDBの索引削除|不可視化・UNIQUE・外部キーの注意 2026-10-26 05:11:35
- MySQL/MariaDBの索引作成|結果比較・UNIQUE・実行計画の確認 2026-10-26 05:11:35
- MySQL/MariaDBの外部キー|制約名・参照切れ・削除と再検査 2026-10-26 04:51:08
- MySQL/MariaDBのカラム追加・削除|既存値・互換性・ロックの確認 2026-10-26 04:51:08
- MySQL/MariaDBのカラム名変更|RENAME COLUMNと利用側の確認 2026-10-26 04:24:55
- MySQL/MariaDBのカラム定義変更|MODIFYの属性保持とロック 2026-10-26 04:24:55