MySQLエラー1093-FROM句で更新のターゲットテーブルを指定できません


593

story_categoryデータベースに破損したエントリを持つテーブルがあります。次のクエリは、破損したエントリを返します。

SELECT * 
FROM  story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN 
       story_category ON category_id=category.id);

私は実行してそれらを削除しようとしました:

DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category 
      INNER JOIN story_category ON category_id=category.id);

しかし、私は次のエラーを受け取ります:

#1093-FROM句で更新のターゲットテーブル 'story_category'を指定できません

どうすればこれを克服できますか?



2
MySQLバグトラッカーの機能リクエストがここにあるようです:テーブルを更新できず、サブクエリで同じテーブルから選択できません
Ben Creasy

回答:


713

更新:この回答は、一般的なエラー分類をカバーしています。OPの正確なクエリを最適に処理する方法に関するより具体的な回答については、この質問に対する他の回答を参照してください

MySQLでは、SELECT部分​​で使用するのと同じテーブルを変更することはできません。
この動作は、http//dev.mysql.com/doc/refman/5.6/en/update.htmlに記載されてい ます。

たぶん、あなたは自分自身にテーブルを結合することができます

ロジックが単純でクエリを再形成できる場合は、適切な選択基準を使用して、サブクエリを失い、テーブルをそれ自体に結合します。これにより、MySQLはテーブルを2つの異なるものとして認識し、破壊的な変更を進めることができます。

UPDATE tbl AS a
INNER JOIN tbl AS b ON ....
SET a.col = b.col

または、サブクエリをfrom句に深くネストしてみてください...

サブクエリが絶対に必要な場合は回避策がありますが、パフォーマンスを含むいくつかの理由により醜くなります。

UPDATE tbl SET col = (
  SELECT ... FROM (SELECT.... FROM) AS x);

FROM句のネストされたサブクエリは暗黙的な一時テーブルを作成するため、更新中の同じテーブルとしてはカウントされません。

...しかし、クエリオプティマイザに注意してください

ただし、MySQL 5.7.6以降では、オプティマイザがサブクエリを最適化しても、エラーが発生する可能性があることに注意してください。幸い、 optimizer_switch変数を使用してこの動作をオフにすることができます。私はこれを短期間の修正以上のものとして、または小さな一回限りのタスクのために行うことはお勧めできませんでしたが。

SET optimizer_switch = 'derived_merge=off';

コメントでこのアドバイスをしてくれたPeter V.Mørchに感謝します。

テクニックの例は、Nabble最初に公開された Baron Schwartzによるもので、言い換えてここで拡張されました。


1
アイテムを削除する必要があり、別のテーブルから情報を取得できず、同じテーブルからサブクエリを実行する必要があったため、この回答に賛成しました。これはエラーをグーグルしながら上にポップアップ表示されるものなので、これは私と同じテーブルからサブクエリを実行しているときに更新しようとする多くの人々にとって最適な答えです。
HMR

2
@Cheekysoft、代わりに値を変数に保存してみませんか?
Pacerier、2015

19
MySQLSET optimizer_switch = 'derived_merge=off';
5.7.6以降では

1
続きPeterV.Mørch@ mysqlserverteam.com/derived-tables-in-mysql-5-7、場合に特定の操作が起こることはできませんマージ、行われています。たとえば、(ダミーに)LIMITで派生ダミーテーブルを提供すると、エラーは発生しません。しかし、これはかなりハックであり、MySQLの将来のバージョンでは、結局のところ、LIMITを使用したクエリのマージをサポートするリスクがまだあります。
user2180613

この回避策の完全な例を教えていただけますか?UPDATE tbl SET col =(SELECT ... FROM(SELECT .... FROM)AS x); まだエラーが発生します
JoelBonetR

310

NexusRexは、同じテーブルから結合を使用して削除するための非常に優れたソリューションを提供しました。

これを行う場合:

DELETE FROM story_category
WHERE category_id NOT IN (
        SELECT DISTINCT category.id AS cid FROM category 
        INNER JOIN story_category ON category_id=category.id
)

