ERROR 1093 (HY000): You can't specify target table 'customer' for update in FROM clause は、UPDATE・DELETE するテーブルを、同じ文のサブクエリの中で直接読んだときに MySQL が出すエラーです。 「更新しながら、同じテーブルを読む」書き方を、MySQL は許していません(MySQL 公式マニュアル)。

直し方は3つです。どれも、確認環境で同じ結果になりました。

直し方 書き方のポイント
JOIN に書き換える DELETE c1 FROM customer c1 JOIN customer c2 ON ...
派生テーブル(サブクエリの中のサブクエリ)で包む WHERE id NOT IN (SELECT id FROM (SELECT ... FROM customer) AS t)
WITH 句(CTE)で先に計算する WITH keep AS (SELECT ... FROM customer) DELETE ...(MySQL 8.0)

確認環境:MySQL 8.0.46、PostgreSQL 16.15(確認日 2026年10月11日)。

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「MySQL ERROR 1093の直し方|同じテーブルの更新」

再現:重複行を1件残して消す

同じメールアドレスの顧客が重複しています。メールアドレスごとに、id が最も小さい行だけを残したいとします。

id  email  name
1   a@x    佐藤
2   b@x    鈴木
3   a@x    佐藤(重複)
4   c@x    高橋
5   b@x    鈴木(重複)
6   a@x    佐藤(重複2)
DELETE FROM customer
WHERE id NOT IN (SELECT MIN(id) FROM customer GROUP BY email);
ERROR 1093 (HY000): You can't specify target table 'customer' for update in FROM clause

削除する customer を、サブクエリの FROM customer で読んでいるためです。UPDATE でも同じです。

UPDATE customer SET score = score + 10
WHERE score < (SELECT AVG(score) FROM customer);      -- 平均より低い人に10点足す
-- ERROR 1093 (HY000): You can't specify target table 'customer' for update in FROM clause

直し方1:JOIN に書き換える

DELETE c1
FROM customer c1
JOIN customer c2 ON c1.email = c2.email AND c1.id > c2.id;
id  email  name
1   a@x    佐藤
2   b@x    鈴木
4   c@x    高橋          ← 各メールアドレスの最小の id だけが残った

「自分より id が小さい同じメールアドレスの行がある行」を消す、という考え方です。UPDATE は、集計した結果と JOIN します。

UPDATE customer c
JOIN (SELECT AVG(score) AS a FROM customer) t ON c.score < t.a
SET c.score = c.score + 10;
id  score
1   60      ← 50 → 60(平均 63.3 より低い)
2   80
3   70      ← 60 → 70
4   40      ← 30 → 40
5   90
6   70      ← 変わらず(平均以上)

直し方2:派生テーブルで包む

サブクエリを、もう一段 SELECT ... FROM ( ... ) AS 別名 で包みます。

DELETE FROM customer
WHERE id NOT IN (
  SELECT id FROM (SELECT MIN(id) AS id FROM customer GROUP BY email) AS keep
);
UPDATE customer SET score = score + 10
WHERE score < (SELECT avg_score FROM (SELECT AVG(score) AS avg_score FROM customer) AS t);

確認環境では、どちらも JOIN と同じ結果になりました。内側のサブクエリの結果が、いったん一時的な表として作られるため、「更新しながら同じテーブルを読む」状態にならず、エラーを避けられます。AS keep のような別名は必須です(派生テーブルには名前が要る)。

「派生テーブルでも効かない」と言われるケース

MySQL 公式マニュアルでは、派生テーブルが外側のクエリに統合(merge)されると、この回避が効かない場合があるとされ、NO_MERGE ヒントや optimizer_switch の derived_merge=off で、一時的な表を作らせる方法が案内されています。

UPDATE /*+ NO_MERGE(d) */ customer,
       (SELECT id FROM customer WHERE score < 60) AS d
SET customer.score = 0
WHERE customer.id = d.id;

