** Bewerkt **
Selecteren uit de doeltabel
Vanaf 13.2.9.8. Subquery's in de FROM-clausule:
Subquery's in de FROM-component kunnen een scalaire waarde, kolom, rij of tabel retourneren. Subquery's in de FROM-clausule kunnen geen gecorreleerde subquery's zijn, tenzij ze worden gebruikt binnen de ON-clausule van een JOIN-bewerking.
Dus ja, u kunt de bovenstaande zoekopdracht uitvoeren.
Het probleem
Er zijn hier eigenlijk twee problemen. Er is gelijktijdigheid, of ervoor zorgen dat niemand anders de gegevens van onder onze voeten verandert. Dit wordt afgehandeld met vergrendeling. Omgaan met de daadwerkelijke wijziging van nieuwe versus oude waarden wordt afgehandeld met afgeleide tabellen.
Vergrendelen
In het geval van uw bovenstaande vraag, met InnoDB, voert MySQL eerst de SELECT uit en verkrijgt het een lees (gedeelde) vergrendeling op elke rij in de tabel afzonderlijk. Als u een WHERE-component in de SELECT-instructie had, dan zouden alleen de records die u selecteert worden vergrendeld, terwijl bereiken er ook voor zouden zorgen dat eventuele hiaten worden vergrendeld.
Een leesvergrendeling voorkomt dat andere zoekopdrachten schrijfvergrendelingen verkrijgen, dus records kunnen niet van elders worden bijgewerkt terwijl ze zijn vergrendeld voor lezen.
Vervolgens verkrijgt MySQL een (exclusieve) schrijfvergrendeling op elk van de records in de tabel afzonderlijk. Als u een WHERE-component in uw UPDATE-instructie had, dan zouden alleen de specifieke records schrijven vergrendeld zijn, en nogmaals, als de WHERE-component een bereik selecteerde, dan zou u een bereik vergrendeld hebben.
Elk record met een leesvergrendeling van de vorige SELECT zou automatisch worden geëscaleerd naar een schrijfvergrendeling.
Een schrijfblokkering voorkomt dat andere zoekopdrachten een lees- of schrijfblokkering krijgen.
U kunt Innotop gebruiken om dit te zien door het in de Lock-modus uit te voeren, een transactie te starten, de query uit te voeren (maar niet vast te leggen), en u zult de vergrendelingen in Innotop zien. U kunt de details ook zonder Innotop bekijken met SHOW ENGINE INNODB STATUS
.
Deadlocks
Uw query is kwetsbaar voor een impasse als er twee instanties tegelijkertijd zijn uitgevoerd. Als query A leesvergrendelingen kreeg, en query B leesvergrendelingen kreeg, zou query A moeten wachten tot de leesvergrendelingen van query B werden vrijgegeven voordat deze de schrijfvergrendelingen kon verkrijgen. Query B zal de leesvergrendelingen echter pas vrijgeven nadat deze is voltooid, en deze zal niet worden voltooid tenzij deze schrijfvergrendelingen kan verkrijgen. Query A en query B bevinden zich in een patstelling en dus in een impasse.
Daarom wilt u misschien een expliciete tabelvergrendeling uitvoeren, zowel om de enorme hoeveelheid recordvergrendelingen te vermijden (die geheugen gebruiken en de prestaties beïnvloeden), en om een impasse te voorkomen.
Een alternatieve benadering is om SELECT ... FOR UPDATE te gebruiken op je innerlijke SELECT. Dit begint met schrijfvergrendelingen op alle rijen in plaats van te beginnen met lezen en ze te escaleren.
Afgeleide tabellen
Voor de innerlijke SELECT maakt MySQL een afgeleide tijdelijke tabel. Een afgeleide tabel is een daadwerkelijke niet-geïndexeerde kopie van de gegevens in de tijdelijke tabel die automatisch wordt gemaakt door MySQL (in tegenstelling tot een tijdelijke tabel die u expliciet maakt en waaraan u indexen kunt toevoegen).
Aangezien MySQL een afgeleide tabel gebruikt, is dat de tijdelijke oude waarde waarnaar u verwijst in uw vraag. Met andere woorden, er is hier geen magie. MySQL doet het net zoals je het ergens anders zou doen, met een tijdelijke waarde.
U kunt de afgeleide tabel zien door een EXPLAIN uit te voeren tegen uw UPDATE-instructie (ondersteund in MySQL 5.6+).