エラーが発生します。

しかし、条件をもう1つの選択でラップする場合:

DELETE FROM story_category
WHERE category_id NOT IN (
    SELECT cid FROM (
        SELECT DISTINCT category.id AS cid FROM category 
        INNER JOIN story_category ON category_id=category.id
    ) AS c
)

それは正しいことをするでしょう!!

説明:クエリオプティマイザーは最初のクエリに対して派生マージ最適化を実行します(これによりエラーで失敗します)が、2番目のクエリは派生マージ最適化の対象となりません。したがって、オプティマイザは最初にサブクエリを実行する必要があります。


6
多分それは私が今日拘束力を持っているからかもしれませんが、それが多分「最良の」ものではないとしても、これは最も簡単な答えでした。
タイラーV.

4
これはうまくいきました、ありがとう!ここでのロジックは何ですか?それがもう1つのレベルにネストされている場合、それは外側の部分の前に実行されますか?そして、それがネストされていない場合、削除がテーブルをロックした後、mySQLはそれを実行しようとしますか?
フラット猫

43
このエラーと解決策は論理的に意味をなさない...しかしそれはうまくいく。MySQL開発者がどのような薬を使用しているのかと時々思います...
Cerin

1
@Cerinに同意します。これはまったく馬鹿げていますが、機能します。
FastTrack

@ekonoval解決策に感謝しますが、それは私にとって最低限の意味ではありません。MySQLをだましていて、彼はそれを受け入れているようですlol
deFreitas

106

inner joinあなたのサブクエリでは不要です。あなたは内のエントリを削除するように見えますstory_categoryどこcategory_idにありませんcategoryテーブル。

これを行う:

DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category);

その代わりに:

DELETE FROM story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN
         story_category ON category_id=category.id);

5
これが一番の答えになるはずです!多分最初の「の代わりに」を削除します。
hoyhoy

1
DISTINCTはここでは不要だと思います-より良いパフォーマンスのために;)。
shA.t

1
私は狂っていなければならない。答えは原作で述べられているものと同じです。
Jeff Lowery 2017年

これも私の答えです。where inID列は使用しないでください。そのため、プライマリテーブルをサブクエリする必要はありません。
Richard

@JeffLowery- 最初のコードブロックはここでの答えです。質問からの2番目のコードブロックです。
ToolmakerSteve

95

最近、同じテーブルのレコードを更新する必要がありました。以下のようにしました。

UPDATE skills AS s, (SELECT id  FROM skills WHERE type = 'Programming') AS p
SET s.type = 'Development' 
WHERE s.id = p.id;

11
これは単に次のように書くことはできませんUPDATE skills SET type='Development' WHERE type='Programming';か?これは元の質問に答えていないようです。
lilbyrdie 2014年

1
やり過ぎのようです、@ lilbyrdieは正しいです-それだけである可能性がありますUPDATE skills SET type='Development' WHERE type='Programming';。なぜ多くの人が自分たちが何をしているのかを考えていない理由がわかりません...
shadyyx

1
これがここでの最良の答えです。構文は理解しやすく、前のステートメントを再利用できます。これは、特定のケースに限定されません。
Steffen Winkler

2
例のケースが疑わしい場合でも、原則は実際のシナリオで理解して展開するのが最善です。表示されたクエリが機能するのは、テーブルリストの暗黙的な結合がサブクエリの一時テーブルを作成するため、安定した結果が提供され、同じテーブルの取得と変更の競合が回避されるためです。
AnrDaemon 2017

1
これはやり過ぎだということについて誰かが言うかもしれないことに関わらず、それはまだタイトルに関して質問に答えます。OPが言及したエラーは、ネストされたクエリで同じテーブルを使用して更新を試みたときにも発生します
smac89

35
DELETE FROM story_category
WHERE category_id NOT IN (
    SELECT cid FROM (
        SELECT DISTINCT category.id AS cid FROM category INNER JOIN story_category ON category_id=category.id
    ) AS c
)

