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