** Diedit **
Memilih dari tabel target
Dari 13.2.9.8. Subquery dalam FROM Clause:
Subquery dalam klausa FROM dapat mengembalikan skalar, kolom, baris, atau tabel. Subquery dalam klausa FROM tidak dapat dikorelasikan dengan subquery, kecuali jika digunakan dalam klausa ON dari operasi JOIN.
Jadi, ya, Anda dapat melakukan kueri di atas.
Masalahnya
Sebenarnya ada dua masalah di sini. Ada konkurensi, atau memastikan bahwa tidak ada orang lain yang mengubah data dari bawah kaki kita. Ini ditangani dengan penguncian. Menangani modifikasi aktual dari nilai baru versus nilai lama ditangani dengan tabel turunan.
Mengunci
Dalam kasus kueri Anda di atas, dengan InnoDB, MySQL melakukan SELECT terlebih dahulu, dan memperoleh kunci baca (bersama) pada setiap baris dalam tabel satu per satu. Jika Anda memiliki klausa WHERE dalam pernyataan SELECT, maka hanya record yang Anda pilih yang akan dikunci, di mana rentang akan menyebabkan celah dikunci juga.
Kunci baca mencegah kueri lain memperoleh kunci tulis, sehingga catatan tidak dapat diperbarui dari tempat lain saat sedang dibaca terkunci.
Kemudian, MySQL memperoleh kunci tulis (eksklusif) pada setiap catatan dalam tabel secara individual. Jika Anda memiliki klausa WHERE dalam pernyataan UPDATE, maka hanya catatan tertentu yang akan dikunci, dan sekali lagi, jika klausa WHERE memilih rentang, rentang akan dikunci.
Catatan apa pun yang memiliki kunci baca dari SELECT sebelumnya akan secara otomatis dieskalasi menjadi kunci tulis.
Kunci tulis mencegah kueri lain mendapatkan kunci baca atau tulis.
Anda dapat menggunakan Innotop untuk melihat ini dengan menjalankannya dalam mode Kunci, memulai transaksi, menjalankan kueri (tetapi jangan melakukan itu), dan Anda akan melihat kunci di Innotop. Anda juga dapat melihat detail tanpa Innotop dengan SHOW ENGINE INNODB STATUS
.
Kebuntuan
Kueri Anda rentan terhadap kebuntuan jika dua instans dijalankan secara bersamaan. Jika kueri A mendapat kunci baca, lalu kueri B mendapat kunci baca, kueri A harus menunggu kunci baca kueri B dirilis sebelum dapat memperoleh kunci tulis. Namun, kueri B tidak akan melepaskan kunci baca sampai setelah selesai, dan tidak akan selesai kecuali dapat memperoleh kunci tulis. Kueri A dan kueri B menemui jalan buntu, dan karenanya, menemui jalan buntu.
Oleh karena itu, Anda mungkin ingin melakukan penguncian tabel eksplisit, baik untuk menghindari penguncian data dalam jumlah besar (yang menggunakan memori dan memengaruhi kinerja), dan untuk menghindari kebuntuan.
Pendekatan alternatif adalah dengan menggunakan SELECT ... FOR UPDATE pada SELECT batin Anda. Ini dimulai dengan kunci tulis di semua baris alih-alih dimulai dengan membaca dan mengeskalasinya.
Tabel turunan
Untuk SELECT dalam, MySQL membuat tabel sementara yang diturunkan. Tabel turunan adalah salinan sebenarnya yang tidak diindeks dari data yang berada di tabel sementara yang dibuat secara otomatis oleh MySQL (sebagai lawan dari tabel sementara yang Anda buat secara eksplisit dan dapat ditambahkan indeks).
Karena MySQL menggunakan tabel turunan, itulah nilai lama sementara yang Anda rujuk dalam pertanyaan Anda. Dengan kata lain, tidak ada keajaiban di sini. MySQL melakukannya sama seperti Anda melakukannya di tempat lain, dengan nilai sementara.
Anda dapat melihat tabel turunan dengan melakukan EXPLAIN terhadap pernyataan UPDATE Anda (didukung di MySQL 5.6+).