3
これが機能する理由、もう1つのレベルをネストするだけで機能する理由を説明できますか?この質問は@EkoNovalの質問へのコメントとして既に尋ねられていますが、誰も応答しませんでした。多分あなたは助けることができます。
Akshay Arora 2016年

@ AkshayArora、@ Cheekysoftの回答の「たぶんテーブルにそれ自体を結合することができる」という見出しの下の部分を確認してください。彼は言ったUPDATE tbl AS a INNER JOIN tbl AS b ON .... SET a.col = b.col-同じテーブルの別のエイリアスがここで使用されているのでこれは機能するでしょう。同様に、@ NexusRexの回答では、最初のSELECTクエリstory_categoryは、2回目に使用される派生テーブルとして機能します。したがって、OPで言及されているエラーはここでは発生しません。
Istiaque Ahmed 2017

31

できない場合

UPDATE table SET a=value WHERE x IN
    (SELECT x FROM table WHERE condition);

同じテーブルなので、だまして実行できます。

UPDATE table SET a=value WHERE x IN
    (SELECT * FROM (SELECT x FROM table WHERE condition) as t)

[更新または削除など]


上記のすべての答えの最も理解しやすく論理的な実装。シンプルで要領を得ています。
Clain Dsilva

これは私のワークフローの一部になりました。このエラーに行き詰まり、この回答に進んでクエリを修正してください。ありがとう。
Vaibhav

13

これは、テーブル内のPriorityカラムの値が1以上の場合、同じテーブルのサブクエリを使用してWHERE句が1である場合、少なくとも1つの行にPriority = 1が含まれていることを確認するために私が行ったものです(これは更新中に確認する条件):


UPDATE My_Table
SET Priority=Priority + 1
WHERE Priority >= 1
AND (SELECT TRUE FROM (SELECT * FROM My_Table WHERE Priority=1 LIMIT 1) as t);

私はそれが少し醜いことを知っていますが、それはうまくいきます。


1
@anonymous_reviewer:誰かのコメントに[-1]または[+1]を与える場合は、なぜそれを与えたのかについても述べてください。ありがとう!!!
sactiw 2010

1
これは正しくないため、-1。SELECTステートメントで使用するのと同じテーブルを変更することはできません。
Chris

1
@クリス私はMySQLでそれを検証しましたが、それは私にとっては問題なく動作するので、最後に検証してから、それが正しいか正しくないと主張するようにお願いします。ありがとう!!!
sactiw

1
このページの下部には、「現在、テーブルを更新してサブクエリの同じテーブルから選択することはできません」と表示されます。-そして、私はこれを多くの場合において真実であると経験しました。 dev.mysql.com/doc/refman/5.0/en/update.html
Chris

19
@Chris私はそれを知っていますが、その回避策があります。これはまさに「UPDATE」クエリで表示しようとしたものであり、うまく機能すると私は信じています。私のクエリを実際に検証しようとしたことはないと思います。
sactiw

6

これを行う最も簡単な方法は、サブクエリ内で親クエリテーブルを参照するときにテーブルエイリアスを使用することです。

例:

insert into xxx_tab (trans_id) values ((select max(trans_id)+1 from xxx_tab));

それを次のように変更します。

insert into xxx_tab (trans_id) values ((select max(P.trans_id)+1 from xxx_tab P));

5

必要な行のIDを一時テーブルに挿入し、そのテーブルで見つかったすべての行を削除できます。

これは、@ Cheekysoftが2つのステップでそれを行うことによって意味したものである可能性があります。


3

@CheekySoftによってリンクされたMysql UPDATE構文によると、それは右下に表示されています。

現在、テーブルを更新してサブクエリの同じテーブルから選択することはできません。

ユニオンでまだ選択しているときに、store_categoryから削除していると思います。


3

OPが達成しようとしている特定のクエリについて、これを行うための理想的で最も効率的な方法は、サブクエリをまったく使用しないことです。

ここにあるLEFT JOINOPの2つのクエリのバージョンは:

