Oracle
 sql >> Base de données >  >> RDS >> Oracle

La mise à jour SQL affectera-t-elle sa sous-requête lors de l'exécution de la mise à jour ?

** Modifié **

Sélectionner dans le tableau cible

À partir du 13.2.9.8. Sous-requêtes dans la clause FROM :

Les sous-requêtes de la clause FROM peuvent renvoyer un scalaire, une colonne, une ligne ou une table. Les sous-requêtes de la clause FROM ne peuvent pas être des sous-requêtes corrélées, sauf si elles sont utilisées dans la clause ON d'une opération JOIN.

Donc, oui, vous pouvez effectuer la requête ci-dessus.

Le problème

Il y a vraiment deux problèmes ici. Il y a simultanéité, ou s'assurer que personne d'autre ne modifie les données sous nos pieds. Ceci est géré par verrouillage. Le traitement de la modification réelle des nouvelles valeurs par rapport aux anciennes est géré avec des tables dérivées.

Verrouillage

Dans le cas de votre requête ci-dessus, avec InnoDB, MySQL effectue d'abord le SELECT et acquiert un verrou de lecture (partagé) sur chaque ligne de la table individuellement. Si vous aviez une clause WHERE dans l'instruction SELECT, seuls les enregistrements que vous sélectionnez seraient verrouillés, où les plages entraîneraient également le verrouillage de tout espace.

Un verrou en lecture empêche toute autre requête d'acquérir des verrous en écriture, de sorte que les enregistrements ne peuvent pas être mis à jour depuis un autre emplacement tant qu'ils sont verrouillés en lecture.

Ensuite, MySQL acquiert un verrou en écriture (exclusif) sur chacun des enregistrements de la table individuellement. Si vous aviez une clause WHERE dans votre instruction UPDATE, seuls les enregistrements spécifiques seraient verrouillés en écriture, et encore une fois, si la clause WHERE sélectionnait une plage, alors vous auriez une plage verrouillée.

Tout enregistrement qui avait un verrou en lecture du précédent SELECT serait automatiquement transformé en verrou en écriture.

Un verrou en écriture empêche les autres requêtes d'obtenir un verrou en lecture ou en écriture.

Vous pouvez utiliser Innotop pour voir cela en l'exécutant en mode Verrouillage, démarrer une transaction, exécuter la requête (mais ne la validez pas), et vous verrez les verrous dans Innotop. De plus, vous pouvez afficher les détails sans Innotop avec SHOW ENGINE INNODB STATUS .

Interblocages

Votre requête est vulnérable à un interblocage si deux instances ont été exécutées en même temps. Si la requête A a obtenu des verrous en lecture, puis la requête B a obtenu des verrous en lecture, la requête A devrait attendre que les verrous en lecture de la requête B soient libérés avant de pouvoir acquérir les verrous en écriture. Cependant, la requête B ne libérera pas les verrous en lecture tant qu'elle ne sera pas terminée, et elle ne se terminera que si elle peut acquérir des verrous en écriture. La requête A et la requête B sont dans une impasse, et donc dans une impasse.

Par conséquent, vous souhaiterez peut-être effectuer un verrouillage de table explicite, à la fois pour éviter la quantité massive de verrous d'enregistrement (qui utilise de la mémoire et affecte les performances) et pour éviter un blocage.

Une autre approche consiste à utiliser SELECT ... FOR UPDATE sur votre SELECT interne. Cela commence par des verrous en écriture sur toutes les lignes au lieu de commencer par lire et de les escalader.

Tables dérivées

Pour le SELECT interne, MySQL crée une table temporaire dérivée. Une table dérivée est une copie réelle non indexée des données qui résident dans la table temporaire qui est automatiquement créée par MySQL (par opposition à une table temporaire que vous créez explicitement et à laquelle vous pouvez ajouter des index).

Étant donné que MySQL utilise une table dérivée, il s'agit de l'ancienne valeur temporaire à laquelle vous faites référence dans votre question. En d'autres termes, il n'y a pas de magie ici. MySQL le fait comme vous le feriez n'importe où ailleurs, avec une valeur temporaire.

Vous pouvez voir la table dérivée en faisant un EXPLAIN contre votre instruction UPDATE (prise en charge dans MySQL 5.6+).