**編集済み **
ターゲットテーブルからの選択
13.2.9.8から。 FROM句のサブクエリ:
FROM句のサブクエリは、スカラー、列、行、またはテーブルを返すことができます。 FROM句のサブクエリは、JOIN操作のON句内で使用されない限り、相関サブクエリにすることはできません。
したがって、はい、上記のクエリを実行できます。
問題
ここには本当に2つの問題があります。並行性があります。つまり、他の誰も私たちの足元からデータを変更しないようにします。これはロックで処理されます。新しい値と古い値の実際の変更の処理は、派生テーブルで処理されます。
ロック
上記のクエリの場合、InnoDBを使用すると、MySQLは最初にSELECTを実行し、テーブルの各行で個別に読み取り(共有)ロックを取得します。 SELECTステートメントにWHERE句がある場合、選択したレコードのみがロックされ、範囲によってギャップもロックされます。
読み取りロックは、他のクエリが書き込みロックを取得するのを防ぐため、読み取りロックされている間、レコードを他の場所から更新することはできません。
次に、MySQLは、テーブル内の各レコードに対する書き込み(排他的)ロックを個別に取得します。 UPDATEステートメントにWHERE句がある場合は、特定のレコードのみが書き込みロックされ、WHERE句で範囲が選択されている場合は、範囲がロックされます。
以前のSELECTからの読み取りロックがあったレコードは、自動的に書き込みロックにエスカレートされます。
書き込みロックは、他のクエリが読み取りロックまたは書き込みロックを取得するのを防ぎます。
Innotopを使用して、ロックモードで実行し、トランザクションを開始し、クエリを実行して(ただし、コミットしないでください)、これを確認できます。Innotopにロックが表示されます。また、SHOW ENGINE INNODB STATUS
を使用すると、Innotopなしで詳細を表示できます。 。
デッドロック
2つのインスタンスが同時に実行された場合、クエリはデッドロックに対して脆弱です。クエリAが読み取りロックを取得し、次にクエリBが読み取りロックを取得した場合、クエリAはクエリBの読み取りロックが解放されるのを待ってから、書き込みロックを取得する必要があります。ただし、クエリBは、終了するまで読み取りロックを解放しません。また、書き込みロックを取得できない限り、クエリBは終了しません。クエリAとクエリBは膠着状態にあるため、デッドロックになっています。
したがって、大量のレコードロック(メモリを使用してパフォーマンスに影響を与える)を回避し、デッドロックを回避するために、明示的なテーブルロックを実行することをお勧めします。
別のアプローチは、内部SELECTでSELECT ...FORUPDATEを使用することです。これは、読み取りから始めてエスカレーションするのではなく、すべての行の書き込みロックから始まります。
派生テーブル
内部SELECTの場合、MySQLは派生一時テーブルを作成します。派生テーブルは、MySQLによって自動的に作成される一時テーブルに存在するデータの実際の非インデックスコピーです(明示的に作成してインデックスを追加できる一時テーブルとは対照的です)。
MySQLは派生テーブルを使用するため、これは質問で参照する一時的な古い値です。言い換えれば、ここには魔法はありません。 MySQLは、他の場所で行うのと同じように、一時的な値を使用してそれを行います。
UPDATEステートメント(MySQL 5.6以降でサポートされています)に対してEXPLAINを実行すると、派生テーブルを確認できます。