SELECT s.* 
FROM story_category s 
LEFT JOIN category c 
ON c.id=s.category_id 
WHERE c.id IS NULL;

注:DELETE s削除操作をstory_categoryテーブルに制限します。
ドキュメンテーション

DELETE s 
FROM story_category s 
LEFT JOIN category c 
ON c.id=s.category_id 
WHERE c.id IS NULL;

1
驚いたことに、これ以上の投票はありません。また、マルチテーブル構文はUPDATEステートメントと結合サブクエリでも機能することに注意してください。LEFT JOIN ( SELECT ... )とは対照的に実行できるためWHERE IN( SELECT ... )、多くのユースケースで実装が役立ちます。
fyrye

2

何かがうまくいかない場合は、正面玄関を通り抜けるときに裏口を取ります。

drop table if exists apples;
create table if not exists apples(variety char(10) primary key, price int);

insert into apples values('fuji', 5), ('gala', 6);

drop table if exists apples_new;
create table if not exists apples_new like apples;
insert into apples_new select * from apples;

update apples_new
    set price = (select price from apples where variety = 'gala')
    where variety = 'fuji';
rename table apples to apples_orig;
rename table apples_new to apples;
drop table apples_orig;

これは速い。データが大きいほど良いです。


11
そして、あなたはあなたのすべての外部キーを失っただけで、おそらくあなたはいくつかのカスケード削除も持っています。
Walf


2

これを試して

DELETE FROM story_category 
WHERE category_id NOT IN (
SELECT DISTINCT category.id 
FROM (SELECT * FROM STORY_CATEGORY) sc;

1

このクエリが役に立てば幸い

DELETE FROM story_category LEFT JOIN (SELECT category.id FROM category) cat ON story_category.id = cat.id WHERE cat.id IS NULL

結果は次のとおりです。 '#1064-SQL構文にエラーがあります。MariaDBサーバーのバージョンに対応するマニュアルで、「LEFT JOIN(SELECT Categories.id FROM Categories)cat ON story_category.id = cat」の近くで使用する正しい構文を確認してください。1行目
イスティアケアーメド

複数テーブルの削除を使用する場合は、影響を受けるテーブルを指定する必要があります。DELETE story_category FROM ...ただし、LEFT JOIN category AS cat ON cat.id = story_category.category_id WHERE cat.id IS NULL結合されたstory_category.id = cat.id
サブクエリ

0

限りでは、にstory_category存在しない行を削除する必要がありますcategory

削除する行を特定するための元のクエリは次のとおりです。

SELECT * 
FROM  story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN 
       story_category ON category_id=category.id
);

元のテーブルNOT INであるサブクエリと組み合わせると、JOIN不必要に複雑になります。これはnot exists、相関サブクエリを使用して、より簡単な方法で表現できます。

select sc.*
from story_category sc
where not exists (select 1 from category c where c.id = sc.category_id);

これをdeleteステートメントに変えるのは簡単です:

delete from story_category
where not exists (select 1 from category c where c.id = story_category.category_id);    

このクエリは、MySQLのどのバージョンでも、私が知っている他のほとんどのデータベースでも実行できます。

DB Fiddleのデモ

-- set-up
create table story_category(category_id int);
create table category (id int);
insert into story_category values (1), (2), (3), (4), (5);
insert into category values (4), (5), (6), (7);

-- your original query to identify offending rows
SELECT * 
FROM  story_category 
WHERE category_id NOT IN (
    SELECT DISTINCT category.id 
    FROM category INNER JOIN 
       story_category ON category_id=category.id);
| category_id |
| ----------:|
| 1 |
| 2 |
| 3 |
-- a functionally-equivalent, simpler query for this
select sc.*
from story_category sc
where not exists (select 1 from category c where c.id = sc.category_id)
| category_id |
| ----------:|
| 1 |
| 2 |
| 3 |
-- the delete query
delete from story_category
where not exists (select 1 from category c where c.id = story_category.category_id);

-- outcome
select * from story_category;
| category_id |
| ----------:|
| 4 |
| 5 |
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.