確認環境(MySQL 8.0.46)では、ヒントを付けなくても、次の書き方はすべてエラーにならずに通りました。

書き方 結果(8.0.46)
DELETE ... WHERE id IN (SELECT id FROM (SELECT id FROM customer WHERE ...) AS t) 通った
UPDATE customer, (SELECT id FROM customer WHERE ...) AS d SET ... WHERE customer.id = d.id 通った
上に /*+ NO_MERGE(d) */ を付けたもの 通った

版や書き方によって結果が変わる可能性があるため、派生テーブルで包んでも 1093 が出るときは NO_MERGE を付けると覚えておきます。

直し方3:WITH 句(MySQL 8.0)

WITH keep AS (SELECT MIN(id) AS id FROM customer GROUP BY email)
DELETE FROM customer WHERE id NOT IN (SELECT id FROM keep);

確認環境では、これも id 1・2・4 が残り、同じ結果になりました。何を残すかが先に名前付きで書かれるので、3つの中では読みやすい書き方です。

実行する前に、消える行を確かめる

DELETE を書き換えたら、同じ条件の SELECT で、消える行を先に確かめます。

SELECT c1.*
FROM customer c1
JOIN customer c2 ON c1.email = c2.email AND c1.id > c2.id;     -- 消える行(3, 5, 6)

本番のデータなら、トランザクションの中で実行して、件数を確かめてから COMMIT します。

START TRANSACTION;
DELETE c1 FROM customer c1 JOIN customer c2 ON c1.email = c2.email AND c1.id > c2.id;
SELECT ROW_COUNT();       -- 消えた件数
-- 想定どおりなら COMMIT、違えば ROLLBACK

PostgreSQL では同じ SQL が通る

(PostgreSQL 16)
DELETE FROM customer2 WHERE id NOT IN (SELECT min(id) FROM customer2 GROUP BY email);   ← 通る
UPDATE customer2 SET score = score + 10 WHERE score < (SELECT avg(score) FROM customer2); ← 通る

1093 は MySQL 特有の制限です。PostgreSQL や他の DB で書いた SQL を MySQL に移すときに出会います。

確認の手順(チェックリスト)

順番 確認すること 方法
1 更新するテーブルを、サブクエリで読んでいないか UPDATE t ... (SELECT ... FROM t)、DELETE FROM t ... (SELECT ... FROM t)
2 書き換える JOIN、派生テーブル(別名つき)、WITH 句
3 まだ出るなら /*+ NO_MERGE(別名) */
4 消える行・変わる行 同じ条件の SELECT で先に確かめる
5 本番 トランザクションの中で ROW_COUNT() を見てから COMMIT

よくある質問

Q. SELECT だけなら同じテーブルをサブクエリで読めますか。 A. 読めます。1093 は、UPDATE・DELETE の対象のテーブルを、同じ文のサブクエリで読んだときだけ出ます。

Q. 重複を消すなら、ほかの方法はありますか。 A. 重複を残す行のルール(最小の id、最新の日時など)を決めてから、ウィンドウ関数(ROW_NUMBER())で番号を付けて消す方法もあります。重複の探し方と消し方の全体は、関連記事の「SQLで重複データを抽出・削除する方法」で詳しく比べています。

まとめ

  • UPDATE・DELETE するテーブルを、同じ文のサブクエリで読むと 1093。MySQL 特有の制限
  • 直し方は JOIN・派生テーブル(別名つき)・WITH 句の3つ
  • 派生テーブルでも出るなら NO_MERGE ヒント
  • 消す前に同じ条件の SELECT で確かめ、本番はトランザクションの中で
  • PostgreSQL では同じ SQL が通る。移行のときに出会いやすい

参考・出典

確認日はいずれも 2026年10月11日です。

  • MySQL 8.0 Reference Manual「UPDATE Statement」:https://dev.mysql.com/doc/refman/8.0/en/update.html

本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。

(記載例)出典:株式会社RJC「MySQL ERROR 1093の直し方|同じテーブルの更新」

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?